README

Manual Loop Closure Tools

English | 中文

Xiangcheng Hu, Jin Wu, Xieyuanli Chen

Manual Loop Closure Tools

CIYouTubeBilibiliPython 3.10GPLv3PRs-WelcomeGitHub StarsFORKGitHub Issues

Introduction

A single wrong loop closure folds a LiDAR map, and a missed loop leaves its drift uncorrected; both are usually found only after mapping, when the online system is no longer running. Manual Loop Closure Tools opens an archived pose graph with its keyframe scans, audits every loop, and lets the operator add, replace or disable loops before the graph is optimized and exported again, without ROS.

Key insight: a loop is judged by the map it produces, not by its noise model. Two visits of the same surface agree in their surface normals, whereas a map folded by a wrong loop overlays different structures. The agreement of cross-visit overlaps therefore scores each loop without a reference trajectory.

  • Loop audit. Every active loop is checked against a drift bound and scored by map consistency and repeated registration, with a keep, check, remove or redundant suggestion.
  • ROS-free optimization and export. A Python GTSAM backend re-solves the edited graph and exports g2o, TUM, the global map and the trajectory, with resumable edit projects.
edge audit

News

  • 2026/11/01: Loop Audit tab and two fixes of the edge weights read from and written to g2o files (see Changelog). Sessions optimized with earlier versions should be optimized again.
  • 2026/04/18: First standalone release of the GUI and the Python optimizer.

Video

Tutorial video

Click the image to play the tutorial video (Bilibili).

Method

workflow

Loop audit, stage 1. A loop is implausible when it corrects more drift than a LiDAR-inertial front end accumulates over the path length $L_{ij}$ between its keyframes:

$$ \lVert \Delta\mathbf{t}{ij} \rVert > 0.5,\mathrm{m} + 0.05,L{ij} \qquad \text{or} \qquad \lVert \Delta\boldsymbol{\theta}{ij} \rVert > 2^{\circ} + 0.1^{\circ}\mathrm{/m} \cdot L{ij} $$

On the sample session the drift bound flags the loops 706→68 and 713→62 (source→target), which would correct 17.1° and 20.0° of rotation drift. Each remaining loop is then added alone to the graph without loops: a loop that lowers the normal agreement ratio (NAR) of the stitched map is suggested for removal, a loop that raises it is kept, and a loop without measurable effect is marked redundant. The NAR is the fraction of cross-visit overlaps $\mathcal{O}$ whose normals agree within 25°:

$$ \mathrm{NAR} = \frac{1}{|\mathcal{O}|} \sum_{(p,q) \in \mathcal{O}} \left[, \angle(\mathbf{n}_p, \mathbf{n}_q) < 25^{\circ} ,\right] $$

Loop audit, stage 2. A greedy leave-one-out removes the loops that pass alone but conflict with the others, and a coarse-to-fine re-registration (1.0, 0.3, 0.1 m) marks measurements that do not reproduce. The suggested removals are disabled in one undoable step.

loaded

Loop editing. The operator picks two keyframes or an existing loop, aligns the source scan in the point-cloud view, and accepts the GICP result as a new or replacing loop. The default loop noise is 5 cm and 0.3°.

Optimization and export. Fast ISAM2 updates the working graph after each edit and Accurate LM gives the batch reference solve. Export rebuilds the global map only once, for the selected run.

Getting Started

Tested on Ubuntu 20.04 with Python 3.10, Open3D 0.19.0, PyQt5 5.15.10 and GTSAM 4.3; ROS and the legacy C++ optimizer are optional.

git clone https://github.com/JokerJohn/Mannual-Loop-Closure-Tools.git
cd Mannual-Loop-Closure-Tools
make venv && source .venv/bin/activate
make gtsam-python                     # Python GTSAM 4.3 wrapper
python launch_gui.py --session-root /path/to/mapping_session

A session contains pose_graph.g2o, optimized_poses_tum.txt and key_point_frame/*.pcd; --g2o /path/to/pose_graph.g2o opens a graph directly. A sample session is available for a first test. Docker and conda paths are described in docs/DOCKER.md and docs/INSTALL.md.

make docker-build && xhost +local:docker
docker run --rm -it --net=host -e DISPLAY=$DISPLAY -e QT_X11_NO_MITSHM=1 \
  -v /tmp/.X11-unix:/tmp/.X11-unix:rw -v /path/to/mapping_session:/data/session \
  manual-loop-closure-tools:latest python launch_gui.py --session-root /data/session

The audit and the optimizer also run headless:

cd gui && python -m manual_loop_closure.loop_audit --session-root /path/to/mapping_session --output audit.json
python gui/manual_loop_closure/python_optimizer/cli.py --help
make test                             # unit tests, including the g2o information order
Output files
Location Purpose Main files
manual_loop_projects/<project_id>/ Edit project for resume and review project_state.json, execution.log, operations.jsonl, loop_audit_*.json
manual_loop_runs/<run_id>/ One optimization run pose_graph.g2o, optimized_poses_tum.txt, manual_loop_constraints.csv, manual_loop_report.json
manual_loop_exports/<export_id>/ Final export pointer export_manifest.json, selected_run.txt, run symlink

Load Session resumes the latest project of a session root, and Resume Project restores a selected project_state.json.

Python and C++ backend parity
Session Constraints Python [s] C++ [s] TUM max t err [m] TUM max r err [rad] Map points Py / C++
office_fs_fastlio_saved 1 10.193 2.096 5.20e-09 3.11e-06 4,850,749 / 4,850,749
floor34-1_fs_fastlio_saved 1 16.335 3.827 1.00e-09 3.56e-06 13,771,605 / 13,771,605
dr_tunnel_2026_01_24_145439 0 12.112 2.909 2.49e-08 2.87e-09 2,139,789 / 2,139,789

The Python backend is the default; the C++ backend in backend/catkin_ws is kept as a parity reference and is built with make backend.

Further documentation: Tool Manual, Python GTSAM 4.3, Wiki, Contributing.

TODO

  • Standalone GUI, Python optimizer, Docker image
  • Loop audit with keep, check, remove and redundant suggestions
  • Wiki page for the loop audit

Citation

This tool is derived from the MS-Mapping codebase. If it helps your work, please cite:

@article{hu2024msmapping,
  title         = {MS-Mapping: An Uncertainty-Aware Large-Scale Multi-Session LiDAR Mapping System},
  author        = {Hu, Xiangcheng and Wu, Jin and Jiao, Jianhao and Jiang, Binqian and Zhang, Wei and Wang, Wenshuo and Tan, Ping},
  journal       = {arXiv preprint arXiv:2408.03723},
  eprint        = {2408.03723},
  archivePrefix = {arXiv},
  primaryClass  = {cs.RO},
  year          = {2024}
}

Related work: MS-Mapping and PALoc.

Acknowledgment

The tool is built on GTSAM, Open3D and PyQt5. The sample session was recorded with FAST-LIO as the front end.

License

Released under the GNU General Public License v3.0.

Contributors

评论

暂无评论