Add lider ROS node and move robot control into its own directory

This commit is contained in:
2026-04-21 11:39:01 +00:00
parent 1d49e45240
commit f5ea157e21
28 changed files with 221 additions and 31 deletions
+137 -29
View File
@@ -149,6 +149,44 @@ The sensor will activate automatically when this command runs and deactivate whe
---
### RPLIDAR A1
Runs in a separate container built from [lidar/Dockerfile](lidar/Dockerfile).
```
┌──────────────────────────────────────┐
│ sllidar_ros2 (rplidar_node) │
│ │
│ serial 115200 baud │
│ angle_compensate = true │
│ scan_mode = Sensitivity │
│ ▼ │
│ /dev/ttyUSB0 ──────> RPLIDAR A1 │
│ │
/scan <───────│ LaserScan @ ~10 Hz │
(sensor_msgs/ │ 360° scan, range 0.1512 m │
LaserScan) │ │
└──────────────────────────────────────┘
```
#### Topics
| Topic | Direction | Type | Description |
|---|---|---|---|
| `/scan` | Published | `sensor_msgs/LaserScan` | 360° laser scan in the `laser` frame |
#### Configuration
The LIDAR container is configured via environment variables in `.env` or `docker-compose.yml`. See the [Launching](#launching) section for details.
#### Verifying LIDAR data
```bash
ros2 topic echo /scan
```
---
## Setting up the robot
### 1. Flash Raspberry Pi OS
@@ -183,29 +221,51 @@ The [ansible/](ansible/) directory contains a playbook that handles the remainin
docker run --rm --privileged tonistiigi/binfmt --install arm64
```
### Build the image
### Build with Docker Compose (recommended)
The Raspberry Pi is `arm64`, so the image must be built for that platform. On an amd64 host use `docker buildx`:
Both images are defined in `docker-compose.yml`. Build them together:
```bash
docker build --platform linux/arm64 -t raspbot_v2:latest .
docker compose build
```
`--load` exports the built image into the local Docker image store so it can be deployed with `docker save`.
Or build a single service:
The build is split into two stages:
```bash
docker compose build robot
docker compose build lidar
```
1. **builder** — installs the Raspbot hardware library, then compiles the ROS package with `colcon`
2. **runtime** — copies only the colcon install overlay and hardware library into a clean `ros:kilted` base; no build tools are included in the final image
The builds are split into two stages each:
1. **builder** — compiles the ROS package(s) with `colcon`; the lidar builder also clones `sllidar_ros2` from GitHub
2. **runtime** — copies only the install overlay into a clean `ros:kilted-ros-core` base; no build tools in the final image
### Build images individually
```bash
# Robot controller
docker build --platform linux/arm64 -f robot/Dockerfile -t raspbot_v2:latest .
# LIDAR
docker build --platform linux/arm64 -f lidar/Dockerfile -t raspbot_v2_lidar:latest .
```
---
## Deploying
Once the image is built, pipe it directly to the target over SSH — no intermediate file or registry needed:
Pipe both images directly to the target over SSH — no intermediate file or registry needed:
```bash
docker save raspbot_v2:latest | ssh matt@raspbot-v2.local docker load
docker save raspbot_v2:latest raspbot_v2_lidar:latest \
| ssh matt@raspbot-v2.local docker load
```
Then copy the compose file to the target:
```bash
scp docker-compose.yml matt@raspbot-v2.local:~/
```
Replace `matt` with the username configured in [ansible/inventory.ini](ansible/inventory.ini).
@@ -214,19 +274,55 @@ Replace `matt` with the username configured in [ansible/inventory.ini](ansible/i
## Launching
The default `CMD` starts both nodes together via the launch file. The container needs access to the I²C bus — pass only that device rather than running privileged:
### Start everything with Docker Compose (recommended)
```bash
docker compose up
```
This starts both the robot controller and LIDAR containers. Logs from both are interleaved in the terminal, each line prefixed with the service name. To run in the background:
```bash
docker compose up -d
docker compose logs -f # follow logs
docker compose down # stop and remove containers
```
### Environment variables
Create a `.env` file in the same directory as `docker-compose.yml` to override defaults:
```bash
ROS_DOMAIN_ID=0
LIDAR_PORT=/dev/ttyUSB0
LIDAR_FRAME_ID=laser
```
| 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 |
### Run containers individually
```bash
# Robot controller
docker run --rm \
--network=host \
--device /dev/i2c-1 \
--env ROS_DOMAIN_ID=0 \
raspbot_v2:latest
# LIDAR
docker run --rm \
--network=host \
--device /dev/ttyUSB0 \
--env ROS_DOMAIN_ID=0 \
raspbot_v2_lidar:latest
```
If your board exposes the controller on a different bus (check with `ls /dev/i2c-*` on the host), substitute the correct device node (e.g. `--device /dev/i2c-0`).
### Overriding parameters at launch
### Overriding robot launch parameters
Launch arguments can be appended after the image name:
@@ -250,6 +346,14 @@ Available launch arguments:
| `tilt_center_deg` | `60.0` | Tilt angle at startup and shutdown (degrees) |
| `ultrasonic_rate_hz` | `10.0` | Ultrasonic sensor publish rate (Hz) |
### LIDAR device permissions
The RPLIDAR connects as a USB serial device. If the user running Docker is not in the `dialout` group, add them and log back in:
```bash
sudo usermod -aG dialout $USER
```
### Sending velocity commands from the host
With the container running, publish from another terminal (requires ROS 2 on the host or a second container on the same network):
@@ -301,20 +405,24 @@ ros2 topic echo /joint_states
```
.
├── Dockerfile # Two-stage production image
├── docker-entrypoint.sh # Sources ROS overlays before exec
├── src/
── raspbot_v2/
├── package.xml # ROS package manifest
── setup.py # ament_python build definition
│ ├── launch/
── robot.launch.py # Starts both nodes together
── raspbot_v2/
├── __init__.py
── motor_controller_node.py # Differential-drive motor control
│ ├── camera_orientation_node.py # Pan/tilt servo control
── ultrasonic_node.py # HC-SR04 range sensor
└── raspbot_v2_interface/ # Vendored Yahboom hardware library
└── Raspbot_Lib/
└── Raspbot_Lib.py # I²C driver (smbus, bus 1, addr 0x2B)
├── docker-compose.yml # Launches robot and lidar containers together
├── docker-entrypoint.sh # Sources ROS overlays before exec (shared by both images)
├── robot/
── Dockerfile # Robot controller image (two-stage)
├── src/
── raspbot_v2/
├── package.xml # ROS package manifest
── setup.py # ament_python build definition
── launch/
│ └── robot.launch.py # Starts all robot nodes together
── raspbot_v2/
├── __init__.py
── motor_controller_node.py # Differential-drive motor control
│ │ ├── camera_orientation_node.py # Pan/tilt servo control
│ │ └── ultrasonic_node.py # HC-SR04 range sensor
└── raspbot_v2_interface/ # Vendored Yahboom hardware library
│ └── Raspbot_Lib/
│ └── Raspbot_Lib.py # I²C driver (smbus, bus 1, addr 0x2B)
└── lidar/
└── Dockerfile # RPLIDAR A1 image (two-stage, clones sllidar_ros2)
```