TinyRobotics
Loading...
Searching...
No Matches
LocalizationAndMapping2D.h
1#pragma once
2#include "TinyRobotics/coordinates/Coordinate.h"
3#include "TinyRobotics/imu/IMU2D.h"
4#include "TinyRobotics/maps/GridBitMap.h"
5#include "TinyRobotics/planning/IFrontierExplorer.h"
6#include "TinyRobotics/sensors/RangeSensor.h"
7
8namespace tinyrobotics {
9/**
10 * @class LocalizationAndMapping2D
11 * @ingroup localization
12 * @brief Modular 2D Simultaneous Localization and Mapping (SLAM) system with
13 * flexible map, IMU, and exploration integration.
14 *
15 * This class implements a highly modular and extensible 2D SLAM system for
16 * occupancy grid mapping, pose estimation, sensor fusion, and autonomous
17 * exploration.
18 *
19 * ## Features
20 * - **Template Parameters:**
21 * - `T`: Scalar type for coordinates and math (default: DistanceM)
22 * IFrontierExplorer<T><T>)
23 * - **Occupancy Grid Mapping:** Maintains a 2D grid map for the environment,
24 * supporting any compatible map type via IMap<T>.
25 * - **Pose Estimation:** Integrates IMU/odometry for robot pose tracking and
26 * frame transforms (IMotionState2D).
27 * - **Sensor Fusion:** Supports range sensors (e.g., Lidar) for obstacle
28 * detection and map updates.
29 * - **Frontier-Based Exploration:** Integrates a flexible frontier explorer for
30 * autonomous navigation.
31 * - **Frame Management:** Uses Frame2D and FrameMgr2D for coordinate transforms
32 * between world, base, and sensor frames.
33 * - **Flexible Integration:** All major components (map, IMU, explorer) are
34 * passed by reference for easy swapping and testing.
35 * - **Message Bus:** Inherits from MessageSource for integration with
36 * message-based architectures.
37 *
38 * ## Template Parameters
39 * @tparam T Scalar type for coordinates and math (default:
40 * DistanceM)
41 * @tparam IFrontierExplorer<T> The frontier exploration strategy (default:
42 * IFrontierExplorer<T><T>)
43 *
44 * ## Ownership & Lifetime
45 * - All major components (map, IMU, explorer) are passed by reference and must
46 * outlive the LocalizationAndMapping2D instance.
47 *
48 * ## Usage Example
49 * @code
50 * GridBitMap map;
51 * IMU2D<float> imu;
52 * Frame2D world, base, lidar;
53 * FrontierExplorer<T> explorer(map);
54 * LocalizationAndMapping2D slam(map, explorer, world, base, lidar, imu);
55 * slam.begin();
56 * // ...
57 * @endcode
58 */
59
60template <typename T>
62 public:
63 LocalizationAndMapping2D(IMap<T>& map, IFrontierExplorer<T>& explorer,
64 Frame2D& world, Frame2D& base, Frame2D& lidar,
65 IMotionState2D& imu)
66 : explorer_(explorer),
67 map_(map),
68 world_(world),
69 base_(base),
70 lidar_(lidar),
71 imu2d_(imu) {
72 callbackHandler_.setValueCallback(onIMUPublished, this);
73 imu2d_.subscribe(callbackHandler_);
74 }
75
76 /// Initialize SLAM system (IMU and range sensor)
77 bool begin() {
78 // update IMU
79 Transform2D& transform = base_.getTransform();
80 // Initialize IMU with the initial pose of the base frame
81 bool rcIMU = imu2d_.begin(transform);
82 bool rcSensor = rangeSensor_.begin();
83 return rcIMU && rcSensor;
84 }
85
86 void end() {
87 imu2d_.end();
88 rangeSensor_.end();
89 }
90
91 /// Register Range Measurement from Lidar
92 void addRangeMeasurement(Distance dist, Angle angle, CellState state) {
93 addRangeMeasurement(dist.getValue(DistanceUnit::M),
94 angle.getValue(AngleUnit::DEG), state);
95 }
96
97 /// Register Range Measurement from Lidar
98 void addRangeMeasurement(float distanceM, float angleDeg, CellState state) {
99 rangeSensor_.setTransform(
100 tf_.getTransform(lidar_, world_)); // Ensure transform is set
101 rangeSensor_.setObstacle(distanceM, angleDeg);
102 Coordinate<T> obs;
103 if (rangeSensor_.getObstacleCoordinate(obs)) {
104 // Transform obstacle coordinate from lidar to world frame
105 Transform2D tf_lidar2world = tf_.getTransform(lidar_, world_);
106 Coordinate<float> obs_world = tf_lidar2world.apply(obs);
107 // Get sensor (lidar) position in world frame
108 Coordinate<float> sensor_world =
109 tf_lidar2world.apply(Coordinate<float>(0, 0));
110 // Mark free cells between sensor and obstacle, and mark obstacle as
111 // OCCUPIED
112 markRay(sensor_world, obs_world, state);
113 }
114 }
115
116 /**
117 * @brief Find the next frontier cell (boundary between known free and
118 * unknown space).
119 * @return World coordinates of the next frontier cell, or (0,0) if none
120 * found.
121 */
122 bool getNextFrontier(Coordinate<float>& result) const {
123 return explorer_.getNextFrontier(result);
124 }
125
126 /// Subscribe to imu and range sensor messages
127 void subscribe(MessageHandler& handler) {
128 imu2d_.subscribe(handler);
129 rangeSensor_.subscribe(handler);
130 }
131
132 void unsubscribeAll() {
133 imu2d_.unsubscribeAll();
134 rangeSensor_.unsubscribeAll();
135 }
136
137 /// Access to map
138 IMap<T>& getMap() { return map_; }
139
140 /// Access to IMU state
141 IMotionState2D& getIMU() { return imu2d_; }
142
143 IFrontierExplorer<T>& getFrontierExplorer() { return explorer_; }
144
145 protected:
146 IFrontierExplorer<T>& explorer_;
147 RangeSensor<T> rangeSensor_;
148 IMotionState2D& imu2d_;
149 IMap<T>& map_;
150 CallbackMessageHandler callbackHandler_;
151 // Frame management
152 Frame2D& world_;
153 Frame2D& base_;
154 Frame2D lidar_;
155 FrameMgr2D tf_;
156
157 /**
158 * @brief Mark all cells between the sensor and the obstacle as FREE, and
159 * the obstacle cell as OCCUPIED.
160 * @param sensor_world World coordinates of the sensor
161 * @param obs_world World coordinates of the obstacle
162 */
163 void markRay(const Coordinate<float>& sensor,
164 const Coordinate<float>& obstacle, CellState state) {
165 auto points = sensor.interpolateTo(obstacle, map_.getResolution());
166 for (auto& loc : points) {
167 if (map_.isValid(loc)) map_.setCell(loc, CellState::FREE);
168 }
169 if (map_.isValid(obstacle)) {
170 map_.setCell(obstacle, state);
171 }
172 }
173
174 static bool onIMUPublished(const Message<float>& msg, void* ref) {
175 // Update the base position
177 self.base_.setTransform(self.imu2d_.getTransform());
178 return true;
179 }
180};
181
182} // namespace tinyrobotics
Represents an angle with unit conversion and wrap-around support.
Definition: Angle.h:42
A generic 3D coordinate class for robotics, navigation, and spatial calculations.
Definition: Coordinate.h:57
Represents a distance measurement with unit conversion support.
Definition: Distance.h:40
Manages a hierarchy of 2D coordinate frames and enables SE(2) transforms and GPS conversion.
Definition: FrameMgr2D.h:196
Interface for frontier-based exploration utilities.
Definition: IFrontierExplorer.h:18
Abstract interface for 2D grid maps and occupancy maps in TinyRobotics.
Definition: IMap.h:79
Interface for representing the navigation state of a robot in 2D space.
Definition: MotionState2D.h:35
Modular 2D Simultaneous Localization and Mapping (SLAM) system with flexible map, IMU,...
Definition: LocalizationAndMapping2D.h:61
IMotionState2D & getIMU()
Access to IMU state.
Definition: LocalizationAndMapping2D.h:141
IMap< T > & getMap()
Access to map.
Definition: LocalizationAndMapping2D.h:138
void addRangeMeasurement(float distanceM, float angleDeg, CellState state)
Register Range Measurement from Lidar.
Definition: LocalizationAndMapping2D.h:98
void addRangeMeasurement(Distance dist, Angle angle, CellState state)
Register Range Measurement from Lidar.
Definition: LocalizationAndMapping2D.h:92
void markRay(const Coordinate< float > &sensor, const Coordinate< float > &obstacle, CellState state)
Mark all cells between the sensor and the obstacle as FREE, and the obstacle cell as OCCUPIED.
Definition: LocalizationAndMapping2D.h:163
bool begin()
Initialize SLAM system (IMU and range sensor)
Definition: LocalizationAndMapping2D.h:77
void subscribe(MessageHandler &handler)
Subscribe to imu and range sensor messages.
Definition: LocalizationAndMapping2D.h:127
bool getNextFrontier(Coordinate< float > &result) const
Find the next frontier cell (boundary between known free and unknown space).
Definition: LocalizationAndMapping2D.h:122
Interface for handling messages in the TinyRobotics framework.
Definition: MessageHandler.h:18
Base class for message sources in the TinyRobotics communication framework.
Definition: MessageSource.h:35
void unsubscribeAll()
Remove all registered message handlers.
Definition: MessageSource.h:69
void subscribe(MessageHandler &handler, MessageOrigin origin=MessageOrigin::Undefined, MessageContent content=MessageContent::Undefined)
Subscribe a message handler to this source, with optional filtering.
Definition: MessageSource.h:59
Generic range sensor abstraction for LIDAR, ultrasonic, or similar sensors.
Definition: RangeSensor.h:69
Represents a 2D rigid body transform (SE(2)): translation and rotation.
Definition: FrameMgr2D.h:42
DistanceUnit
Supported distance units for conversion and representation.
Definition: Distance.h:10
AngleUnit
Supported angle units for conversion and representation.
Definition: Angle.h:11
CellState
Cell state for occupancy grid mapping (e.g., UNKNOWN, FREE, OCCUPIED).
Definition: Common.h:32
Represents a 2D coordinate frame in a hierarchical frame tree.
Definition: FrameMgr2D.h:130
Generic message structure for communication, parameterized by value type.
Definition: Message.h:72