Files
ros-raspbot-v2/README.md
T

288 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# my_robot
ROS 2 package for the Yahboom Raspbot V2 platform — differential-drive motor control and pan/tilt camera orientation.
---
## Architecture
Both nodes share the same I²C bus. The Linux kernel serialises individual transactions, so they can run as separate processes without additional locking.
### Motor controller
```
┌───────────────────────────────────┐
│ MotorControllerNode │
│ │
/cmd_vel ──────────>│ Twist → differential kinematics │
(geometry_msgs/Twist)│ left = linear (angular × wb/2)│
│ right = linear + (angular × wb/2)│
│ │
/wheel_speeds ──────>│ Direct per-wheel override │
(Float32MultiArray │ [FL, FR, RL, RR] │
4 × float32) │ │
│ ▼ │
│ raspbot_v2_interface │
│ I²C bus 1, addr 0x2B │
│ ▼ │
│ /dev/i2c-1 ─────────> Motors │
│ │
/current_wheel_speeds│<─ telemetry @ 10 Hz │
(Float32MultiArray) │ [FL, FR, RL, RR] │
└───────────────────────────────────┘
```
#### Topics
| Topic | Direction | Type | Description |
|---|---|---|---|
| `/cmd_vel` | Subscribed | `geometry_msgs/Twist` | Velocity command — `linear.x` (m/s) and `angular.z` (rad/s) |
| `/wheel_speeds` | Subscribed | `std_msgs/Float32MultiArray` | Direct per-wheel speed override `[FL, FR, RL, RR]` in library units (0255) |
| `/current_wheel_speeds` | Published | `std_msgs/Float32MultiArray` | Current wheel speeds read from hardware, published at 10 Hz |
#### Parameters
| Parameter | Default | Description |
|---|---|---|
| `wheel_base` | `0.3` | Distance between left and right wheels in metres |
| `max_speed` | `1.0` | Maximum motor speed in library units |
---
### Camera orientation controller
```
┌──────────────────────────────────────┐
│ CameraOrientationNode │
│ │
/joint_command ────────>│ JointState (names: pan, tilt) │
(sensor_msgs/ │ position in radians │
JointState) │ │
│ pan → servo 1 (0°–180°) │
│ tilt → servo 2 (0°–110°) │
│ │
│ ▼ │
│ raspbot_v2_interface │
│ I²C bus 1, addr 0x2B │
│ ▼ │
│ /dev/i2c-1 ──────> Pan/tilt servos │
│ │
/joint_states <────────│ current angles @ 10 Hz │
(sensor_msgs/ │ position in radians │
JointState) │ │
└──────────────────────────────────────┘
```
#### Topics
| Topic | Direction | Type | Description |
|---|---|---|---|
| `/joint_command` | Subscribed | `sensor_msgs/JointState` | Commanded pan/tilt angles. Joint names `"pan"` and `"tilt"`, positions in **radians**. Unknown joint names are ignored. |
| `/joint_states` | Published | `sensor_msgs/JointState` | Current angles reflected from the last command, published at 10 Hz |
#### Parameters
| Parameter | Default | Description |
|---|---|---|
| `pan_servo_id` | `1` | Raspbot servo channel for pan |
| `tilt_servo_id` | `2` | Raspbot servo channel for tilt |
| `pan_min_deg` | `0.0` | Pan lower limit (degrees) |
| `pan_max_deg` | `180.0` | Pan upper limit (degrees) |
| `tilt_min_deg` | `0.0` | Tilt lower limit (degrees) |
| `tilt_max_deg` | `110.0` | Tilt upper limit (degrees) — hardware cap |
| `pan_center_deg` | `90.0` | Startup and shutdown park position for pan |
| `tilt_center_deg` | `60.0` | Startup and shutdown park position for tilt |
| `state_rate_hz` | `10.0` | `~/joint_states` publish rate |
#### Hardware interface
The node drives the pan and tilt servos over **I²C bus 1** (device address `0x2B`). The same `/dev/i2c-1` device used by the motor controller is sufficient — no additional device node is required.
---
## Setting up the robot
### 1. Flash Raspberry Pi OS
Use the [Raspberry Pi Imager](https://www.raspberrypi.com/software/) to write Raspberry Pi OS (64-bit, Lite recommended) to a microSD card.
Before writing, open the imager's **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 has booted and is reachable on the network (test with `ping raspbot-v2.local`), proceed to the next step.
### 2. Provision with Ansible
The [ansible/](ansible/) directory contains a playbook that handles the remaining setup (enabling SPI, installing Docker). See [ansible/README.md](ansible/README.md) for full instructions.
---
## Building
### Prerequisites
- Docker (with BuildKit enabled)
- For cross-compilation from an amd64 host, QEMU user-space emulation must be registered with the kernel. If you haven't done this before, run once:
```bash
docker run --rm --privileged tonistiigi/binfmt --install arm64
```
### Build the image
The Raspberry Pi is `arm64`, so the image must be built for that platform. On an amd64 host use `docker buildx`:
```bash
docker build --platform linux/arm64 -t my_robot:latest .
```
`--load` exports the built image into the local Docker image store so it can be deployed with `docker save`.
The build is split into two stages:
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
---
## Deploying
Once the image is built, pipe it directly to the target over SSH — no intermediate file or registry needed:
```bash
docker save my_robot:latest | ssh matt@raspbot-v2.local docker load
```
Replace `matt` with the username configured in [ansible/inventory.ini](ansible/inventory.ini).
---
## Launching
The container needs access to the I²C bus that the motor controller is wired to. Pass only that device rather than running the container in privileged mode:
```bash
docker run --rm \
--network=host \
--device /dev/i2c-1 \
--env ROS_DOMAIN_ID=0 \
my_robot:latest
```
If your board exposes the motor controller on a different bus (check with `ls /dev/i2c-*` on the host), substitute the correct device node (e.g. `--device /dev/i2c-0`).
### Camera orientation node
Override the default `CMD` to run the camera orientation node instead:
```bash
docker run --rm \
--network=host \
--device /dev/i2c-1 \
--env ROS_DOMAIN_ID=0 \
my_robot:latest \
ros2 run my_robot camera_orientation
```
#### Overriding parameters at launch
```bash
docker run --rm \
--network=host \
--device /dev/i2c-1 \
--env ROS_DOMAIN_ID=0 \
my_robot:latest \
ros2 run my_robot camera_orientation \
--ros-args -p pan_center_deg:=90.0 -p tilt_center_deg:=45.0
```
#### Commanding the camera from the host
Pan to centre (90°) and tilt to 30°:
```bash
ros2 topic pub --once /joint_command sensor_msgs/msg/JointState \
"{name: ['pan', 'tilt'], position: [1.5708, 0.5236]}"
```
You can command a single axis by omitting the other joint name:
```bash
# Pan only
ros2 topic pub --once /joint_command sensor_msgs/msg/JointState \
"{name: ['pan'], position: [0.0]}"
```
#### Verifying orientation state
```bash
ros2 topic echo /joint_states
```
---
### Overriding parameters at launch (motor controller)
ROS 2 parameters can be passed through `--ros-args`:
```bash
docker run --rm \
--network=host \
--device /dev/i2c-1 \
--env ROS_DOMAIN_ID=0 \
my_robot:latest \
ros2 run my_robot motor_controller \
--ros-args -p wheel_base:=0.25 -p max_speed:=0.8
```
### Sending velocity commands from the host
With the container running, publish a `cmd_vel` message from another terminal (requires ROS 2 installed on the host or a second container on the same network):
```bash
# Drive forward at 0.2 m/s
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.02}, angular: {z: 0.0}}"
# Turn on the spot
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.0}, angular: {z: 0.5}}"
# Stop
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.0}, angular: {z: 0.0}}"
```
### Verifying telemetry
```bash
ros2 topic echo /current_wheel_speeds
```
---
## Project layout
```
.
├── Dockerfile # Two-stage production image
├── docker-entrypoint.sh # Sources ROS overlays before exec
├── src/
│ └── my_robot/
│ ├── package.xml # ROS package manifest
│ ├── setup.py # ament_python build definition
│ └── my_robot/
│ ├── __init__.py
│ ├── motor_controller_node.py # Differential-drive motor control
│ └── camera_orientation_node.py # Pan/tilt servo control
└── raspbot_v2_interface/ # Vendored Yahboom hardware library
└── Raspbot_Lib/
└── Raspbot_Lib.py # I²C driver (smbus, bus 1, addr 0x2B)
```