Skip to content

Commit fe37e0b

Browse files
committed
V0.5 plus readme changes.
1 parent c0b415a commit fe37e0b

File tree

4 files changed

+65
-35
lines changed

4 files changed

+65
-35
lines changed

README.md

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
# micropython-fusion
2+
23
Sensor fusion calculating yaw, pitch and roll from the outputs of motion tracking devices. This
34
uses the Madgwick algorithm, widely used in multicopter designs for its speed and quality. An
4-
update takes about 1.6mS on the Pyboard
5+
update takes about 1.6mS on the Pyboard. The code is intended to be independent of the sensor
6+
device: testing was done with the InvenSense MPU-9150.
7+
8+
## Disclaimer
9+
10+
I should point out that I'm unfamiliar with aircraft conventions and would appreciate feedback
11+
if my observations about coordinate conventions are incorrect.
512

613
# fusion module
714

@@ -15,12 +22,12 @@ if you use a 6 degrees of freedom (DOF) sensor, yaw will be invalid.
1522

1623
```update(accel, gyro, mag)```
1724

18-
For 9DOF sensors. Accepts 3-tuples (x, y, z) of accelerometer, gyro and magnetometer data and
19-
updates the filters. This should be called periodically, depending on the required response
20-
speed. Units:
25+
For 9DOF sensors. Accepts three 3-tuples (x, y, z) of accelerometer, gyro and magnetometer data
26+
and updates the filters. This should be called periodically with a frequency depending on the
27+
required response speed. Units:
2128
accel: Typically G. Values are normalised in the algorithm so units are irrelevant.
2229
gyro: Degrees per second.
23-
magnetometer: Microteslas.
30+
magnetometer: Microtesla.
2431

2532
```update_nomag(accel, gyro)```
2633

@@ -33,8 +40,8 @@ gyro: Degrees per second.
3340
```calibrate(getxyz, stopfunc)```
3441

3542
The first argument is a function returning a tuple of magnetic x,y,z values from the sensor.
36-
The second is a function returning ```True``` when calibration is deemed complete: a timer or an
37-
input from the user. It updates the ```magbias``` property.
43+
The second is a function returning ```True``` when calibration is deemed complete: this could
44+
be a timer or an input from the user. The method updates the ```magbias``` property.
3845

3946
Calibration is performed by rotating the unit around each orthogonal axis while the routine
4047
runs, the aim being to compensate for offsets caused by static local magnetic fields.
@@ -45,17 +52,19 @@ Three read-only properties provide access to the angles. These are in degrees.
4552

4653
```yaw```
4754

48-
Angle relative to North (anticlockwise)
55+
Angle relative to North. Better terminology is "heading" since it is ground referenced: yaw
56+
is also used to mean the angle of an aircraft's fuselage relative to its direction of motion.
4957

5058
```pitch```
5159

52-
Angle of aircraft nose relative to ground (+ve is towards ground)
60+
Angle of aircraft nose relative to ground (conventionally +ve is towards ground). Also known
61+
as "elevation".
5362

5463
```roll```
5564

56-
Angle of aircraft wings to ground anticlockwise (left wing down)
65+
Angle of aircraft wings to ground, also known as "bank".
5766

58-
### Notes for beginners
67+
### Notes for constructors
5968

6069
If you're developing a machine using a motion sensing device consider the effects of vibration.
6170
This can be surprisingly high (use the sensor to measure it). No amount of digital filtering
@@ -64,27 +73,31 @@ these will be aliased down into the filter passband and affect the results. It's
6473
neccessary to isolate the sensor with a mechanical filter, typically a mass supported on very
6574
soft rubber mounts.
6675

67-
You may want to take control of garbage collection (GC). In systems with continuously running
68-
control loops there is a case for doing an explicit GC on each iteration: this tends to make the
69-
GC time shorter and ensures it occurs at a predictable time. See the MicroPython ```gc``` module.
70-
7176
If using a magnetoemeter consider the fact that the Earth's magnetic field is small: the field
7277
detected may be influenced by ferrous metals in the machine being controlled or by currents in
7378
nearby wires. If the latter are variable there is little chance of compensating for them, but
7479
constant magnetic offsets may be addressed by calibration. This involves rotating the machine
7580
around each of three orthogonal axes while running the fusion object's ```calibrate``` method.
7681

77-
Update rate - TODO
82+
The local coordinate system for the sensor is usually defined as follows, assuming the vehicle
83+
is on the ground:
84+
z Vertical axis, vector points towards ground
85+
x Axis towards the front of the vehicle, vector points in normal direction of travel
86+
y Vector points left from pilot's point of view (I think)
87+
88+
You may want to take control of garbage collection (GC). In systems with continuously running
89+
control loops there is a case for doing an explicit GC on each iteration: this tends to make the
90+
GC time shorter and ensures it occurs at a predictable time. See the MicroPython ```gc``` module.
7891

7992
# Background notes
8093

81-
These are blatantly plagiarised as this isn't my field, but in my defence I have quoted sources.
94+
These are blatantly plagiarised as this isn't my field. I have quoted sources.
8295

8396
### Yaw Pitch and Roll
8497

8598
Perhaps better titled heading, elevation and bank: there seems to be ambiguity about the concept
8699
of yaw, whether this is measured relative to the aircraft's local coordinate system or that of
87-
the Earth. I gather Tait-Bryan angles are earth-relative.
100+
the Earth. The angles emitted by the Madgwick algorithm (Tait-Bryan angles) are earth-relative.
88101
See http://en.wikipedia.org/wiki/Euler_angles#Tait.E2.80.93Bryan_angles
89102

90103
The following adapted from https://github.com/kriswiner/MPU-9250.git
@@ -97,11 +110,17 @@ is positive, up toward the sky is negative.
97110
Roll is angle between sensor y-axis and Earth ground plane, y-axis up is positive roll.
98111
These arise from the definition of the homogeneous rotation matrix constructed from quaternions.
99112
Tait-Bryan angles as well as Euler angles are non-commutative; that is, the get the correct orientation
100-
the rotations must be applied in the correct order which for this configuration is yaw, pitch, and then roll.
101-
For more see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles which has additional links.
113+
the rotations must be applied in the correct order which for this configuration is yaw, pitch,
114+
and then roll. For more see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
115+
which has additional links.
116+
117+
I have seen sources which contradict the above directions for yaw (heading) and roll (bank).
102118

103119
### Beta
104120

121+
The Madgwick algorithm has a "magic number" Beta which determines the tradeoff between accuracy
122+
and response speed.
123+
105124
Source: https://github.com/kriswiner/MPU-9250.git
106125
There is a tradeoff in the beta parameter between accuracy and response speed.
107126
In the original Madgwick study, beta of 0.041 (corresponding to GyroMeasError of 2.7 degrees/s)

fusion.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Sensor fusion for the micropython board. 22nd May 2015
2-
# Ported to Python by Peter Hinch
3-
# V0.2 Experimental. This is alpha quality code and needs further testing.
1+
# Sensor fusion for the micropython board. 24th May 2015
2+
# Ported to MicroPython/Pyboard by Peter Hinch.
3+
# V0.5 angles method replaced by yaw pitch and roll properties
4+
# V0.4 calibrate method added
45

56
import pyb
67
from math import sqrt, atan2, asin, degrees, radians
@@ -15,8 +16,7 @@
1516
class Fusion(object):
1617
'''
1718
Class provides sensor fusion allowing yaw, pitch and roll to be extracted. This uses the Madgwick algorithm.
18-
The update method must be called peiodically. The calculations take 1.6mS on the Pyboard. Suggested update
19-
rate 50Hz (20mS)
19+
The update method must be called peiodically. The calculations take 1.6mS on the Pyboard.
2020
'''
2121
declination = 0 # Optional offset for true north. A +ve value adds to yaw (anticlockwise rotation)
2222
def __init__(self):
@@ -51,14 +51,6 @@ def roll(self):
5151
return degrees(atan2(2.0 * (self.q[0] * self.q[1] + self.q[2] * self.q[3]),
5252
self.q[0] * self.q[0] - self.q[1] * self.q[1] - self.q[2] * self.q[2] + self.q[3] * self.q[3]))
5353

54-
def angles(self):
55-
yaw = degrees(atan2(2.0 * (self.q[1] * self.q[2] + self.q[0] * self.q[3]),
56-
self.q[0] * self.q[0] + self.q[1] * self.q[1] - self.q[2] * self.q[2] - self.q[3] * self.q[3]))
57-
pitch = degrees(-asin(2.0 * (self.q[1] * self.q[3] - self.q[0] * self.q[2])))
58-
roll = degrees(atan2(2.0 * (self.q[0] * self.q[1] + self.q[2] * self.q[3]),
59-
self.q[0] * self.q[0] - self.q[1] * self.q[1] - self.q[2] * self.q[2] + self.q[3] * self.q[3]))
60-
return yaw + self.declination, pitch, roll
61-
6254
def update_nomag(self, accel, gyro): # 3-tuples (x, y, z) for accel, gyro
6355
ax, ay, az = accel # Units G (but later normalised)
6456
gx, gy, gz = (radians(x) for x in gyro) # Units deg/s

fusionlcd.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212

1313
Calibrate = False
1414

15+
def scale(func, template, *vecs):
16+
res = []
17+
for v in vecs:
18+
res.append(tuple(map(func, template, v)))
19+
return res
20+
21+
rot = (-1, 1, 1)
22+
1523
def lcd_thread(mylcd, imu):
1624
if Calibrate:
1725
sw = pyb.Switch()
@@ -27,8 +35,9 @@ def lcd_thread(mylcd, imu):
2735
count += 1
2836
if count % 25 == 0:
2937
mylcd[1] = "{:7.0f} {:7.0f} {:7.0f}".format(fuse.yaw, fuse.pitch, fuse.roll)
30-
mx, my, mz = imu.get_mag() # Note blocking mag read
31-
fuse.update(imu.get_accel(), imu.get_gyro(), (mx, my, mz))
38+
# vectors = scale(lambda x, y: x*y, rot, imu.get_mag())
39+
fuse.update(imu.get_accel(), imu.get_gyro(), imu.get_mag())
40+
# fuse.update(imu.get_accel(), imu.get_gyro(), (mx, my, mz))
3241
# fuse.update_nomag(imu.get_accel(), imu.get_gyro())
3342

3443
objSched = Sched()

fusiontest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@
99
fuse = Fusion()
1010

1111
Calibrate = False
12+
Timing = True
1213

1314
if Calibrate:
1415
print("Calibrating. Press switch when done.")
1516
sw = pyb.Switch()
1617
fuse.calibrate(imu.get_mag, sw)
1718
print(fuse.magbias)
1819

20+
if Timing:
21+
mag = imu.get_mag() # Don't include blocking read in time
22+
accel = imu.get_accel() # or i2c
23+
gyro = imu.get_gyro()
24+
start = pyb.micros()
25+
fuse.update(accel, gyro, mag) # 1.65mS on Pyboard
26+
t = pyb.elapsed_micros(start)
27+
print("Update time (uS):", t)
28+
1929
count = 0
2030
while True:
2131
pyb.delay(20)

0 commit comments

Comments
 (0)