Skip to content

Add a GPS + IMU Fusion Part#1237

Open
Phill310 wants to merge 10 commits into
autorope:mainfrom
Phill310:main
Open

Add a GPS + IMU Fusion Part#1237
Phill310 wants to merge 10 commits into
autorope:mainfrom
Phill310:main

Conversation

@Phill310

@Phill310 Phill310 commented Jun 12, 2026

Copy link
Copy Markdown

This is the addition mentioned in #1236.

  • adds the BNO08x IMU into IMU.py
  • creates a fusion part in gps_imu_fusion.py
    This fusion part uses a 5 state Kalman filter to predict your position based on your velocity and acceleration data. The 5th variable is an acceleration bias to prevent drift when the car isn't moving. There is also a low pass EMA filter to clean out noise. Finally we added a friction factor to slowly bleed velocity.

We tested with a SparkFun NEO-F10N GPS which had a frequency of 10Hz. I believe the BNO08x IMU has a frequency of 50 Hz so in theory the fusion part can go up to 50 Hz. The main limiting factor is the drive loop frequency since the fusion part recalculates position once every drive loop.

This should support any GPS since the fusion part only takes the x and y position data from the gps.

Config Options added:

  • USE_FUSION
  • FUSION_DEBUG

I hope this explains everything we did. If not please ask questions so I can expand on our thinking.
Should I also make a pr in the docs repo to add a section for the fusion part and update the IMU section?

@TCIII

TCIII commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

@Phill310,

Are the x and y GPS positions in degrees or in meters from the Greenwich Meridian and the Equator?

Regards,
TCIII

@Phill310

Copy link
Copy Markdown
Author

@Phill310,

Are the x and y GPS positions in degrees or in meters from the Greenwich Meridian and the Equator?

Regards, TCIII

I believe that the Fusion part outputs the position in UTM coordinates (same as the gps part as far as I know). So yes it give the distance from the equator in meters but for easting it outputs the number of meters from its specific zone's central meridian. Is this something we should describe in the fusion class?

@DocGarbanzo DocGarbanzo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Phill310 - this is good PR that we would like to merge. I have two general feedback points:

  1. We should not have to call pip install some_package to make use of the BNO085 or other sensors. The relevant python packages should be added to setup.cfg so everything works out of the box.
  2. The IMU interfaces use scalars instead of vectors for acceleration, gyro, quaternions, etc. That is bad, but it is of course not your fault as such code was there before. It would be great if instead of making this even worse, we would clean this up as part of this PR as for the moment, no-one is really using IMU data yet. - Let me know if this exceeds your intended contribution and we can do a refactoring PR afterwards.

Comment thread donkeycar/parts/gps_imu_fusion.py Outdated
cosy_cosp = 1 - 2 * (qj * qj + qk * qk)
return math.atan2(siny_cosp, cosy_cosp)

def run(self, gps_x, gps_y, ax, ay, az, gx, gy, gz, qi, qj, qk, qr):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change the interface to pass vectors instead of single coordinates:
[gps_x, gpy_y] -> gps
[ax, ay, az] -> a or accel?
[gx, gy, gz] -> g or gyro?
[qi, qj, qk, qr] -> q or quarternion?

Comment thread donkeycar/parts/imu.py

- MPU9250
pip install mpu9250-jmdev

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These install commands are partially wrong (sudo...). can you add the python dependencies into setup.cfg so they get installed as part of the standard donkey install?

Comment thread donkeycar/parts/imu.py Outdated
self.gyro = { 'x' : 0., 'y' : 0., 'z' : 0. }
self.accel = {'x': 0., 'y': 0., 'z': 0.}
self.gyro = {'x': 0., 'y': 0., 'z': 0.}
self.mag = {'x': 0., 'y': 0., 'z': 0.}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dictionaries are unnecessary here, please just use vectors (or lists, if you prefer python native over numpy. Both types would be fine).

Comment thread donkeycar/parts/imu.py Outdated
self.mag = { 'x' : ret[13], 'y' : ret[14], 'z' : ret[15] }
self.accel = {'x': ret[1] * GRAVITY, 'y': ret[2] * GRAVITY, 'z': ret[3] * GRAVITY}
self.gyro = {'x': ret[4], 'y': ret[5], 'z': ret[6]}
self.mag = {'x': ret[13], 'y': ret[14], 'z': ret[15]}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, vectors are better than dicts.

Comment thread donkeycar/parts/imu.py Outdated


def run_threaded(self):
return self.accel['x'], self.accel['y'], self.accel['z'], self.gyro['x'], self.gyro['y'], self.gyro[

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be much cleaner to return a tuple of two vectors (accel and gyro) and a float (temp), instead of unravelling the vectors.

if cfg.SIM_RECORD_LOCATION:
inputs += ['pos/pos_x', 'pos/pos_y', 'pos/pos_z', 'pos/speed', 'pos/cte']
types += ['float', 'float', 'float', 'float', 'float']
types += ['float', 'float', 'float', 'float', 'float']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this...

types += ['float', 'float', 'float', 'float', 'float']
types += ['float', 'float', 'float', 'float', 'float']
if cfg.SIM_RECORD_GYROACCEL:
inputs += ['gyro/gyro_x', 'gyro/gyro_y', 'gyro/gyro_z', 'accel/accel_x', 'accel/accel_y', 'accel/accel_z']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this...

types += ['float', 'float', 'float', 'float', 'float', 'float']
types += ['float', 'float', 'float', 'float', 'float', 'float']
if cfg.SIM_RECORD_VELOCITY:
inputs += ['vel/vel_x', 'vel/vel_y', 'vel/vel_z']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and this as well.

Comment thread donkeycar/templates/complete.py Outdated
@@ -877,7 +873,7 @@ def add_camera(V, cfg, camera_type):
'imu/acl_x', 'imu/acl_y', 'imu/acl_z',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't want to touch this class, but want to work with vectors, then you can write a one-liner class that combines x, y, z values into a vector, like:

class Vectoriser:
    def run(x, y, z):
        return [x, y, z]

Comment thread donkeycar/templates/path_follow.py Outdated
V.add(fusion,
inputs=[
'gps/utm/longitude', 'gps/utm/latitude', # From GPS
'imu/acl_x', 'imu/acl_y', 'imu/acl_z', # From IMU

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, use vectors here.

@aokhader

aokhader commented Jun 14, 2026

Copy link
Copy Markdown

We are working on refactoring our code to use vectors instead of scalars, and we added the package install to setup.cfg. For the refactoring of the IMU code in general and for the code that wasn't directly from our fusion module, we left it as-is since we thought it was the base setup for the code. We are going to try to refactor the the code for all the IMU modules, but I'm not sure if we can verify if everything works the way it should for all the components including the camera since it would be out of our scope. Right now it is a PR in our repo if it helps.

@aokhader

aokhader commented Jun 17, 2026

Copy link
Copy Markdown

I've refactored the IMU code to use vectors and changed all the dependencies for the IMU to do the same, as well as verified that it works properly on path following. Is there anything else that needs fixing in this code?
Just to reiterate, the IMU fusion module is GPS-agnostic, so it can be used with a GPS with RTK corrections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants