What is global localization
Global localization is knowing where the robot is in a persistent, externally-fixed frame, a map or a GPS datum, not just where it is relative to where it started. Local odometry answers "how far from the starting point"; global localization answers "what is the robot's pose in the world", so the same place has the same coordinates every run.
Everything in this section runs on limit, the robot. The three sensors, the ZED 2i, the Ouster OS0-128, and the F9P, are on the dog, and so is the full Nav2 live stack: the EKF, navsat_transform, AMCL, and Nav2 all run on the robot. It is a self-contained system with no separate server in the loop.
Three approaches feed the pose:
- VIO (local): the ZED 2i visual-inertial odometry gives a short-term, centimeter-level pose relative to boot. It is the foundation the other two build on.
- GNSS (absolute, outdoor): with sky view, the F9P anchors the pose in a world frame via
navsat_transform→ EKFodom1. - Map-matching (indoor): AMCL matches the Ouster scan against a pre-built map to place the robot in the map frame (
map → odom).
The three sensors
All three run on the robot and feed a different part of the pose. The ZED 2i is the continuous source; the Ouster and the F9P pin it to a world frame.
| Sensor | Role in global localization | Key spec | Main topics |
|---|---|---|---|
| ZED 2i (front stereo) | PRIMARY continuous VIO odometry (local pose) + the IMU source. The foundation the EKF fuses. | stereo, depth-referenced (meters, no scale drift); VIO ~20–28 Hz · IMU ~99 Hz · depth cloud ~4.9 Hz · RGB ~2.3 Hz | /zed2i_front/zed_node/odom, /imu/data |
| Ouster OS0-128 (rev7) | AMCL map-matching input (indoor) + the obstacle/costmap layer. | 128-channel mid-range LiDAR; ~3–6 Hz (driver reader cap); AMCL reads it out to 30 m | /ouster/points, /ouster/scan → /scan_corrected |
| u-blox F9P (GNSS) | outdoor absolute anchor (lat/lon/alt → navsat datum). | 5 constellations (GPS/GLONASS/BeiDou/Galileo/QZSS); single-point ~±5 m, RTK 2 cm + 2 ppm; ~1 Hz; USB /dev/ttyACM0 |
/fix, /raw_nmea |
The ZED 2i's VIO is relative and drifts over long runs, in position, heading, and z; yaw is the fragile axis on a quadruped that rotates in place. The EKF cannot correct drift it cannot see, which is why the Ouster (AMCL) and the F9P (GNSS) exist. The ZED node runs with publish_tf:=false, so Nav2 owns the TF tree.
How the pose is built
One ekf_localization filter on the robot ties it together: a full 15-state 3D EKF (two_d_mode: false) at 30 Hz. It fuses the ZED IMU, the ZED VIO odometry, and the GNSS anchor when a fix is available, and publishes /odom plus the odom → base_link TF.
| Input | Source | Used for |
|---|---|---|
| imu0 | ZED 2i IMU via stamp_shift (/imu_corrected) | RPY + acceleration, gravity removed (~80–99 Hz) |
| odom0 | stamp-shifted ZED VIO odometry (/zed_odom_corrected) | position + orientation only |
| odom1 | navsat_transform output (/odometry/gps) | x, y when a GNSS fix is available |
With no fix (odom1 silent), the filter runs on IMU + VIO alone: valid 30 Hz /odom, origin at the ZED boot point. The moment a GNSS fix flows, odom1 adds x and y and the same output is globally anchored, with no reconfiguration.
Path A, GNSS (outdoor, absolute)
Chain: F9P on /dev/ttyACM0 → nmea_navsat_driver → /fix → navsat_transform → /odometry/gps → EKF odom1. Active when the antenna has sky view; once /fix is valid, navsat_transform sets the datum and /odometry/gps flows into the EKF. Indoors the receiver is on standby and the EKF runs on ZED VIO + IMU alone.
Path B, AMCL (indoor, map-relative)
Chain: /ouster/scan → stamp_shift → /scan_corrected → AMCL + costmaps. AMCL places the robot on a pre-built map using the Ouster scan and publishes map → odom (tf_broadcast: true). Config: set_initial_pose: true, robot_model_type: DifferentialMotionModel, laser_max_range: 30.0.
Position source fusion
The robot can estimate its position in several different ways, and each one can fail for a different reason. This is the pivotal point of the stack: the pose has to come from somewhere, and the sources will disagree.
- From the commands the robot was given. In simulation this is exact. On the real dog it is not: an obstacle in the path that results in a misstep means the commanded motion never happened.
- From GNSS. Absolute and persistent, but it needs sky. Cloudy weather degrades the fix, and indoors it disappears entirely.
- From IMU acceleration. Always available, but sensor readings can be off: bias, drift, vibration on a walking robot.
The ZED EKF solution is one option for addressing this: instead of trusting one source, it fuses them into a single estimate, weighted by how much each can be trusted at that moment. Additional functionality, per-sensor health checks and calibration visibility, is available through the live Limit dash. It is not part of the demo on this site.
What the EKF is
An EKF, an Extended Kalman filter, is a state estimator that alternates two steps. Predict advances the current best estimate with the motion model, here the IMU and the VIO odometry. Update corrects that prediction when a measurement arrives, a GNSS fix or a map match, in proportion to the relative trust in the two, tracked as a covariance, the filter's measured uncertainty in its own estimate. "Extended" means it handles the nonlinear motion model by linearizing it around the current estimate. The output is a single position with an honest error bar, so a disagreement between sources becomes a number you can read instead of a mystery.
This is also the point where we fine tune calibration, and calibration is the area where unusual behavior can result in missed understanding for users. The direction is total transparency: which source the robot trusted, why, and how confident it was, all readable. Machine learning evaluation of the position solution, the gait logic, and the vision systems is planned and not yet implemented.
F9P accuracy
The F9P has two accuracy modes, and the difference comes down to corrected data:
| Mode | What it is | Position accuracy |
|---|---|---|
| Single-point | the receiver alone, no base station | ~±5 m |
| RTK | the receiver subtracts a base station's measured error against its known position | 2 cm + 2 ppm |
The target operating point is RTK: a base station knows exactly where it is, measures the same satellites, and broadcasts its error; the F9P removes it and meter-class becomes centimeter-class. Indoors, with no sky view, the receiver publishes no fix at all, a design state rather than a failure: the EKF runs on ZED VIO + IMU until sky view returns.
What this setup enables
Once the pose is mapped to a GPS datum, the robot lives in a world frame that persists between runs. That is the "distance detail" this sensor set is built for:
- Site-scale positioning. With the GNSS anchor the pose is valid across the whole site, kilometer-scale, meter-class single-point and centimeter-class with RTK.
- Centimeter-level between fixes. The ZED 2i keeps tracking at centimeter-level short-term accuracy, so between 1 Hz GNSS updates the EKF output stays smooth, the F9P pins the frame and the ZED smooths the path.
- Indoor continuity. When sky view is lost, AMCL on the Ouster scan holds the robot in the map frame and the ZED VIO carries the pose until a match or a fix takes over.
- Repeatable runs. Goals you can set on a map and come back to, routes you can repeat, a robot that knows where it has been.
Related docs
Nav2 (stack dataflow, /cmd_vel chain) · Locomotion (CHAMP + CAN) · Topics (full roster)