Python example: mrpt_slam_example.py
ICP scan alignment and incremental ICP-SLAM with mrpt.slam.
Modules: mrpt.slam, mrpt.maps, mrpt.obs, mrpt.poses
1#!/usr/bin/env python3
2"""
3ICP scan alignment and incremental ICP-SLAM with mrpt.slam.
4
5Demonstrates:
6 - CICP: align two point-cloud maps, inspect TICPReturnInfo
7 - CMetricMapBuilderICP: incremental ICP-SLAM from a sequence of laser scans
8"""
9
10import math, numpy as np
11from mrpt.slam import (
12 CICP, CICPOptions, TICPReturnInfo,
13 CMetricMapBuilderICP, CMetricMapBuilderICPOptions,
14 TICPAlgorithm, TICPCovarianceMethod,
15)
16from mrpt.maps import CSimplePointsMap
17from mrpt.obs import CObservation2DRangeScan, CActionCollection, CSensoryFrame
18from mrpt.poses import CPosePDFGaussian, CPose2D
19
20# ---------------------------------------------------------------------------
21# Build two synthetic point clouds offset by 1 m along X
22# ---------------------------------------------------------------------------
23def make_cloud(cx, cy, n_points=50, radius=3.0):
24 pts = CSimplePointsMap()
25 for i in range(n_points):
26 angle = 2 * math.pi * i / n_points
27 pts.insertPoint(cx + radius * math.cos(angle),
28 cy + radius * math.sin(angle), 0.0)
29 return pts
30
31cloud1 = make_cloud(0.0, 0.0)
32cloud2 = make_cloud(1.0, 0.0) # shifted 1 m in X
33
34# ---------------------------------------------------------------------------
35# CICP — align cloud2 onto cloud1
36# ---------------------------------------------------------------------------
37opts = CICPOptions()
38opts.maxIterations = 40
39opts.thresholdDist = 0.5
40opts.thresholdAng = math.radians(5)
41
42icp = CICP(opts)
43
44init_est = CPosePDFGaussian() # identity initial guess
45
46result_pdf, info = icp.AlignPDF(cloud1, cloud2, init_est)
47print(f"ICP result:")
48print(f" nIterations = {info.nIterations}")
49print(f" goodness = {info.goodness:.4f}")
50print(f" quality = {info.quality:.4f}")
51print(f" {info}")
52
53# ---------------------------------------------------------------------------
54# CMetricMapBuilderICP — simple ICP-SLAM pipeline
55# ---------------------------------------------------------------------------
56def make_scan(radius=5.0, n_rays=180, noise=0.01):
57 """Generate a synthetic 180° laser scan (arc at `radius` metres)."""
58 scan = CObservation2DRangeScan()
59 scan.aperture = math.pi
60 scan.maxRange = 20.0
61 scan.rightToLeft = True
62 scan.resizeScan(n_rays)
63 rng = np.random.default_rng(0)
64 for i in range(n_rays):
65 r = radius + rng.normal(0, noise)
66 scan.setScanRange(i, float(r))
67 scan.setScanRangeValidity(i, True)
68 return scan
69
70builder = CMetricMapBuilderICP()
71builder.ICP_options.insertionLinDistance = 0.3
72builder.ICP_options.insertionAngDistance = math.radians(10)
73builder.useSimplePointsMap() # configure mapInitializers with a CSimplePointsMap (must be before initialize())
74builder.initialize()
75
76# Feed 5 scans; each call to processObservation builds the map
77for step in range(5):
78 scan = make_scan(radius=4.0 + step * 0.1)
79 scan.sensorLabel = "LASER"
80 builder.processObservation(scan)
81
82print(f"\nCMetricMapBuilderICP after 5 scans:")
83print(f" map size = {builder.getCurrentlyBuiltMapSize()} keyframes")
84pose_pdf = builder.getCurrentPoseEstimation()
85print(f" current pose PDF: {pose_pdf}")
86xs, ys = builder.getCurrentMapPoints()
87print(f" point-map size: {len(xs)} points")