Ackermann / car-like robots¶
For robots that steer like a car: F1/10 racers, full-size vans, golf carts, agricultural vehicles, anything with front-wheel steering. Unlike differential drive robots, these can't spin in place. They turn by angling the front wheels, which means the geometry of how the robot moves is different.
FusionCore has a built-in Ackermann motion model that accounts for this. It applies the same non-holonomic constraint as the DifferentialDrive model (the robot can't slide sideways) but is parameterized for car-like steering.
Setting it up¶
Two things to set in your config:
Wheelbase is the distance between the centre of your front axle and the centre of your rear axle, measured in metres. Get it wrong and the filter's motion predictions will be slightly off. Measure it physically on your robot rather than looking it up online: manufacturing tolerances vary.
| Platform | Typical wheelbase |
|---|---|
| F1/10 scale car (Traxxas Slash base) | ~0.32 m |
| F1/10 scale car (AckerBot, MIT Racecar) | ~0.32–0.34 m |
| Golf cart | ~1.8 m |
| Ford Transit full-size van (130" WB) | ~3.30 m |
| Ford Transit full-size van (148" WB) | ~3.75 m |
| Mercedes Sprinter | ~3.67 m |
| Ford Transit Connect (compact van) | ~2.73 m |
F1/10 indoor setup¶
Sensors: VESC wheel encoder + RealSense D435i IMU + optional VSLAM or LiDAR scan-matching
ros2 launch fusioncore_ros fusioncore.launch.py \
fusioncore_config:=$(ros2 pkg prefix fusioncore_ros)/share/fusioncore_ros/config/f1tenth_indoor.yaml \
--ros-args \
-r /odom/wheels:=/vesc/odom \
-r /imu/data:=/camera/imu
Note: the D435i IMU topic changed between driver versions. Use /camera/imu with realsense2_camera v3.x, or /camera/camera/imu with v4.x (ROS 2 Humble and later). Check with ros2 topic list | grep imu after launching the camera node.
If you're using KISS-ICP or RealSense T265 visual odometry as a second velocity source, uncomment encoder2.topic in f1tenth_indoor.yaml:
encoder2.topic: "/kiss/odometry" # KISS-ICP on LiDAR
# or
encoder2.topic: "/camera/odom/sample" # RealSense T265 visual odometry
Note: encoder2 requires a nav_msgs/Odometry topic with velocity in the twist field. slam_toolbox publishes a pose topic, not an odometry topic, so it won't work here directly.
The full config is at config/f1tenth_indoor.yaml.
Van / large vehicle outdoor setup¶
Sensors: ZED 2i (IMU + visual odom) + wheel encoder or by-wire odometry + GPS
ros2 launch fusioncore_ros fusioncore.launch.py \
fusioncore_config:=$(ros2 pkg prefix fusioncore_ros)/share/fusioncore_ros/config/van_outdoor_gps.yaml \
--ros-args \
-r /odom/wheels:=/your/wheel/odom \
-r /imu/data:=/zed/zed_node/imu/data \
-r /gnss/fix:=/your/gps/fix
If you don't have wheel encoders yet, remap /odom/wheels to the ZED visual odometry and it becomes your only velocity source:
Then set encoder2.topic: "" in the config (disable the second visual odom source since both would be the same topic).
The full config is at config/van_outdoor_gps.yaml.
VESC gives zero covariance. Is that a problem?¶
No. FusionCore checks the covariance in every incoming odometry message. If it's zero or negative (which is what VESC outputs by default), FusionCore silently ignores the message covariance and uses the encoder.vel_noise and encoder.yaw_noise values from your config instead. You don't need to patch the VESC driver.
Which IMU to use if you have two¶
You can fuse both. Set imu2.topic in your config to the second IMU topic and FusionCore will treat them as independent measurements of the same state:
imu2.topic: "/vesc/imu"
imu2.frame_id: "" # or override if driver uses a non-standard frame
imu2.remove_gravitational_acceleration: false
Both IMUs must be in (or TF-transformable to) base_link.
For most F1/10 setups the RealSense D435i IMU is the better primary (/imu/data). It has a lower noise floor than most VESC motor controllers and handles the high angular rates of racing turns well. The VESC IMU then becomes the secondary (imu2.topic).
If you only want one IMU, just leave imu2.topic: "" and remap /imu/data to whichever sensor you prefer.
ZED 2i: one important config difference¶
The ZED SDK subtracts gravity from the IMU before publishing, which most IMU drivers don't do. If you use a ZED 2i, set this in your config:
Without this, FusionCore will assume the IMU is publishing raw specific force (gravity included) and will try to handle gravity internally. Since it's already been removed by the ZED SDK, the filter ends up double-correcting and the state estimate goes wrong.
Quick check: look at linear_acceleration.z when the vehicle is stationary on flat ground. If it reads ~9.8 m/s², leave the flag false. If it reads ~0.0 m/s², set it true.
Disabling TF broadcasting¶
By default FusionCore broadcasts the odom -> base_link transform. If another node already owns that transform, or you're running two FusionCore instances and only one should broadcast TF, set:
/fusion/odom keeps publishing normally. Only the TF broadcast is suppressed.
SLAM integration¶
Same as any other FusionCore setup:
Just launch both. They don't conflict.