Sep 2024 – Apr 2025 · ROS 2 · Jetson Orin NX · Team lead

Frontier-Based Autonomous Exploration Vehicle

Led a team to develop an autonomous exploration system using ROS2 and LiDAR. Implemented SLAM algorithms including Cartographer and Navigation2 for real-time mapping and path planning. Integrated YOLOv11 for object detection and deployed the complete system on embedded hardware.

Demo

Overview

Search and rescue after a disaster is a race against time, and debris, heat or radiation often make the site too dangerous for people to enter first. This project builds a small tracked robot that goes in ahead of them: it explores an unknown indoor space on its own, builds the map as it drives, and marks every person it finds on that map, so rescuers know where to go before they set foot inside.

Everything runs on board in ROS 2: Cartographer for SLAM, Navigation2 for planning and control, YOLOv11 with a depth camera for finding casualties, and my frontier-exploration node that decides where to go next.

1. System architecture & task modes

SOFTWARE LAYER · ROS 2 HUMBLE ON THE JETSON Cartographer 2D LiDAR SLAM mapping · localization Navigation2 global + local planning costmaps · control Casualty localization YOLOv11-seg + depth → map coordinates Frontier exploration information gain vs. travel cost Sensors RPLIDAR C1 · 2D LiDAR Astra Pro Plus · RGB-D Jetson Orin NX 16GB Ubuntu 22.04 ROS 2 Humble STM32F407VET6 Cortex-M4 · 168 MHz motor control IMU & drive MPU6050 IMU MG513 motors · tracks USB UART IIC COMMUNICATION ROS 2 topics /scan · /map · /odometry/filtered · /cmd_vel · casualty positions HOST COMPUTER SLAVE COMPUTER
System architecture. The Jetson (host) runs the whole ROS 2 stack and talks to the sensors over USB; the STM32 (slave) drives the motors and reads the IMU over IIC, and the two boards talk over UART.

The robot works in two modes. In exploration mode it maps an unknown building and records casualties as it goes; in rescue mode it reloads that map and drives to each recorded casualty in turn.

MODE 1 · AUTONOMOUS EXPLORATION Initialize hardware & links Build the map · Cartographer Pick the optimal frontier Plan & drive · Nav2 Frontier reached? Exploration finished? Save map + casualty list yes yes no no runs throughout casualty detection & 3D localization YOLOv11 + depth MODE 2 · RESCUE ON THE SAVED MAP Initialize hardware & links Localize · Cartographer Load saved map · AMCL Next casualty → Nav2 goal Plan & drive · Nav2 Casualty reached? All casualties reached? Rescue mission complete yes yes no no
Task flow of the two modes. Exploration repeats map → frontier → navigate until no useful frontier is left, while casualty detection runs alongside; rescue localizes on the saved map with AMCL and visits the stored casualty positions one by one.

2. Hardware integration

The tracked robot platform with a LiDAR on top and a depth camera at the front
The platform: a tracked chassis carrying the Jetson, a 2D LiDAR on top and an RGB-D camera at the front.
Component Role Key specification
Jetson Orin NX 16GB Onboard computer, runs all of ROS 2 Ubuntu 22.04 · ROS 2 Humble
RPLIDAR C1 2D LiDAR for SLAM and costmaps ToF · 0.05–12 m range · 8–12 Hz scan
Orbbec Astra Pro Plus RGB-D camera for casualty detection Depth 0.6–8 m · up to 640×480 @ 30 fps · RGB FOV 66.01° × 40.2°
STM32F407VET6 + MPU6050 Motor control and IMU Cortex-M4 · 168 MHz
MG513 DC motors Tracked drive Up to 1.1 m/s · 3 kg payload

3. Casualty detection & 3D localization

A bounding box is not enough to place a person on a map: its corners fall on the floor and the wall behind, so the depth inside it is mostly background. The detector therefore uses YOLOv11 segmentation, and only depth pixels inside the person’s mask are used.

PER CAMERA FRAME ① Segment YOLOv11-seg on RGB → person mask ② Range mask on depth image median filter → d_tar ③ Bearing mask centre x_cen → θ_off via FOV ④ Camera frame x_c = d_tar sin θ_off y_c = d_tar cos θ_off ⑤ Map frame + robot pose → casualty list IMAGE PLANE d_tar x_cen W_img TOP VIEW · CAMERA FRAME y (forward) x camera FOV d_tar θ_off x_c y_c robot casualty
Casualty localization. The segmentation mask selects the person's pixels in the aligned depth image; a median filter removes noise and outliers, and the mean of what remains is the range d_tar. The horizontal position of the mask centre gives the bearing θ_off, and the robot pose from odometry carries the point from the camera frame into the map.

With \(x_{cen}\) the horizontal centre of the mask, \(W_{img}\) the image width and \(\theta_{FOV}\) the camera’s horizontal field of view:

\[\theta_{off} = \frac{2x_{cen} - W_{img}}{2W_{img}} \cdot \theta_{FOV}\] \[x_c = d_{tar}\sin\theta_{off}, \qquad y_c = d_{tar}\cos\theta_{off}\]

Each position is saved with the map, and these are the goals the rescue mode later drives to.

4. SLAM odometry bridge

Cartographer provides the robot pose as TF transforms, while the navigation and localization nodes expect a nav_msgs/Odometry stream with velocities. A small bridge node converts one into the other:

  1. Look up map → odom and odom → base_link and compose the robot pose, \(T_{mb} = T_{mo} \cdot T_{ob}\), giving position \(p = [x, y, z]^T\) and orientation quaternion \(q\).
  2. Convert \(q\) to roll, pitch and yaw.
  3. Differentiate in time: \(v(t) \approx \dfrac{p_{mb}(t) - p_{mb}(t_{prev})}{\Delta t}\), and the yaw rate from the wrapped yaw difference.
  4. Publish /odometry/filtered with pose, twist and covariance.

5. Optimized frontier exploration

Classic frontier exploration drives to the nearest frontier, which wastes time on tiny pockets and doubles back across the map. My node scores every frontier by how much unknown space it would reveal against how far the robot must drive to reach it:

  1. Preprocess — subscribe to Cartographer’s occupancy grid and reshape it into a 2D array.
  2. Detect frontiers — run Canny edge detection on the boundary between known free space and unknown space.
  3. Decide — pick the frontier with the best gain–cost trade-off and send it to Nav2 as the next goal.
\[I_f = \sum_{c \,\in\, N(f,r),\ c\ \text{unknown}} w_c, \qquad D_f = d_{path}(p, f)\] \[f^* = \arg\max_{f \in F} \left( w_1 I_f - w_2 D_f \right)\]

\(I_f\) counts the weighted unknown cells within radius \(r\) of frontier \(f\); \(D_f\) is the path length from the robot position \(p\); \(w_1, w_2\) set the trade-off.

Map update Cartographer SLAM · every 2 s Frontier extraction boundary of known free space Frontier scoring f* = argmax w₁·I_f − w₂·D_f Navigate best frontier → Nav2 goal exploration loop information gain vs. travel cost
Exploration loop. Every 2 s the map is rebuilt and frontiers are extracted along the boundary of known free space; the frontier with the best information-gain-versus-travel-cost score becomes the next Nav2 goal, and scores update as the map grows — steady exploration without redundant paths.
Frontier visualization map: frontiers, robot, chosen frontier and located casualties RViz view of the same moment with the Cartographer map and the Nav2 costmap
The same moment of a run in two views. Left, the frontier map: red lines are frontiers, the dark-blue dot is the robot, the green dot is the chosen frontier and orange dots are located casualties. Right, RViz with Cartographer's map and Nav2's inflated costmap.

6. Field experiments

A real rescue is a large, complex space under time pressure, so the test asks a simple question: how much area does the robot search in five minutes? It was run in three indoor environments with different layouts, under three speed limits, three runs each — 27 runs in total.

Dormitory corridor narrow · many turns Teaching-area corridor long · open · flat Laboratory cluttered · irregular SITE SPEED LIMIT 0.3 m/s 0.6 m/s 0.9 m/s
The three test sites (top) and a map built in five minutes at each speed limit. Maps show shape only and are not drawn to a common scale. White is explored free space, grey is unknown, black marks obstacles and red lines are frontiers; orange dots are located casualties, the blue dot is the robot and the green dot is the next frontier.
SEARCHED AREA IN 5 MIN · 3 RUNS PER SETTING 0.3 m/s 0.6 m/s 0.9 m/s one run 0 100 200 300 400 searched area (m²) 118.8 202.0 213.3 Dormitory corridor narrow · many turns +6% from 0.6 to 0.9 m/s 95.4 148.7 348.5 Teaching-area corridor long · open · flat 3.7× from 0.3 to 0.9 m/s 195.8 248.2 241.5 Laboratory cluttered · irregular peaks at 0.6 m/s Bars show the mean of the three runs; dots show the individual runs.
Area searched in five minutes. Each environment responds differently to a higher speed limit.

What the numbers say. The best speed limit depends on the layout. In the long, open teaching-area corridor, a faster robot simply covers more ground. In the narrow dormitory corridor, turns and tight passages cap the gain once the limit passes 0.6 m/s. In the cluttered laboratory, 0.9 m/s explores no more than 0.6 m/s. A single hand-tuned speed limit is therefore a compromise.

7. Takeaways

Simple by today’s standards, but a complete real-robot loop built from individual parts — sensors, embedded control, SLAM, navigation, perception and decision-making — and the deployment skills used in every later project started here.

The experiments also pointed to what came next. The thesis proposed two directions: tuning exploration parameters such as the speed limit online with model-free RL instead of by hand, and replacing the modular LiDAR stack with model-based, end-to-end RL from vision alone. Learning-based navigation is the line my later research followed.