ROS2 4-wheel diff drive robot.
Started as a university project for a microcontrolers-related subject.
The tiny piece of code that has anything to do with the subject is present in ros2_ws/src/skid_drive_firmware and it is just an excuse for building this robot.
Note
This is still a work in progress. Future improvements include sensor fusion and SLAM, as well as replacing the microcontroller and motors with a closed PID loop.
- ROS2 Jazzy installed natively on workstation and the robot. Using a VM for development is a good alternative
- Some hardware (TODO: description)
- Docker and Docker Compose (optional, for development only)
-
Clone the repository:
git clone git@github.com:b-Tomas/skid-drive-robot.git cd skid-drive-robot -
Initialize and update git submodules:
git submodule update --init --recursive
-
(Optional) Build the Docker images for development:
docker compose -f docker/compose.yaml build
Before building the workspace for the first time, you need to set up rosdep to install system dependencies:
-
Initialize rosdep (if not already done):
sudo rosdep init
-
Update rosdep database:
rosdep update
You can use either Docker or native ROS2 installation on your workstation.
Option A: Using Docker (optional)
-
Start the development container:
./docker/start-dev.sh
-
Inside the container, install system dependencies:
cd /ros2_ws rosdep install --from-paths src --ignore-src -r -y -
Build the workspace:
colcon build --symlink-install
-
Source the installation:
source install/setup.bash
Option B: Using Native ROS2 Installation
-
Install system dependencies:
cd ros2_ws rosdep install --from-paths src --ignore-src -r -y -
Build the workspace:
colcon build --symlink-install
-
Source the installation:
source install/setup.bash
Tip
You need to source install/setup.bash in each new shell session to use the built packages. Alternatively, you can add it to your ~/.bashrc for automatic sourcing.
Since the Raspberry Pi on the robot uses a different architecture (ARM) than the workstation (x86_64), packages must be built on the robot itself and cannot just be transferred using sync.sh.
With ROS2 installed natively on the robot:
-
Sync source code from workstation to robot:
./sync.sh
-
On the robot, install system dependencies:
cd ros2_ws rosdep install --from-paths src --ignore-src -r -y -
Build the workspace:
# The extra environment variable and sequential setting was the combination of # parameters that prevented my RPi 3b from hanging MAKEFLAGS="-j 1" colcon build --symlink-install --executor sequential
-
Source the installation:
source install/setup.bash
Tip
You need to source install/setup.bash in each new shell session to use the built packages. Alternatively, you can add it to your ~/.bashrc for automatic sourcing.
Using Docker:
Run ./docker/start-dev.sh to start the dev container with GUI support.
Using Native ROS2:
Source your workspace:
source ros2_ws/install/setup.bash # if the workspace is builtNote
The docker setup is to be improved. This container was enough to run some simple ROS2 commands for quickstarting packages.
In the end I resorted to an Ubuntu Desktop VM with native ROS.
First set up the udev rules for the RPLidar A1M8:
cd ros2_ws/src/sllidar_ros2/
source scripts/create_udev_rules.shTo launch the RPLidar A1M8, run:
ros2 launch skid_drive_bringup rplidar_a1m8.launch.pyYou can override parameters:
ros2 launch skid_drive_bringup rplidar_a1m8.launch.py serial_port:=/dev/ttyUSB0The phone IMU bridge node publishes IMU data from a phone to ROS2 topics via WebSocket.
To launch the phone IMU bridge, run:
ros2 run phone_imu_bridge imu_server_nodeThe node will:
- Start a WebSocket server on
0.0.0.0:5000 - Publish IMU data to the
/phone_imutopic (sensor_msgs/msg/Imu) - Use frame ID
phone_imu_link
See the phone-imu-ros2 submodule for more details.
To set up the transform tree linking all robot frames:
ros2 launch skid_drive_bringup static_transforms.launch.pyThis publishes static transforms from base_link to:
laser(lidar frame)
You can customize the laser position/orientation:
ros2 launch skid_drive_bringup static_transforms.launch.py \
laser_x:=0.1 laser_y:=0.0 laser_z:=0.2 \
laser_roll:=0.0 laser_pitch:=0.0 laser_yaw:=0.0To visualize lidar and IMU data in RViz:
ros2 launch skid_drive_bringup rviz.launch.pyThis will launch RViz2 with a pre-configured visualization that includes:
- LaserScan display for lidar data (
/scantopic) - TF display showing coordinate frames (including IMU orientation)
- Grid for reference
This assumes the following are running:
- Static transforms (
static_transforms.launch.py) - Lidar node (
rplidar_a1m8.launch.py) - Phone IMU bridge (
phone_imu_bridge.launch.py)
The motor control node listens to cmd_vel and uses software PWM to achieve variable speed control, even though the hardware only supports discrete on/off states.
To start the motor control node:
ros2 run skid_drive_control skid_drive_control_nodeTo specify the serial port and other parameters:
ros2 run skid_drive_control skid_drive_control_node --ros-args \
-p port:=/dev/ttyUSB0 \
-p pwm_frequency:=50.0 \
-p max_velocity:=1.0port(string, default:/dev/ttyS0): Serial port for motor controllerbaudrate(int, default:9600): Serial baud ratetimeout(float, default:0.5): Serial read timeoutpwm_frequency(float, default:50.0): Software PWM frequency in Hzmax_velocity(float, default:1.0): Maximum velocity for normalization
To control the robot using keyboard (recommended method):
ros2 run teleop_twist_keyboard teleop_twist_keyboardInstructions will appear in the console.
TODO
To test serial communication with the motor controller:
ros2 run skid_drive_firmware serial_test.pyThis script allows you to manually control the motors via serial commands:
J: Turn leftK: Move forwardL: Move backward;: Turn rightSpace: Stop all motorsEnter: Exit
skid-drive-robot/
├── README.md
├── LICENSE
├── ros2_ws/
│ ├── src/
│ │ ├── skid_drive_description/ (TODO)
│ │ ├── skid_drive_bringup/
│ │ ├── skid_drive_hardware/ (TODO)
│ │ ├── skid_drive_controller/ (TODO)
│ │ ├── skid_drive_control/
│ │ ├── skid_drive_firmware/
│ │ ├── phone-imu-ros2/ (git submodule)
│ │ └── sllidar_ros2/ (git submodule)
└── docs/
- ROS2 Jazzy
- sllidar_ros2 (included as git submodule)
- phone-imu-ros2 (included as git submodule)
- python3-serial (for serial communication with motor controller)
All system dependencies are declared in the package package.xml files and can be installed automatically using rosdep install.

