The ros controller will timeout if it doesn't receive a cmd_vel message in a given period, stopping the wheels. webui sends messages at 100ms intervals, so the update rate needs to be 10Hz This may need to be reviewed when using other input systems.
raspbot_v2
ROS 2 robot platform based on the Yahboom Raspbot V2. Multiple services run as Docker containers, coordinated by Docker Compose.
Sub-projects
| Directory | Description |
|---|---|
| robot/ | Differential-drive motor control, pan/tilt camera, and ultrasonic range sensor |
| lidar/ | RPLIDAR A1 laser scanner |
| oled/ | OLED display dashboard |
| wifi/ | Wi-Fi hotspot fallback manager |
| camera_publisher/ | V4L2 camera → ROS 2 topic publisher |
| webrtc_streamer/ | WebRTC browser stream server |
| webui/ | Browser-based robot controller |
| ansible/ | Provisioning playbook for the Raspberry Pi |
Setting up the robot
1. Flash Raspberry Pi OS
Use the Raspberry Pi Imager to write Raspberry Pi OS (64-bit, Lite recommended) to a microSD card.
Before writing, open Advanced options (⚙) and configure:
| Setting | Value |
|---|---|
| Hostname | raspbot-v2.local |
| SSH | Enabled |
| Username / Password | Your preferred credentials |
| Wi-Fi | Your network SSID and password (if not using Ethernet) |
Write the image, insert the card, and power on the Pi. Once it is reachable on the network (test with ping raspbot-v2.local), proceed to the next step.
2. Provision with Ansible
The ansible/ directory contains a playbook that handles the remaining setup (enabling SPI, installing Docker). See ansible/README.md for full instructions.
Building
Prerequisites
-
Docker with BuildKit enabled
-
For cross-compilation from an amd64 host, register QEMU user-space emulation once:
docker run --rm --privileged tonistiigi/binfmt --install arm64
Build all images
docker compose build
Or build a single service:
docker compose build robot
docker compose build lidar
Deploying
Pipe images directly to the target over SSH — no intermediate file or registry needed:
# Upload all images
docker save $(docker compose config --images) | ssh <user>@raspbot-v2.local docker load
# Upload a single image
docker save raspbot_v2_oled:latest | ssh <user>@raspbot-v2.local docker load
docker compose config --images resolves all image names from the compose file, including any environment variable substitutions.
Then copy the compose file and any .env to the target:
scp docker-compose.yml <user>@raspbot-v2.local:~/
Replace <user> with the username configured in ansible/inventory.ini.
Launching
Start everything
docker compose up
To run in the background:
docker compose up -d
docker compose logs -f # follow logs
docker compose down # stop and remove containers
Environment variables
Create a .env file alongside docker-compose.yml to override defaults:
ROS_DOMAIN_ID=0
LIDAR_PORT=/dev/ttyUSB0
LIDAR_FRAME_ID=laser
WIFI_SSID=MyNetwork
| Variable | Default | Description |
|---|---|---|
ROS_DOMAIN_ID |
0 |
ROS 2 domain — must match on all nodes |
LIDAR_PORT |
/dev/ttyUSB0 |
Host device node for the RPLIDAR |
LIDAR_FRAME_ID |
laser |
frame_id in published LaserScan messages |
WIFI_SSID |
(empty) | Target SSID; if unset the Wi-Fi container creates a hotspot immediately |
HOTSPOT_SSID |
raspbot-hotspot |
Fallback hotspot SSID |
HOTSPOT_PASSWORD |
raspbot1234 |
Fallback hotspot passphrase |
Project layout
.
├── docker-compose.yml
├── docker-entrypoint.sh
├── robot/ # Motor controller, pan/tilt, ultrasonic
├── lidar/ # RPLIDAR A1
├── oled/ # OLED display dashboard
├── wifi/ # Wi-Fi hotspot fallback
├── camera_publisher/ # V4L2 camera → ROS 2 topic
├── webrtc_streamer/ # WebRTC browser stream
├── webui/ # Browser-based controller UI
└── ansible/ # Raspberry Pi provisioning