Skip to content

Commit f77bf37

Browse files
Add bringup troubleshooting guide for common localization failures.
Documents the startup checklist, /alignment_status interpretation, and map visibility notes called out in issues #70, #35, #43, and #48 so new users can diagnose "starts but does not localize" without opening code. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4cd15f0 commit f77bf37

4 files changed

Lines changed: 194 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
- add `docs/frame_contract.md` for `map` / `odom` / `base_link` expectations and issue #58 / #27 guidance
1515
- publish TF immediately on `/initialpose` and add explicit frame/map diagnostics for issue #47
16+
- add `docs/troubleshooting.md` for bringup failures and `/alignment_status` interpretation (issues #70, #35, #43, #48)
1617
- guard `use_odom` integration until initial pose is valid and keep pose finite for NDT init guess
1718
- ignore non-finite `/initialpose` payloads and stop eager scan replay during pose reset
1819
- v1.1 relocalization is documented as a validated dry-run `/initialpose` command artifact only

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,12 @@ Default frames: `map` (global), `odom`, `base_link`.
122122
- Nav2-style mode uses `enable_map_odom_tf: true` and requires an external `odom -> base_link` publisher
123123
- `use_odom: true` uses the `/odom` topic for pose prediction; it is not the same as odom TF
124124

125-
See [docs/frame_contract.md](docs/frame_contract.md) for the full contract and troubleshooting for
125+
See [docs/frame_contract.md](docs/frame_contract.md) for the full contract for
126126
issues [#58](https://github.com/rsasaki0109/lidar_localization_ros2/issues/58) and
127127
[#27](https://github.com/rsasaki0109/lidar_localization_ros2/issues/27).
128128

129+
If the node starts but pose does not update, see [docs/troubleshooting.md](docs/troubleshooting.md).
130+
129131
## Nav2 Requirements
130132

131133
For full Nav2 use, provide:
@@ -263,6 +265,7 @@ git branch -D <branch>
263265
| Experiment interfaces and decisions | [docs/interfaces.md](docs/interfaces.md), [docs/experiments.md](docs/experiments.md), [docs/decisions.md](docs/decisions.md) |
264266
| Roadmap | [docs/competitive_roadmap.md](docs/competitive_roadmap.md) |
265267
| Reliability / open issues | [docs/reliability_roadmap.md](docs/reliability_roadmap.md) |
268+
| Bringup troubleshooting | [docs/troubleshooting.md](docs/troubleshooting.md) |
266269
| Frame / TF contract | [docs/frame_contract.md](docs/frame_contract.md) |
267270

268271
## ROS 2 Support

docs/reliability_roadmap.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,24 @@ Last triaged: 2026-06-10
1818

1919
| Issue | Title | Category | Priority | Status | Next action |
2020
| --- | --- | --- | --- | --- | --- |
21-
| [#76](https://github.com/rsasaki0109/lidar_localization_ros2/issues/76) | initial pose result process died | crash | P0 | in progress | guard non-finite `/initialpose`; stop eager `cloudReceived` on reset |
21+
| [#76](https://github.com/rsasaki0109/lidar_localization_ros2/issues/76) | initial pose result process died | crash | P0 | fix in main | non-finite `/initialpose` rejected; no eager scan replay on reset |
2222
| [#47](https://github.com/rsasaki0109/lidar_localization_ros2/issues/47) | rviz no map frame; node dies on 2D pose | crash | P0 | fix in main | publish TF on `/initialpose`; clearer frame/map diagnostics |
23-
| [#56](https://github.com/rsasaki0109/lidar_localization_ros2/issues/56) | crash with `use_odom:=true` | crash | P0 | fix in branch | skip odom before initial pose; guard non-finite pose integration |
23+
| [#56](https://github.com/rsasaki0109/lidar_localization_ros2/issues/56) | crash with `use_odom:=true` | crash | P0 | fix in main | skip odom before initial pose; guard non-finite pose integration |
2424
| [#58](https://github.com/rsasaki0109/lidar_localization_ros2/issues/58) | strange tf_tree with odom frame | frame / TF | P1 | documented | see [frame_contract.md](frame_contract.md) Mode A vs B |
2525
| [#27](https://github.com/rsasaki0109/lidar_localization_ros2/issues/27) | use odom TF instead of odom topic | frame / TF | P1 | documented | odom topic vs odom TF clarified; TF-only mode not implemented |
2626
| [#55](https://github.com/rsasaki0109/lidar_localization_ros2/issues/55) | `odom_frame_id_` defined but not used | frame / TF | P2 | open | code audit + doc alignment |
2727
| [#75](https://github.com/rsasaki0109/lidar_localization_ros2/issues/75) | map alignment | map | P1 | open | collect map frame / initial pose / GT offset evidence |
2828
| [#68](https://github.com/rsasaki0109/lidar_localization_ros2/issues/68) | MGRS map not displayed | map | P2 | open | document supported map formats and frame assumptions |
29-
| [#48](https://github.com/rsasaki0109/lidar_localization_ros2/issues/48) | map showing alongside live data in rviz | map | P2 | open | clarify `/initial_map` vs runtime map behavior |
29+
| [#48](https://github.com/rsasaki0109/lidar_localization_ros2/issues/48) | map showing alongside live data in rviz | map | P2 | documented | see [troubleshooting.md](troubleshooting.md) map visibility section |
3030
| [#44](https://github.com/rsasaki0109/lidar_localization_ros2/issues/44) | localization pose offset | map | P2 | open | separate map-frame issue from registration tuning |
3131
| [#72](https://github.com/rsasaki0109/lidar_localization_ros2/issues/72) | pose covariance | covariance | P2 | open | document current covariance semantics; no fusion claim yet |
32-
| [#70](https://github.com/rsasaki0109/lidar_localization_ros2/issues/70) | program starts but positioning fails | docs / diagnostics | P1 | open | add troubleshooting for no-initial-pose / no-map / no-cloud |
33-
| [#43](https://github.com/rsasaki0109/lidar_localization_ros2/issues/43) | map publishing clarification | docs | P3 | open | fold into map notes / troubleshooting |
32+
| [#70](https://github.com/rsasaki0109/lidar_localization_ros2/issues/70) | program starts but positioning fails | docs / diagnostics | P1 | documented | see [troubleshooting.md](troubleshooting.md) bringup checklist |
33+
| [#43](https://github.com/rsasaki0109/lidar_localization_ros2/issues/43) | map publishing clarification | docs | P3 | documented | see [troubleshooting.md](troubleshooting.md) map visibility section |
3434
| [#41](https://github.com/rsasaki0109/lidar_localization_ros2/issues/41) | how to reduce drift? | docs | P3 | open | point to public benchmark limits and tuning docs |
3535
| [#33](https://github.com/rsasaki0109/lidar_localization_ros2/issues/33) | about ros2 humble | docs | P3 | open | README now documents Jazzy-first / Humble-compatible |
3636
| [#34](https://github.com/rsasaki0109/lidar_localization_ros2/issues/34) | different sensor like Ouster | docs | P3 | open | point to `cloud_topic` / frame-id configuration |
3737
| [#49](https://github.com/rsasaki0109/lidar_localization_ros2/issues/49) | ERROR run with Rslidar | docs | P3 | open | collect sensor-specific launch params |
38-
| [#35](https://github.com/rsasaki0109/lidar_localization_ros2/issues/35) | direct positioning got stuck | docs / diagnostics | P2 | open | inspect `/alignment_status` reject reasons |
38+
| [#35](https://github.com/rsasaki0109/lidar_localization_ros2/issues/35) | direct positioning got stuck | docs / diagnostics | P2 | documented | see [troubleshooting.md](troubleshooting.md) stuck positioning section |
3939
| [#52](https://github.com/rsasaki0109/lidar_localization_ros2/issues/52) | different results | docs / diagnostics | P3 | open | require same map / seed / bag window for comparisons |
4040
| [#37](https://github.com/rsasaki0109/lidar_localization_ros2/issues/37) | high CPU usage | docs | P3 | open | document backend cost and crop settings |
4141
| [#54](https://github.com/rsasaki0109/lidar_localization_ros2/issues/54) | `corrent_pose_with_cov_stamped_ptr_` locking | enhancement | P3 | open | code audit if reproduced under concurrency |
@@ -69,6 +69,9 @@ Done criteria for sprint 1:
6969
- `#47` RViz initial pose:
7070
- `/initialpose` now publishes `map` TF immediately when possible
7171
- frame mismatch / map-not-ready warnings are explicit in node logs
72+
- Sprint 2 troubleshooting (`#70`, `#35`, `#43`, `#48`):
73+
- added [troubleshooting.md](troubleshooting.md) with bringup checklist and `/alignment_status` guide
74+
- README links troubleshooting from Frames And TF / Read More
7275
- README or `docs/benchmarking.md` links to this roadmap
7376
- fixed issues get a one-line note in `CHANGELOG.md` Unreleased reliability section
7477

docs/troubleshooting.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Troubleshooting
2+
3+
This guide covers the most common bringup failures reported in open issues
4+
[#70](https://github.com/rsasaki0109/lidar_localization_ros2/issues/70),
5+
[#35](https://github.com/rsasaki0109/lidar_localization_ros2/issues/35),
6+
[#43](https://github.com/rsasaki0109/lidar_localization_ros2/issues/43), and
7+
[#48](https://github.com/rsasaki0109/lidar_localization_ros2/issues/48).
8+
9+
For frame-tree and TF questions, see [frame_contract.md](frame_contract.md).
10+
For open-issue triage, see [reliability_roadmap.md](reliability_roadmap.md).
11+
12+
## Symptom: Node Starts But Pose Never Updates
13+
14+
This is the most common "it launches but does not localize" pattern.
15+
16+
### Minimum Bringup Checklist
17+
18+
Work through these in order:
19+
20+
1. **Map loaded**
21+
- `use_pcd_map:=true` with a valid `map_path`, or a `/map` publisher is running
22+
- log should show `Map Size <N>` with `N > 0`
23+
2. **Initial pose set**
24+
- publish `/initialpose` with `header.frame_id = map`
25+
- RViz **2D Pose Estimate** is the usual path; see [frame_contract.md](frame_contract.md)
26+
3. **LiDAR cloud arriving**
27+
- `ros2 topic hz <cloud_topic>` shows a steady rate
28+
- point cloud has `x`, `y`, `z` fields (`PointCloud2`)
29+
4. **Frames consistent**
30+
- `base_frame_id` matches your robot TF
31+
- lidar frame is reachable from `base_link` if TF is used
32+
33+
Quick smoke:
34+
35+
```bash
36+
source scripts/setup_local_env.sh
37+
ros2 launch lidar_localization_ros2 nav2_lidar_localization.launch.py \
38+
map_path:=/absolute/path/to/map.ply \
39+
cloud_topic:=/your/points
40+
41+
# other terminal
42+
ros2 topic echo /alignment_status --once
43+
ros2 topic echo /pcl_pose --once
44+
ros2 run tf2_ros tf2_echo map base_link
45+
```
46+
47+
If `/pcl_pose` never updates, inspect `/alignment_status` next.
48+
49+
## Reading `/alignment_status`
50+
51+
Topic: `/alignment_status` (`diagnostic_msgs/msg/DiagnosticArray`)
52+
53+
Each update publishes one status named `lidar_localization_ros2/alignment`.
54+
The `message` field is the primary reject reason. Key-value pairs carry context.
55+
56+
### Startup flags (check first)
57+
58+
| Key | Meaning when `false` |
59+
| --- | --- |
60+
| `map_received` | map not loaded yet; wait for `map_path` load or `/map` |
61+
| `initialpose_received` | no valid `/initialpose` in `map` frame yet |
62+
63+
### Common `message` values
64+
65+
| Message | Likely cause | What to try |
66+
| --- | --- | --- |
67+
| `ok` | last alignment accepted | localization is running |
68+
| `scan_missing_xyz_field` | cloud is not `PointCloud2` with x/y/z | fix driver conversion (common with custom lidar bridges) |
69+
| `filtered_scan_empty` | crop/range filter removed all points | check `min_scan_range` / `max_scan_range`, sensor FOV |
70+
| `local_map_crop_too_small` | predicted pose is outside local map crop | wrong initial pose or map frame offset; re-send `/initialpose` |
71+
| `registration_not_converged` | NDT/GICP did not converge | initial pose too far from truth; try closer seed |
72+
| `fitness_score_over_threshold_rejected` | match quality too poor | fix initial pose, map alignment, or sensor frame |
73+
| `fitness_score_over_threshold_consistency_recovered` | borderline reject overridden by consistency gate | monitor; may recover if environment is consistent |
74+
| `recovery_retry_from_last_pose_recovered` | retry from last good pose succeeded | transient glitch; usually OK |
75+
| `gtsam_update_rejected` / `ekf_update_rejected` | backend smoother rejected the update | often follows repeated measurement rejects |
76+
77+
### Useful numeric keys
78+
79+
| Key | How to use it |
80+
| --- | --- |
81+
| `fitness_score` | lower is better; compare to `effective_score_threshold` |
82+
| `consecutive_rejected_updates` | high streak means seed or map is wrong |
83+
| `seed_translation_since_accept_m` | large drift since last accept → open-loop problem |
84+
| `filtered_point_count` | very low → scan preprocessing or crop issue |
85+
| `reinitialization_requested` | `true` means node wants a new `/initialpose` |
86+
87+
Watch live:
88+
89+
```bash
90+
ros2 topic echo /alignment_status
91+
```
92+
93+
For benchmark runs, record diagnostics to CSV with `benchmark_diagnostic_recorder`
94+
(see [benchmarking.md](benchmarking.md)).
95+
96+
## Symptom: Direct Positioning Got Stuck (#35)
97+
98+
"Stuck" usually means repeated rejects, not a hung process.
99+
100+
1. echo `/alignment_status` and note `message` + `consecutive_rejected_updates`
101+
2. if `fitness_score_over_threshold_rejected`:
102+
- re-check initial pose in RViz (roughly correct position and yaw)
103+
- confirm map and trajectory are in the same coordinate convention
104+
3. if `local_map_crop_too_small`:
105+
- initial pose is far from where scans actually are
106+
- or `local_map_radius` is too small for your map scale
107+
4. if `registration_not_converged`:
108+
- move initial pose closer manually, then let scan matching refine
109+
110+
Do not expect automatic relocalization in v1.1 runtime; use a new `/initialpose`
111+
when `reinitialization_requested` stays true.
112+
113+
## Symptom: Map Not Visible in RViz (#43, #48)
114+
115+
Two different map paths exist:
116+
117+
| Source | Topic / param | When it appears |
118+
| --- | --- | --- |
119+
| PCD/PLY file | `map_path` + `use_pcd_map:=true` | after node loads file; also publishes `/initial_map` |
120+
| External map server | `/map` subscription | when another node publishes the map |
121+
122+
RViz tips:
123+
124+
- set **Fixed Frame** to `map`
125+
- add **PointCloud2** display on `/initial_map` or `/map` depending on your launch
126+
- live localization cloud is separate from the static map topic
127+
128+
If the map displays but localization fails, the issue is usually initial pose or
129+
scan frame mismatch, not map publishing.
130+
131+
## Symptom: Pose Looks Offset From Reality (#44, #75)
132+
133+
Separate these cases:
134+
135+
1. **Consistent offset** (always wrong by ~same translation/rotation)
136+
- map build frame vs localization frame mismatch
137+
- initial pose yaw error
138+
- check map origin and `/initialpose` seed
139+
2. **Drift over time**
140+
- tuning / environment issue; see public benchmark limits in [benchmarking.md](benchmarking.md)
141+
3. **Jumps after rejects**
142+
- inspect `/alignment_status` reject streak before the jump
143+
144+
Collect before filing an issue:
145+
146+
- map format and `map_path`
147+
- `/initialpose` used
148+
- 3–5 `/alignment_status` samples around failure
149+
- `ros2 run tf2_ros tf2_echo map base_link`
150+
151+
## Sensor-Specific Notes
152+
153+
| Sensor / setup | Check |
154+
| --- | --- |
155+
| Livox MID-360 | [mid360_legged_jetson.md](mid360_legged_jetson.md), `check_mid360_legged_bringup.py` |
156+
| Ouster / Velodyne / RoboSense | set `cloud_topic` and lidar `frame_id` in launch YAML |
157+
| Nav2 full stack | need 3D map + 2D `map_yaml` + `odom -> base_link`; see README Nav2 section |
158+
159+
## Crash / TF Issues (Sprint 1 fixes)
160+
161+
| Symptom | Doc |
162+
| --- | --- |
163+
| RViz "No map frame" / 2D pose crash | [frame_contract.md](frame_contract.md) — TF now published on `/initialpose` |
164+
| `use_odom:=true` crash at startup | set `/initialpose` before odom integrates; use current `main` |
165+
| Strange odom TF tree | pick Mode A (`map->base_link`) or Mode B (`map->odom` + external odom) |
166+
167+
## Still Stuck?
168+
169+
1. run the public demo to verify your environment can reproduce a known-good path:
170+
171+
```bash
172+
source scripts/setup_local_env.sh
173+
scripts/run_public_demo.sh
174+
```
175+
176+
2. open a GitHub issue with:
177+
- ROS distro
178+
- launch command
179+
- `/alignment_status` message and key values
180+
- whether `map_received` and `initialpose_received` are `true`

0 commit comments

Comments
 (0)