A maze-solving robot built on ROS 2 (Humble) using the linorobot2 platform. The robot uses frontier-based exploration with SLAM and Nav2 to map and navigate through unknown environments.
| Name | Institute |
|---|---|
| Budharaju Pavani Siva Priya | Amrita Vishwa Vidyapeetham |
| Prajan S | Amrita Vishwa Vidyapeetham |
| Viswajit Arunkumar | Amrita Vishwa Vidyapeetham |
| Yashwanth Ram Mohan C | Amrita Vishwa Vidyapeetham |
Mentor: Jayasree P.R.
📄 Click Here to View Certificate
Click Here for the Maze Solver Robot Preparation
Click Here for the Maze Solver Robot Exploration of a Room with teleop_twist_keyboard in ROS 2
Detailed view of the Raspberry Pi 4 with gold dual-fan heatsink and the LD19 LiDAR sensor (blue LEDs active) mounted on top
The robot navigating inside the physical maze — white foam board walls, blue LED glow visible from the robot chassis
Real-time occupancy grid map being built by SLAM Toolbox during exploration, visualized in RViz2. The map shows explored free space (white), walls (dark), and the robot's current LiDAR scan (colored rays). Terminal logs show SLAM Toolbox, EKF, and Nav2 running simultaneously.
├── linorobot2_hardware/ # ESP32 firmware (micro-ROS, motors, encoders, IMU)
├── linorobot2_ws/ # ROS 2 visualization workspace (RViz2 launch files)
├── ldlidar_ros2_ws/ # LiDAR driver workspace (LD series LiDAR sensors)
└── maze_robot_ws/ # Core maze-solving logic (SLAM + Nav2 + Frontier Exploration)
The chassis is fabricated from laser-cut acrylic sheets in a two-plate structure (base plate + top plate), connected by four 30 mm spacers for structural rigidity.
| Spec | Value |
|---|---|
| Drive Type | Differential Drive (2WD) |
| Wheel Diameter | 44 mm |
| Wheel Base | 15 cm |
| Robot Footprint | 15 × 15 cm |
| Robot Height | ~8.3 cm |
| Support | Ball caster wheel |
Sensor Placement:
- 2D LiDAR mounted at the center for symmetrical 360° scanning
- IMU positioned at the center of the bottom plate to minimize motion-induced measurement errors
| Layer | Component | Role |
|---|---|---|
| High-level | Raspberry Pi 4 Model B | ROS 2 nodes, SLAM, Nav2, path planning |
| Low-level | ESP32 | Motor PWM, encoder reading, micro-ROS client |
| Storage | 128 GB SD Card | OS, ROS packages, project data |
| Display | OLED Screen | Runtime status — boot, mapping, obstacle avoidance |
| Component | Model | Purpose |
|---|---|---|
| LiDAR | LD Lidar STL19P (LD19) | 360° environmental scanning, obstacle detection |
| IMU | MPU6050 | Orientation, acceleration, angular velocity |
| Motors | N20 DC Geared + Encoder | Drive + odometry feedback |
| Motor Driver | TB6612FNG | Dual-channel H-bridge for motor power |
| Component | Spec |
|---|---|
| Battery | 11.1V Lithium-Ion (3S, 3380mAh 60C) |
| Distribution | XT60 Power Distribution Board |
| Connectors | XT60 (battery), Type-C (Pi), Micro-USB (ESP32) |
| Safety | Rocket switch (master power cutoff) |
┌─────────────────────────────────────────────────────────────┐
│ Robot System │
│ │
│ ESP32 Firmware (micro-ROS) │
│ ├── Motor Control (PWM + PID) │
│ ├── Encoder Feedback (Odometry) │
│ └── IMU (MPU6050) → Orientation │
│ ↓ │
│ LiDAR (LD19) → /scan topic │
│ ↓ │
│ SLAM Toolbox → /map (OccupancyGrid) │
│ ↓ │
│ Frontier Server → Detects unknown regions │
│ ↓ │
│ Frontier Explorer → Selects & navigates to frontiers │
│ ↓ │
│ Nav2 (navigate_to_pose) → Path planning & execution │
└─────────────────────────────────────────────────────────────┘
The firmware runs on an ESP32 microcontroller using PlatformIO and communicates with ROS 2 via micro-ROS over Serial.
| Parameter | Value |
|---|---|
| Robot Type | Differential Drive (2WD) |
| Motor Driver | Generic 2-IN driver |
| IMU | MPU6050 (I2C) |
| Wheel Diameter | 44 mm |
| Wheel Base (LR distance) | 15 cm |
| Max Motor RPM | 330 |
| Encoder CPR | 1400 counts/rev |
| Transport | micro-ROS over Serial |
| ROS Distro | Humble |
| Function | Pin |
|---|---|
| Left Motor PWM | 14 |
| Left Motor IN_A | 23 |
| Left Motor IN_B | 13 |
| Right Motor PWM | 33 |
| Right Motor IN_A | 32 |
| Right Motor IN_B | 25 |
| Left Encoder A/B | 26 / 27 |
| Right Encoder A/B | 19 / 18 |
| IMU SDA / SCL | 21 / 22 |
| IMU I2C Address | 0x68 |
| micro-ROS Agent Port | 8888 |
| Constant | Value |
|---|---|
| Kp | 0.6 |
| Ki | 0.8 |
| Kd | 0.5 |
micro_ros_platformio— ROS 2 communication over SerialI2Cdevlib-MPU6050— IMU sensor driverESP32Encoder— Hardware encoder readingESP32Servo— PWM servo/motor control
Provides ROS 2 drivers for the LDRobot LD-series LiDAR sensors. Publishes laser scan data to the /scan topic consumed by SLAM Toolbox.
- LD06
- LD14 / LD14P
- STL19P (used in this project)
| Topic | Type | Description |
|---|---|---|
/scan |
sensor_msgs/LaserScan |
Laser scan data output |
Provides RViz2 launch files for visualizing the robot model, SLAM map, and navigation.
| Launch File | Purpose |
|---|---|
robot_model.launch.py |
Displays URDF robot model in RViz2 |
slam.launch.py |
Opens RViz2 configured for SLAM visualization |
navigation.launch.py |
Opens RViz2 configured for Nav2 navigation |
This is the heart of the project. The slam_robot ROS 2 package implements autonomous frontier-based exploration to map and solve mazes.
slam_robot/
├── slam_robot/
│ ├── frontier_detection.py # BFS-based frontier detection algorithm
│ ├── frontier_explorer.py # Autonomous navigation to frontiers
│ ├── frontier_server.py # Publishes detected frontiers as ROS topics
│ └── frontier_utils.py # Grid/world coordinate utilities
├── config/
│ ├── slam_toolbox_params.yaml # SLAM Toolbox configuration
│ └── nav2_params.yaml # Nav2 stack configuration
└── launch/
└── bringup.launch.py # Full system launch file
A frontier is the boundary between explored free space and unexplored (unknown) space on the map. By navigating to these frontiers, the robot systematically uncovers the entire environment — effectively "solving" the maze by mapping it completely.
The detect_frontiers() function runs a BFS (Breadth-First Search) starting from the robot's current grid position:
- The BFS walks through all free cells (value
0–49in the occupancy grid). - For each free cell, it checks its 4-connected neighbors.
- If a neighbor is unknown (
-1) and has at least one free neighbor, it qualifies as a frontier cell. - Connected frontier cells are grouped into frontier regions using an 8-connected BFS (
build_new_frontier()). - Each frontier region is represented by its size (number of cells) and centroid (world coordinates).
- Only frontiers with
size >= 8cells are kept to filter noise.
Map Legend:
0 = Free (explored)
-1 = Unknown (unexplored)
100 = Occupied (wall)
F = Frontier cell (boundary between free and unknown)
[100][100][100][100][100]
[ 0][ 0][ F][ -1][ -1]
[ 0][ 0][ F][ -1][ -1]
[ 0][ 0][ F][ -1][ -1]
[100][100][100][100][100]
The FrontierServerNode:
- Subscribes to
/map(OccupancyGrid from SLAM Toolbox) - Gets robot position via TF (
map → base_footprint) - Calls
detect_frontiers()every 1 second or on demand - Publishes results to
/frontiersas aMarkerArray(each marker = one frontier)
The FrontierExplorerNode:
-
Triggers frontier detection by publishing to
/get_frontiersat 1 Hz. -
Receives the latest frontiers from
/frontiers. -
Selects the best frontier using the cost function:
cost = distance_to_frontier / frontier_sizeThis balances proximity (prefer nearby frontiers) with informativeness (prefer larger unexplored regions).
-
Sends a
NavigateToPoseaction goal to Nav2, which handles path planning and obstacle avoidance. -
Terminates exploration after 30 consecutive seconds with no frontiers found — indicating the maze is fully mapped.
| Factor | Effect |
|---|---|
| Smaller distance | Lower cost → preferred |
| Larger frontier size | Lower cost → preferred |
| Both combined | Efficient, greedy exploration |
SLAM Toolbox runs in online async mapping mode.
| Parameter | Value |
|---|---|
| Solver | Ceres (SPARSE_NORMAL_CHOLESKY) |
| Map Resolution | 0.05 m/cell |
| Max Laser Range | 20 m |
| Min Travel Distance | 0.5 m |
| Loop Closure | Enabled |
| Scan Topic | /scan |
| Base Frame | base_footprint |
| Map Frame | map |
| Odom Frame | odom |
Nav2 provides path planning and execution for navigating to selected frontiers.
| Component | Configuration |
|---|---|
| Localization | AMCL (Adaptive Monte Carlo) |
| Motion Model | Differential Drive |
| Global Planner | Default (Navfn) |
| Local Planner | Default (DWB) |
| Max Particles | 2000 |
| Min Particles | 500 |
| Robot Base Frame | base_footprint |
| Scan Topic | /scan |
- ROS 2 Humble
- PlatformIO (for firmware)
- micro-ROS agent
cd linorobot2_hardware/firmware
pio run -e esp32 --target uploadros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0 -b 115200cd ldlidar_ros2_ws
source install/setup.bash
ros2 launch ldlidar_ros2 ld19.launch.pycd maze_robot_ws
source install/setup.bash
ros2 launch slam_robot bringup.launch.pycd linorobot2_ws
source install/setup.bash
ros2 launch linorobot2_viz slam.launch.py| Topic | Type | Publisher | Subscriber |
|---|---|---|---|
/scan |
sensor_msgs/LaserScan |
LiDAR driver | SLAM Toolbox |
/map |
nav_msgs/OccupancyGrid |
SLAM Toolbox | Frontier Server |
/odom |
nav_msgs/Odometry |
micro-ROS (ESP32) | Nav2, SLAM |
/cmd_vel |
geometry_msgs/Twist |
Nav2 | micro-ROS (ESP32) |
/get_frontiers |
std_msgs/Empty |
Frontier Explorer | Frontier Server |
/frontiers |
visualization_msgs/MarkerArray |
Frontier Server | Frontier Explorer |
/frontier_markers |
visualization_msgs/MarkerArray |
Frontier Explorer | RViz2 |
/navigate_to_pose |
Action | Frontier Explorer | Nav2 |
| Category | Technology |
|---|---|
| Robotics Framework | ROS 2 Humble |
| Microcontroller | ESP32 |
| Main Computer | Raspberry Pi 4 Model B |
| Firmware Framework | PlatformIO + Arduino (LinoRobot2) |
| MCU–ROS Bridge | micro-ROS (Serial / USB) |
| SLAM | SLAM Toolbox (online async) |
| Navigation | Nav2 |
| Exploration | Custom BFS + A* Frontier Detection |
| LiDAR | LDRobot LD19 (STL19P) |
| IMU | MPU6050 |
| Motor Driver | TB6612FNG Dual H-Bridge |
| Visualization | RViz2, Gazebo |
| CAD | AutoCAD |
| Chassis | Laser-cut acrylic (2-plate structure) |
| Language (firmware) | C++ |
| Language (ROS nodes) | Python 3 |
- Motor 1 (Left) encoder direction is inverted (
MOTOR1_ENCODER_INV = true) and motor direction is also inverted (MOTOR1_INV = true) — tuned for the physical wiring of the robot. - The ESP32 pin
MOTOR1_IN_Awas moved from GPIO 12 to GPIO 23 to avoid boot-mode conflicts on the ESP32. - The exploration loop terminates after 30 consecutive seconds of no frontier detections, signaling complete maze coverage.
- The bringup launch file is configured for simulation (
use_sim_time: true) and can be switched to real hardware by setting it tofalse.
Built with ROS 2 Humble · linorobot2 · SLAM Toolbox · Nav2 · micro-ROS




