Full 6-DOF Fusion
Node ID: fullFusion · Role: Filter · Realtime config: no
Description
Unscented Kalman filter fusing IMU with optical tracking into a full 6-DOF pose: position, velocity, orientation, gyro and accelerometer bias, and gravity. Optical measurements are latency-compensated through a delayed-measurement scheme, so it tracks position as well as orientation.
Algorithm notes
How it works
This node blends a high-rate IMU with a lower-rate optical tracker into a single smooth 6-DOF pose. The IMU carries the pose between optical frames and fills in fast motion; the optical tracker supplies the absolute position and orientation that keeps the result from drifting. Because optical frames always arrive a little after the moment they describe, the node remembers what its state looked like at the instant each frame was captured and corrects that instant, then carries the correction forward. Along the way it learns the gyroscope and accelerometer biases and the local gravity direction, so accuracy improves over the first seconds of use.
What you must configure
IMU to Optical Frame must describe how the IMU is mounted relative to the tracked target. If it is wrong, the two sensors disagree about which way the rig is turning and the pose will shake or run away during motion.
Target to IMU Offset is the distance from the tracked optical target to the IMU, in the body frame. Without it, every rotation of the rig injects position error. Measure it from CAD or by hand; centimetre accuracy is enough. If you cannot measure it, enable Estimate Offset Automatically and rotate the rig for a few seconds - rotation is what makes the offset observable. Tighten Offset Prior (m) when you know the value well and only want it refined.
Optical Frame Rate (Hz) and Optical Latency (ms) must match the real tracker. They decide when the node takes its snapshots. Reference to Optical Frame and Reference to Optical Offset are optional: use them to report a point other than the tracked target, for example an eye point or a tool tip.
How it starts
Nothing is output until the first optical frame arrives - that frame anchors the pose. From then on, one pose is emitted per IMU sample. Biases stay frozen until gravity looks right and the rig is nearly still, so leave it stationary briefly after start-up. A large orientation jump against the optical data re-anchors the pose automatically.
Tuning
- Optical Position Error (m) / Optical Orientation Error (rad): raise to smooth a jittery tracker, lower to follow it more tightly.
- Gyro Noise (deg/s) / Accelerometer Noise (m/s^2): raise if the pose looks over-confident between optical frames or the biases wander.
- Optical Timing Window (ms): raise if frames are being rejected, lower if the timing is noisy.
- Dead Reckoning Max Time (ms): raise to coast further through occlusions, lower to freeze position sooner.
- Use RK4 Integration: turn on for very fast motion, at extra CPU cost.
Troubleshooting
- Position drifts or lags while orientation is fine - optical frames are not being fused. Check Optical Latency (ms) and Optical Frame Rate (Hz) against the tracker, and that Optical Expiry Age (ms) is longer than the latency plus one frame (leave it empty to derive it).
- Pose swims or overshoots when the rig turns - Target to IMU Offset is wrong or IMU to Optical Frame does not match the mounting.
- Output is jittery - lower the trust in the tracker by raising Optical Position Error (m).
- Pose reacts sluggishly to real motion - Optical Position Error (m) or the noise values are too high.
- Nothing is output - no optical data has arrived yet, or no IMU data is reaching the node.
- Position freezes during occlusions - expected past Dead Reckoning Max Time (ms); raise it if your gaps are longer.
Inputs / Outputs
- Inputs:
Imu,Optical - Outputs:
FusedPose,FusionStateInt
Config aliases
FullFusionFilter, fullFusionFilter, fullFusion
Required feature
imu_optical_fusion
Properties
Each row shows the setting’s label in the node’s Properties panel and, in code, its key in the node’s settings object in the config file. A setting omitted from the config file uses the default shown here.
| Property | Type | Default | Description |
|---|---|---|---|
IMU to Optical Frame imuToOpticalFrameQuat | quaternion | {"w":1.0,"x":0.0,"y":0.0,"z":0.0} | Quaternion (w,x,y,z) rotating the IMU body frame into the frame the optical tracker reports the rigid body in. Accounts for how the IMU is mounted relative to the optical target. Identity means the two frames coincide. |
Reference to Optical Frame referenceToOpticalFrameQuat | quaternion | {"w":1.0,"x":0.0,"y":0.0,"z":0.0} | Quaternion (w,x,y,z) applied to the fused pose on output, right-multiplied so it acts on the body frame. Use it to move the reported orientation from the tracked target to the point of interest. |
Reference to Optical Offset referenceToOpticalFrameVec | vector3 | {"x":0.0,"y":0.0,"z":0.0} | Lever-arm offset in meters from the tracked optical target to the point of interest, expressed in the body frame. Rotated by the fused orientation and added to the output position. |
Optical Tracker
| Property | Type | Default | Description |
|---|---|---|---|
Optical Frame Rate (Hz) opticalFPS | number | 90.0 | Frame rate of the optical tracker. Sets the spacing at which the filter schedules the delayed-measurement snapshots that latency compensation matches incoming frames against. |
Optical Latency (ms) opticalLatencyMS | number | 25 | Expected age of an optical frame when it reaches the filter. Used to schedule how far ahead a snapshot is taken; the per-frame latency reported by the source is what each measurement is actually matched with. |
Optical Timing Window (ms) opticalTimingWindowMS | number | 11 | How far an arriving optical frame’s capture time may differ from a pending snapshot’s time and still be matched to it. Too small and frames get rejected; too large and they are attributed to the wrong instant. |
Optical Expiry Age (ms) opticalExpiryAgeMS | number | — | How long a pending snapshot is kept before it is discarded. Must exceed the real optical latency or measurements arrive after their snapshot is gone. Leave empty to derive it from the frame rate plus the expected latency. |
Dead Reckoning Max Time (ms) deadReckonMaxTimeMS | number | 500 | How long the filter keeps integrating position from the IMU after optical data stops. Past this it holds position and tracks orientation only, so an occlusion cannot fling the pose away. |
Noise Model
| Property | Type | Default | Description |
|---|---|---|---|
Optical Position Error (m) posErrorM | number | 0.001 | One-sigma uncertainty of the optical position measurement. Raise it to trust the IMU more and smooth optical jitter, lower it to follow the tracker more tightly. |
Optical Orientation Error (rad) rotErrorRad | number | 0.01 | One-sigma uncertainty of the optical orientation measurement. |
Gyro Noise (deg/s) gyroErrorDegS | number | 0.1 | One-sigma gyroscope noise driving the prediction step. Setting it too small makes the filter overconfident in the gyro and pushes the error into the estimated bias. |
Accelerometer Noise (m/s^2) accErrorMS2 | number | 0.3 | One-sigma accelerometer noise driving the prediction step. |
Use RK4 Integration useRK4 | boolean | false | Integrate the state with fourth-order Runge-Kutta instead of Euler. More accurate for fast motion at four times the derivative evaluations per step. |
Antipodal Threshold antipodalThreshold | number | 0.9 | Dot-product threshold below which a measured orientation is treated as antipodal to the predicted one and flipped to the equivalent Modified Rodrigues parameters. Guards rotations near 180 degrees. |
Lever Arm
| Property | Type | Default | Description |
|---|---|---|---|
Target to IMU Offset (m) leverArmM | vector3 | {"x":0.0,"y":0.0,"z":0.0} | Vector in the body frame from the tracked optical target to the IMU. The accelerometer senses motion at the IMU, so without this the rotation of the rig injects position error. Also the starting point when the offset is estimated. |
Estimate Offset Automatically estimateLeverArm | boolean | false | Solve for the target-to-IMU offset online instead of trusting the entered value. Needs rotation to observe: turning the rig moves the target relative to the IMU, which is what pins the offset down. Costs three extra state dimensions. |
Offset Prior (m) leverArmSigmaM | number | 0.05 | How far the estimated offset may move away from the entered value, one sigma. Lower it when the offset is known from CAD and only needs refining. |
Example node definition
A minimal entry in config.json looks like the block below. Paste it under the sinks key, keyed by an instance name of your choice. All settings are optional: anything not listed under settings uses the default from the Properties table above. See The configuration file for how the sections and endpoint wiring work.
{
"sinks": {
"fullFusion": {
"dataEndpoint": "inproc://fullFusion_data",
"inputEndpoints": [
"inproc://imu_data",
"inproc://optical_data"
],
"inputDataFilter": [
"Imu",
"Optical"
],
"settings": {}
}
}
}