「LSM303DLHを用いたRaspberry Piでのデジタルコンパス・加速度入力 プログラム」でLSM303DLHの加速度センサと地磁気センサからモーションデータを取得しましたが、このモーションデータからピッチ、ロール、ヘディングを計算します。
デジタルコンパス・加速度センサ「LSM303DLH」との接続
Raspberry PiとLSM303DLHとの接続は前回と同じですが、今回はRapberryPi Zero Wを次のように接続しました。
加速度センサと地磁気センサからピッチ、ロール、ヘディングの計算式
加速度センサと地磁気センサからピッチ、ロール、ヘディングの計算は、「アルプスIoT Smart Moduleのモーションセンサからピッチ、ロール、ヘディングの取得」で計算しましたが、計算時に例外が発生するため、計算式を少し変えます。
加速度センサからの出力 $(A_x, A_y, A_z)$ をグローバル座標系へと回転させるロール、ピッチ、ヘディングを $\gamma 、 \rho 、 \Psi$ とします。
ロール $\gamma$ は次の計算式で求めます。
\begin{eqnarray}
\gamma & = & \tan^{-1} \left( \frac{A_y}{A_z} \right)
\end{eqnarray}
ピッチ $\rho$ は次の計算式で求めます。
\begin{eqnarray}
\rho & = & \tan^{-1} \left( \frac{-A_x}{\sqrt{A_y^2 + A_z^2}} \right)
\end{eqnarray}
磁気センサからの出力を $(M_x, M_y, M_z)$ をグローバル座標系へと回転させ、加速度センサから算出したロールとピッチを $\gamma 、 \rho$ として、ヘディング $\Psi$ は次の計算式で求めます。
\begin{eqnarray}
M_{x1} & = & M_x\cos\rho+ M_z\sin\rho \\
M_{y1} & = & M_x\sin\gamma\sin\rho + M_y\cos\gamma – M_z\sin\gamma\cos\rho \\ \\
Heading \, \Psi & = & \tan^{-1}(\frac{M_{y1}}{M_{x1}}) & (M_{x1}> 0 で M_{y1}\geq 0 )\\
& = & 180° + \tan^{-1}(\frac{M_{y1}}{M_{x1}}) & (M_{x1}< 0 )\\
& = & 360° + \tan^{-1}(\frac{M_{y1}}{M_{x1}}) & (M_{x1}> 0 で M_{y1}\leq 0 )\\
& = & 90° & (M_{x1}= 0 で M_{y1}< 0 )\\
& = & 270° & (M_{x1}= 0 で M_{y1}> 0 )\\
\end{eqnarray}
前回は、ロール $\gamma$ 、ピッチを $\rho$ を $\sin^{-1}$ 関数を用いて求めましたが、関数の引数が範囲外になって例外が発生しました。このため計算式を変更しています。
ピッチ、ロール、ヘディングの計算ソフトの作成
ピッチ、ロール、ヘディングを計算するソフト「motionpad.py」を次に示します。
#!/usr/bin/env python3
# coding: UTF-8
import math
import time
import board
import busio
import adafruit_lsm303
def computeAngle(GeoMagnetic_X,GeoMagnetic_Y,GeoMagnetic_Z,Acceleration_X,Acceleration_Y,Acceleration_Z):
print ('computeAngle')
try:
Pitch = math.atan(-Acceleration_X/math.sqrt(Acceleration_Y**2+Acceleration_Z**2))
print ('Pitch:{0:.7f}'.format(Pitch ))
Roll = math.atan(Acceleration_Y/Acceleration_Z)
print ('Roll:{0:.7f}'.format(Roll ))
Mx = GeoMagnetic_X*math.cos(Pitch)+GeoMagnetic_Z*math.sin(Pitch)
#Mx = GeoMagnetic_X*math.cos(Pitch)+GeoMagnetic_Y*math.sin(Pitch)*math.sin(Roll) +GeoMagnetic_Z*math.sin(Pitch)*math.cos(Roll)
print ('cos:{0:.7f} sin:{1:.7f}'.format(math.cos(Pitch),math.sin(Pitch) ))
My=GeoMagnetic_X*math.sin(Roll)*math.sin(Pitch)+GeoMagnetic_Y*math.cos(Roll)-GeoMagnetic_Z*math.sin(Roll)*math.cos(Pitch)
#My=GeoMagnetic_Y*math.cos(Roll)-GeoMagnetic_Z*math.sin(Roll)
print ('Mx:{0:.7f} My:{1:.7f}'.format(Mx,My ))
Heading = math.atan(My/Mx)
if Mx>0 and My>=0:
pass
elif Mx<0:
Heading = math.pi + Heading
elif Mx>0 and My<=0:
Heading = 2*math.pi + Heading
elif Mx==0 and My<0:
Heading = math.pi/2
elif Mx==0 and My>0:
Heading = math.pi/2*3
except ValueError:
print ('computeAngle-except')
return False ,0,0,0
return True ,math.degrees(Roll),math.degrees(Pitch),math.degrees(Heading)
def write_report(report):
with open('/dev/hidg0', 'rb+') as fd:
fd.write(report.encode())
def main():
NULL_CHAR = chr(0)
i2c = busio.I2C(board.SCL, board.SDA)
sensor = adafruit_lsm303.LSM303(i2c)
fd = open('/dev/hidg0', 'rb+')
file=open("test.csv", "w", encoding="utf-8")
file_raw=open("test_raw.csv", "w", encoding="utf-8")
i = 0
while True:
raw_accel_x, raw_accel_y, raw_accel_z = sensor.raw_acceleration
accel_x, accel_y, accel_z = sensor.acceleration
raw_mag_x, raw_mag_y, raw_mag_z = sensor.raw_magnetic
mag_x, mag_y, mag_z = sensor.magnetic
print('Acceleration raw: ({0:6d}, {1:6d}, {2:6d}), (m/s^2): ({3:10.3f}, {4:10.3f}, {5:10.3f})'.format(raw_accel_x, raw_accel_y, raw_accel_z, accel_x, accel_y, accel_z))
print('Magnetometer raw: ({0:6d}, {1:6d}, {2:6d}), (gauss): ({3:10.3f}, {4:10.3f}, {5:10.3f})'.format(raw_mag_x, raw_mag_y, raw_mag_z, mag_x, mag_y, mag_z))
print('')
#raw_accel_x =2080
#raw_accel_y =16
#raw_accel_z =16240
#raw_mag_x =187
#raw_mag_y =-19
#raw_mag_z =-339
GeoMagnetic_X = mag_x
GeoMagnetic_Y = mag_y
GeoMagnetic_Z = mag_z
Acceleration_X = accel_x
Acceleration_Y = accel_y
Acceleration_Z = accel_z
file.write('{0:.3f} ,{1:.3f} ,{2:.3f},{3:.3f} ,{4:.3f} ,{5:.3f}\n'.format(mag_x,mag_y,mag_z,accel_x,accel_y,accel_z ))
file_raw.write('{0:.3f} ,{1:.3f} ,{2:.3f},{3:.3f} ,{4:.3f} ,{5:.3f}\n'.format(raw_mag_x,raw_mag_y,raw_mag_z,raw_accel_x,raw_accel_y,raw_accel_z ))
i = i+1
if i>1200:
file.close()
file_raw.close()
break
ok,Roll,Pitch,Heading = computeAngle(GeoMagnetic_X,GeoMagnetic_Y,GeoMagnetic_Z,Acceleration_X,Acceleration_Y,Acceleration_Z)
if ok :
print ('Roll:{0:.3f} Pitch:{1:.3f} Heading:{2:.3f}'.format(Roll,Pitch,Heading ))
write_report(chr(1)+chr(0x02)+chr(0)+chr(0x03)+chr(0)+chr(0x04)+chr(0))
time.sleep(1)
if __name__ == "__main__":
main()
ピッチ、ロール、ヘディングの計算ソフトの実行
次のコマンドで作成した計算ソフト「」motionpad.pyを実行します。次のように、計算したピッチ、ロール、ヘディングが表示されます。
$ python3 motionpad.py Acceleration raw: ( -6304, -1488, -640), (m/s^2): ( -3.701, -0.874, -0.376) Magnetometer raw: ( 55, 2, 8), (gauss): ( 5.000, 0.182, 0.816) computeAngle Pitch:1.3192895 Roll:1.1646075 cos:0.2488636 sin:0.9685385 Mx:2.0349618 My:4.3338733 Roll:66.727 Pitch:75.590 Heading:64.848 Acceleration raw: ( 320, -16, 16272), (m/s^2): ( 0.188, -0.009, 9.553) Magnetometer raw: ( 49, -1, 10), (gauss): ( 4.455, -0.091, 1.020) computeAngle Pitch:-0.0196631 Roll:-0.0009833 cos:0.9998067 sin:-0.0196619 Mx:4.4336212 My:-0.0898198 Roll:-0.056 Pitch:-1.127 Heading:358.839 Acceleration raw: ( 304, 16, 16112), (m/s^2): ( 0.178, 0.009, 9.459) Magnetometer raw: ( 57, 2, 11), (gauss): ( 5.182, 0.182, 1.122) computeAngle Pitch:-0.0188657 Roll:0.0009930 cos:0.9998220 sin:-0.0188646 Mx:5.1597216 My:0.1806066 Roll:0.057 Pitch:-1.081 Heading:2.005 Acceleration raw: ( 320, -80, 16288), (m/s^2): ( 0.188, -0.047, 9.562) Magnetometer raw: ( 55, -2, 6), (gauss): ( 5.000, -0.182, 0.612) computeAngle Pitch:-0.0196436 Roll:-0.0049116 cos:0.9998071 sin:-0.0196423 Mx:4.9870094 My:-0.1783271 Roll:-0.281 Pitch:-1.125 Heading:357.952 Acceleration raw: ( 304, 32, 16304), (m/s^2): ( 0.178, 0.019, 9.572) Magnetometer raw: ( 50, -6, 6), (gauss): ( 4.545, -0.545, 0.612) computeAngle Pitch:-0.0186435 Roll:0.0019627 cos:0.9998262 sin:-0.0186425 Mx:4.5332509 My:-0.5468213 Roll:0.112 Pitch:-1.068 Heading:353.122
