Matteo RuzdijicAbout
Four-wheeled autonomous rover with large off-road tires, a Raspberry Pi, and a LiDAR unit mounted on top, in front of a project poster board

Autonomous Rover

ROS 2 · SLAM · Navigation · 2026

Role
Navigation & Controls Software
Timeline
Jan 2026 – Apr 2026
Team
Team of 3
Tools
ROS 2, Python, C++, Arduino, Nav2, LiDAR SLAM

Built a ROS 2 rover that maps a room with LiDAR, localizes itself without drift, and navigates it autonomously.

The Problem

We wanted to build an autonomous mobile robot from the ground up. It had to localize itself and navigate an unknown space in real time, not rely on a pre-mapped route or a joystick.

My Approach

I worked with two teammates and built the ROS 2 stack bridging Python and C++ navigation nodes to low-level Arduino motor control, over a custom serial bridge I wrote myself. I integrated an SL LiDAR for 2D SLAM mapping, wrote dual-track encoder ISRs for wheel odometry, and tuned the PID motor controllers by hand. Nav2 handled path planning, and I built a joystick teleop mode for manual runs when we needed one.

Outcome

The rover localized without drift and built usable SLAM maps in real time. It navigated test environments autonomously through Nav2, no manual correction needed.

System Architecture

The rover runs a distributed ROS 2 graph bridging software to hardware: a joystick teleop node and the LiDAR driver publish over the DDS pub/sub layer, two custom bridge nodes translate that into the Arduino's serial protocol and timestamp-corrected scan data, and SLAM toolbox and the Arduino's own control loop consume them to build the map and drive the wheels. Hover over each node for details.

Teleop Node

teleop.py

Reads a Logitech F710 joystick over pygame and publishes geometry_msgs/Twist commands to /cmd_vel at 30 Hz, with slew-rate limiting and an e-stop trigger for manual runs.

LiDAR Driver

sllidar_ros2

Third-party driver for the SLLiDAR unit, publishing raw /scan data. Its onboard clock has a known timestamp bug that the scan relay corrects downstream.

ROS 2 DDS Middleware

Publish / Subscribe network

The pub/sub layer connecting every node. Teleop, the LiDAR driver, both bridges, robot_state_publisher's static TF tree, and SLAM toolbox all talk to each other over ROS 2 topics and transforms.

Scan Relay

scan_relay.py

Re-stamps every LiDAR scan with the current ROS time and republishes it as /scan_corrected, working around the driver's hardware timer bug so SLAM toolbox's TF filter can match scans to transforms.

Serial Bridge

serial_bridge.py

Converts /cmd_vel into the Arduino's 'V,vd,wd' serial protocol, and parses the Arduino's 'O,v,w' odometry lines back into /odom and the odom→base_footprint transform.

SLAM Toolbox

online_async_launch

Fuses /scan_corrected with the live TF tree to build the 2D occupancy map and localize the rover in real time. Started a few seconds after the serial bridge connects so odometry TF is already flowing.

Arduino Microcontroller

robot_controller.ino

Runs interrupt-driven encoder counters for both wheels and a feedforward + PI control loop at 20 Hz to hold each wheel's commanded speed, streaming odometry back over serial.
Top-down view of the rover chassis with LiDAR unit and onboard electronics mounted
Rover chassis with LiDAR and sensor mounting
2D LiDAR point cloud showing the rover's SLAM map, with scan rays radiating out from the rover's position
2D SLAM map generated during a test run