|
楼主 |
发表于 2011-2-13 22:40:35
|
显示全部楼层
回复【楼主位】TADLAW
-----------------------------------------------------------------------
多旋翼直升机(四轴飞行器)之开源整合平台
六、电路模组应用单元
主题二:飞行姿态分析仪
程式测试三: (AHRS 飞行姿态方位参考系统)
1.主旨:取MAG 9DOF 模组中的3轴陀螺仪+3轴加速计+3轴磁力计的取样值, 做出AHRS 姿态方位参考系统。
2.目的:
a. 使用PYTHON 的高阶程式语言, 处理MAG 9DOF 模组中的3轴陀螺仪+3轴加速计+3轴磁力计的取样值, 经由程式计算, 透过VPYTHON 3D绘图模组, 将AHRS画在电脑萤幕上, 以利于直觉观察。
b. AHRS 程式模组是四元数+DCM(Direction Cosine Matrix)滤波实作, 可平缓手动摇晃以及振动干扰的影响。
3.模块对应的程式清单:
a. 飞控模块 测试程式 (同电子罗盘,在14楼)
b. USB TO TWI(I2C) 模组程式 (同电子罗盘)
c. 上位机安装程式(PYTHON+VPYTHON 3D绘图+PYUSB) (同电子罗盘)
d. AHRS 上位机程式 点击此处下载 ourdev_615876AOB5E8.zip(文件大小:3K) (原文件名:AHRS_01.zip)
4.心得:
a. 磁力计在使用前要做校正处理。
b. 会自动修正陀罗仪累积误差。
c. pitch 锁在 磁北上, 不知道对不对?
d. 与电子罗盘比较, 反应比较慢。
e. 四轴飞行姿态分析的重点在陀罗仪, 当机体转动时的动能,会由陀罗仪的角速值表现出来。此时透过马达转速改变将机体推回, 若是推回力道小于机体转动时的动能, 则此一控制是阻尼器的观念。若是等于则是平衡器的观念。由此可知, 机体要前后左右重量平衡, 马达推力要线性一致, 四轴的调校才会比较容易。
f. 当陀罗仪以平衡器的观念来做即时控制时, 加速计的角色则转化为绝对参考值,用比较长时间累积的平均值就可以获得, 过滤干扰就有可能不用数学概念的浮点计算了。
5. 备注:接序的测试是将无刷马达接上, 观察实际的振动及磁场影响。
AHRS萤幕截图
(原文件名:APPSUB_02_MAG9DOF_09.PNG)
====================================================================================
AHRS_01.PY
====================================================================================
#====[AHRS ]==================================================================
#=====================================================================================================
# AHRS
# S.O.H. Madgwick
# 25th August 2010
#=====================================================================================================
# Description:
#
# Quaternion implementation of the 'DCM filter' [Mayhony et al]. Incorporates the magnetic distortion
# compensation algorithms from my filter [Madgwick] which eliminates the need for a reference
# direction of flux (bx bz) to be predefined and limits the effect of magnetic distortions to yaw
# axis only.
#
# User must define 'halfT' as the (sample period / 2), and the filter gains 'Kp' and 'Ki'.
#
# Global variables 'q0', 'q1', 'q2', 'q3' are the quaternion elements representing the estimated
# orientation. See my report for an overview of the use of quaternions in this application.
#
# User must call 'AHRSupdate()' every sample period and parse calibrated gyroscope ('gx', 'gy', 'gz'),
# accelerometer ('ax', 'ay', 'ay') and magnetometer ('mx', 'my', 'mz') data. Gyroscope units are
# radians/second, accelerometer and magnetometer units are irrelevant as the vector is normalised.
#
#=====================================================================================================
import math
import usb.core
import usb.util
from visual import *
import string
from time import time
# Main scene
scene=display(title="MAG9DOF AHRS test")
scene.range=(1.2,1.2,1.2)
#scene.forward = (0,-1,-0.25)
scene.forward = (1,0,-0.25)
scene.up=(0,0,1)
# Second scene (Roll, Pitch, Yaw)
scene2 = display(title='MAG9DOF AHRS test',x=0, y=0, width=500, height=200,center=(0,0,0), background=(0,0,0))
scene2.range=(1,1,1)
scene.width=500
scene.y=200
scene2.select()
#Roll, Pitch, Yaw
cil_roll = cylinder(pos=(-0.4,0,0),axis=(0.2,0,0),radius=0.01,color=color.red)
cil_roll2 = cylinder(pos=(-0.4,0,0),axis=(-0.2,0,0),radius=0.01,color=color.red)
cil_pitch = cylinder(pos=(0.1,0,0),axis=(0.2,0,0),radius=0.01,color=color.green)
cil_pitch2 = cylinder(pos=(0.1,0,0),axis=(-0.2,0,0),radius=0.01,color=color.green)
#cil_course = cylinder(pos=(0.6,0,0),axis=(0.2,0,0),radius=0.01,color=color.blue)
#cil_course2 = cylinder(pos=(0.6,0,0),axis=(-0.2,0,0),radius=0.01,color=color.blue)
arrow_course = arrow(pos=(0.6,0,0),color=color.cyan,axis=(-0.2,0,0), shaftwidth=0.02, fixedwidth=1)
#Roll,Pitch,Yaw labels
label(pos=(-0.4,0.3,0),text="Roll",box=0,opacity=0)
label(pos=(0.1,0.3,0),text="Pitch",box=0,opacity=0)
label(pos=(0.55,0.3,0),text="Yaw",box=0,opacity=0)
label(pos=(0.6,0.22,0),text="N",box=0,opacity=0,color=color.yellow)
label(pos=(0.6,-0.22,0),text="S",box=0,opacity=0,color=color.yellow)
label(pos=(0.38,0,0),text="W",box=0,opacity=0,color=color.yellow)
label(pos=(0.82,0,0),text="E",box=0,opacity=0,color=color.yellow)
label(pos=(0.75,0.15,0),height=7,text="NE",box=0,color=color.yellow)
label(pos=(0.45,0.15,0),height=7,text="NW",box=0,color=color.yellow)
label(pos=(0.75,-0.15,0),height=7,text="SE",box=0,color=color.yellow)
label(pos=(0.45,-0.15,0),height=7,text="SW",box=0,color=color.yellow)
L1 = label(pos=(-0.4,0.22,0),text="-",box=0,opacity=0)
L2 = label(pos=(0.1,0.22,0),text="-",box=0,opacity=0)
L3 = label(pos=(0.7,0.3,0),text="-",box=0,opacity=0)
# Main scene objects
scene.select()
# Reference axis (x,y,z)
arrow(color=color.green,axis=(1,0,0), shaftwidth=0.02, fixedwidth=1)
arrow(color=color.green,axis=(0,-1,0), shaftwidth=0.02 , fixedwidth=1)
arrow(color=color.green,axis=(0,0,-1), shaftwidth=0.02, fixedwidth=1)
# labels
label(pos=(0,0,0.8),text="9DOF Razor IMU test",box=0,opacity=0)
label(pos=(1,0,0),text="X",box=0,opacity=0)
label(pos=(0,-1,0),text="Y",box=0,opacity=0)
label(pos=(0,0,-1),text="Z",box=0,opacity=0)
# IMU object
platform = box(length=1, height=0.05, width=1, color=color.red)
p_line = box(length=1,height=0.08,width=0.1,color=color.yellow)
plat_arrow = arrow(color=color.green,axis=(1,0,0), shaftwidth=0.06, fixedwidth=1)
# find our device
dev = usb.core.find(idVendor=0x16C0, idProduct=0x05DC)
#dev.set_configuration()
AX_MAX = 0.0
AY_MAX = 0.0
AZ_MAX = 0.0
AX_MIN = 0.0
AY_MIN = 0.0
AZ_MIN = 0.0
AX_MAX = 17296.0
AY_MAX = 16224.0
AZ_MAX = 17392.0
AX_MIN = -16688.0
AY_MIN = -16592.0
AZ_MIN = -16672.0
#MX_MAX = 0.0
#MY_MAX = 0.0
#MZ_MAX = 0.0
#MX_MIN = 0.0
#MY_MIN = 0.0
#MZ_MIN = 0.0
MX_MAX = 127.0
MY_MAX = 234.0
MZ_MAX = 174.0
MX_MIN = -367.0
MY_MIN = -255.0
MZ_MIN = -271.0
CT_MX = 5
#====[AHRS]===========================================================================================
Kp = 2.0 # 2.0 proportional gain governs rate of convergence to accelerometer/magnetometer
Ki = 0.005 # 0.005 integral gain governs rate of convergence of gyroscope biases
halfT = 0.02 # 0.02 half the sample period
# quaternion elements representing the estimated orientation
q0 = 1.0
q1 = 0.0
q2 = 0.0
q3 = 0.0
# scaled integral error
exInt = 0.0
eyInt = 0.0
ezInt = 0.0
while(1):
ret = dev.ctrl_transfer(0xC0, 0x05, 0x00, 0x0, 18)
if len(ret) == 18:
# auxiliary variables to reduce number of repeated operations
q0q0 = q0*q0
q0q1 = q0*q1
q0q2 = q0*q2
q0q3 = q0*q3
q1q1 = q1*q1
q1q2 = q1*q2
q1q3 = q1*q3
q2q2 = q2*q2
q2q3 = q2*q3
q3q3 = q3*q3
# print ret
A_XI = ret[0] + ret[1]*256
A_YI = ret[2] + ret[3]*256
A_ZI = ret[4] + ret[5]*256
if A_XI > 32768: A_XI = A_XI - 65536
if A_YI > 32768: A_YI = A_YI - 65536
if A_ZI > 32768: A_ZI = A_ZI - 65536
A_X = A_XI / 32768.0
A_Y = A_YI / 32768.0
A_Z = A_ZI / 32768.0
G_X = ret[6] + ret[7]*256
G_Y = ret[8] + ret[9]*256
G_Z = ret[10] + ret[11]*256
if G_X > 32768: G_X = G_X - 65536
if G_Y > 32768: G_Y = G_Y - 65536
if G_Z > 32768: G_Z = G_Z - 65536
M_XI = ret[12] + ret[13]*256
M_YI = ret[14] + ret[15]*256
M_ZI = ret[16] + ret[17]*256
if M_XI > 32768: M_XI = (M_XI - 65536)
if M_YI > 32768: M_YI = (M_YI - 65536)
if M_ZI > 32768: M_ZI = (M_ZI - 65536)
# if M_X > MX_MAX: MX_MAX = M_X
# if M_Y > MY_MAX: MY_MAX = M_Y
# if M_Z > MZ_MAX: MZ_MAX = M_Z
# if M_X < MX_MIN: MX_MIN = M_X
# if M_Y < MY_MIN: MY_MIN = M_Y
# if M_Z < MZ_MIN: MZ_MIN = M_Z
M_X = (M_XI - MX_MIN) / (MX_MAX - MX_MIN) * 2.0 - 1.0
M_Y = (M_YI - MY_MIN) / (MY_MAX - MY_MIN) * 2.0 - 1.0
M_Z = (M_ZI - MZ_MIN) / (MZ_MAX - MZ_MIN) * 2.0 - 1.0
# print 'M_X:%f, M_Y:%f, M_Z:%f' % (M_X,M_Y,M_Z)
# print 'MX_MAX:%6d, MY_MAX:%6d, MZ_MAX:%6d' % (MX_MAX,MY_MAX,MZ_MAX)
# print 'MX_MIN:%6d, MY_MIN:%6d, MZ_MIN:%6d' % (MX_MIN,MY_MIN,MZ_MIN)
gx = (G_X / 32768.0) * 250.0 * (pi / 180.0)
gy = (G_Y / 32768.0) * 250.0 * (pi / 180.0)
gz = (G_Z / 32768.0) * 250.0 * (pi / 180.0)
#====[normalise the measurements]====================
norm = math.sqrt(A_X*A_X+A_Y*A_Y+A_Z*A_Z)
ax = A_X / norm
ay = A_Y / norm
az = A_Z / norm
norm = math.sqrt(M_X*M_X+M_Y*M_Y+M_Z*M_Z)
mx = M_X / norm
my = M_Y / norm
mz = M_Z / norm
#====[compute reference direction of flux]==========================================
hx = 2*mx*(0.5 - q2q2 - q3q3) + 2*my*(q1q2 - q0q3) + 2*mz*(q1q3 + q0q2)
hy = 2*mx*(q1q2 + q0q3) + 2*my*(0.5 - q1q1 - q3q3) + 2*mz*(q2q3 - q0q1)
hz = 2*mx*(q1q3 - q0q2) + 2*my*(q2q3 + q0q1) + 2*mz*(0.5 - q1q1 - q2q2)
bx = sqrt((hx*hx) + (hy*hy))
bz = hz
#====[estimated direction of gravity and flux (v and w)]================
vx = 2*(q1q3 - q0q2)
vy = 2*(q0q1 + q2q3)
vz = q0q0 - q1q1 - q2q2 + q3q3
wx = 2*bx*(0.5 - q2q2 - q3q3) + 2*bz*(q1q3 - q0q2)
wy = 2*bx*(q1q2 - q0q3) + 2*bz*(q0q1 + q2q3)
wz = 2*bx*(q0q2 + q1q3) + 2*bz*(0.5 - q1q1 - q2q2)
#====[error is sum of cross product between reference direction of fields and direction measured by sensors
ex = (ay*vz - az*vy) + (my*wz - mz*wy)
ey = (az*vx - ax*vz) + (mz*wx - mx*wz)
ez = (ax*vy - ay*vx) + (mx*wy - my*wx)
#====[integral error scaled integral gain
exInt = exInt + ex*Ki
eyInt = eyInt + ey*Ki
ezInt = ezInt + ez*Ki
#====[adjusted gyroscope measurements
gx = gx + Kp*ex + exInt
gy = gy + Kp*ey + eyInt
gz = gz + Kp*ez + ezInt
#====[integrate quaternion rate and normalise
q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT
q1 = q1 + ( q0*gx + q2*gz - q3*gy)*halfT
q2 = q2 + ( q0*gy - q1*gz + q3*gx)*halfT
q3 = q3 + ( q0*gz + q1*gy - q2*gx)*halfT
#====[normalise quaternion]==========================
norm = math.sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3)
q0 = q0 / norm
q1 = q1 / norm
q2 = q2 / norm
q3 = q3 / norm
#====[quaternionToEuler]===========================================================
euler0 = math.atan2(2*q1*q2 - 2*q0*q3, 2*q0*q0 + 2*q1*q1 - 1) #====[psi]====
euler1 = -math.asin(2*q1*q3 + 2*q0*q2) #====[theta]==
euler2 = math.atan2(2*q2*q3 - 2*q0*q1, 2*q0*q0 + 2*q3*q3 - 1) #====[phi]====
print 'euler0=%9f, euler1=%9f, euler2=%9f' %(math.degrees(euler0),math.degrees(euler1),math.degrees(euler2))
pitch = euler1
roll = euler2
yaw = euler0
#====[Tilt compensated Magnetic filed X:]=============================
# MH_X = M_X*cos_pitch - M_Z*sin_pitch
#====[Tilt compensated Magnetic filed Y:]=============================
# MH_Y = M_X*sin_roll*sin_pitch + M_Y*cos_roll - M_Z*sin_roll*cos_pitch
#====[Magnetic Heading]=============================
# yaw = math.atan2(MH_Y,MH_X)
axis=(math.cos(pitch)*math.cos(yaw),-math.cos(pitch)*math.sin(yaw),math.sin(pitch))
up=(math.sin(roll)*math.sin(yaw)+math.cos(roll)*math.sin(pitch)*math.cos(yaw),math.sin(roll)*math.cos(yaw)-math.cos(roll)*math.sin(pitch)*math.sin(yaw),-math.cos(roll)*math.cos(pitch))
# print axis,up
platform.axis=axis
platform.up=up
platform.length=1.0
platform.width=0.65
plat_arrow.axis=axis
plat_arrow.up=up
plat_arrow.length=0.8
p_line.axis=axis
p_line.up=up
cil_roll.axis=(0.2*cos(roll),0.2*sin(roll),0)
cil_roll2.axis=(-0.2*cos(roll),-0.2*sin(roll),0)
cil_pitch.axis=(0.2*cos(pitch),0.2*sin(pitch),0)
cil_pitch2.axis=(-0.2*cos(pitch),-0.2*sin(pitch),0)
arrow_course.axis=(0.2*sin(yaw),0.2*cos(yaw),0)
L1.text = '%5.1f' % (math.degrees(roll))
L2.text = '%5.1f' % (math.degrees(pitch))
L3.text = '%5.1f' % (math.degrees(yaw)) |
|