# my_robot — Motor Controller Node ROS 2 package for differential-drive motor control on the Yahboom Raspbot V2 platform. --- ## Architecture ``` ┌──────────────────────────────────┐ │ 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 (0–255) | | `/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 | ### Hardware interface The node drives the Yahboom Raspbot V2 motor controller over **I²C bus 1** (device address `0x2B`) using the bundled `raspbot_v2_interface` library. The only host device required is `/dev/i2c-1`. --- ## 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. --- ## 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). --- ## 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 --- ## 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 \ --device /dev/i2c-1 \ 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`). ### Overriding parameters at launch ROS 2 parameters can be passed through `--ros-args`: ```bash docker run --rm \ --device /dev/i2c-1 \ 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.2}, 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 ├── package.xml # ROS package manifest ├── setup.py # ament_python build definition ├── my_robot/ │ ├── __init__.py │ └── motor_controller_node.py └── raspbot_v2_interface/ # Vendored Yahboom hardware library └── Raspbot_Lib/ └── Raspbot_Lib.py # I²C driver (smbus, bus 1, addr 0x2B) ```