Sim localization
The MAP panel on /sim/[robotId] switches between building a grid and estimating pose on a frozen grid. Both paths run in the browser. This page does not open a socket.
Modes
LocalizationSource.mode is mapping or localization.
- Mapping (simulated SLAM) —
InBrowserMapSourcekeeps integrating planar scans. The occupancy grid, costmaps, and travelled path keep updating. - Localize on saved map — the current grid is copied and frozen, or the user loads
map.pgmandmap.yaml. The copy is parsed withreadMapServer(the same map_server reader the save button uses). The live grid is not written. Switching back to mapping resumes it from the cells it already had.
Loading a file selects localization and keeps that file until Freeze built map snapshots the live grid again. A bad file leaves the current mode and the live grid unchanged.
Particle filter
ParticleFilter is the in-browser estimator. The default cloud is 300 particles. The count, motion noise, sensor mixture, and initial-pose sigmas are constructor options.
- Motion. The increment is the simulator base pose from the previous update to this one, expressed in the previous body frame (
xforward,yleft). Each particle applies that increment in its own body frame. Translation and yaw noise are Gaussian. The scales arealphaTrans,alphaTransRot,transFloor,alphaYaw,alphaYawTrans, andyawFloor. A step with no motion adds no noise. - Measurement. A ring of ranges is cast with the mapping ray walker (
rayCellsInGrid) from the simulator pose against the sim obstacles. Bearings match the mapper: beamiis atyaw + (i + 0.5) * 2π / N - π. Each hit is scored on a likelihood field, the Euclidean distance from the beam endpoint to the nearest occupied cell (occupancy>= 65). The mixture iszHit * exp(-d² / 2σ²) + zRand. A max-range beam useszMaxand does not consult the field. Weights are normalised with a log-sum-exp. - Resampling. Low-variance resampling (one uniform draw in
[0, 1/N), then a stride of1/N) runs when the effective sample size falls below half the cloud. Output weights are uniform and sum to 1. - Estimate. Weighted mean of
xandy. Yaw is the weighted circular mean. Covariance is the weighted 3×3 of[x, y, yaw], row-major. - Initial pose.
setInitialPosedraws a Gaussian around the givenx,y, and yaw and sets the state back toinitialising. The numeric fields on the MAP panel call this. A click-drag pose tool is not part of this panel. - State.
initialisingafter a new prior, while the cloud is still wide, or when the frozen grid has no occupied cells.trackingwhen the position spread is under 0.45 m and the hit beams agree with the map.lostwhen the hit beams disagree with a map that does contain obstacles, or the spread exceeds 1.6 m.
The viewport shows the estimate error against the simulator pose, in metres, a state pill, and the particle cloud as a map layer. The panel copy is "in-browser estimator (simulated)".
LocalizationSource
interface LocalizationSource {
readonly kind: "in-browser" | "rosbridge";
readonly mode: "mapping" | "localization";
readonly state: "initialising" | "tracking" | "lost";
readonly estimate: { x: number; y: number; yaw: number };
readonly covariance: readonly [
number, number, number,
number, number, number,
number, number, number,
]; // row-major x, y, yaw
setInitialPose(pose: { x: number; y: number; yaw: number }): void;
}
InBrowserLocalizationSource is the in-browser kind. RosbridgeLocalizationSource is the rosbridge kind. The MAP panel constructs only the in-browser source.
Rosbridge stub
RosbridgeLocalizationSource accepts already-decoded JSON. Constructing it does not open a socket, subscribe, or apply TF.
Mapping mode is the slam_toolbox side. The grid is still nav_msgs/OccupancyGrid on /map, documented in docs/sim-mapping.md. This stub does not build that grid. Switching the stub to mapping clears the particle list and returns the state to initialising.
Localization mode is AMCL.
| Topic | Message | Role |
|---|---|---|
/amcl_pose |
geometry_msgs/PoseWithCovarianceStamped |
ingestAmclPose copies the pose and the planar part of the covariance |
/initialpose |
geometry_msgs/PoseWithCovarianceStamped |
setInitialPose stores the message a publisher would send |
/particle_cloud |
geometry_msgs/PoseArray |
ingestParticleCloud copies the poses |
Nav2 AMCL's default cloud topic is /particlecloud. This shape uses /particle_cloud. A future bridge can alias the two; the stub does not subscribe to either.
PoseWithCovarianceStamped as rosbridge JSON:
interface RosbridgePoseWithCovarianceStamped {
header: { stamp: { sec: number; nanosec: number }; frame_id: string };
pose: {
pose: {
position: { x: number; y: number; z: number };
orientation: { x: number; y: number; z: number; w: number };
};
covariance: number[]; // length 36, row-major 6×6: x, y, z, roll, pitch, yaw
};
}
The planar 3×3 reads covariance indices 0 (x x), 1 (x y), 5 (x yaw), 6 (y x), 7 (y y), 11 (y yaw), 30 (yaw x), 31 (yaw y), and 35 (yaw yaw). Yaw comes from the quaternion. z, roll, and pitch are ignored.
PoseArray as rosbridge JSON:
interface RosbridgePoseArray {
header: { stamp: { sec: number; nanosec: number }; frame_id: string };
poses: Array<{
position: { x: number; y: number; z: number };
orientation: { x: number; y: number; z: number; w: number };
}>;
}
ingestAmclPose sets the state to tracking when xx + yy is at most 2, and to lost when it is larger. The frame id is stored on the message and is not transformed.