Added Dockerfile to build robot controoler container
This commit is contained in:
Vendored
+4
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"python.analysis.extraPaths": [
|
"python.analysis.extraPaths": [
|
||||||
"/opt/ros/kilted/lib/python3.12/site-packages"
|
"/opt/ros/kilted/lib/python3.12/site-packages"
|
||||||
|
],
|
||||||
|
"ROS2.distro": "kilted",
|
||||||
|
"python.autoComplete.extraPaths": [
|
||||||
|
"/opt/ros/kilted/lib/python3.12/site-packages"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
|
project(my_robot)
|
||||||
|
|
||||||
|
find_package(ament_cmake REQUIRED)
|
||||||
|
find_package(ament_cmake_python REQUIRED)
|
||||||
|
|
||||||
|
ament_python_install_package(${PROJECT_NAME})
|
||||||
|
|
||||||
|
ament_package()
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# ── Stage 1: build ────────────────────────────────────────────────────────────
|
||||||
|
FROM ros:kilted AS builder
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
python3-colcon-common-extensions \
|
||||||
|
python3-pip \
|
||||||
|
ros-${ROS_DISTRO}-ament-cmake-python \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /ws
|
||||||
|
|
||||||
|
# Install the vendored Raspbot hardware library
|
||||||
|
COPY raspbot_v2_interface/ raspbot_v2_interface/
|
||||||
|
# PEP 668: newer Debian/Ubuntu marks the system Python as externally managed and blocks pip
|
||||||
|
# by default. --break-system-packages overrides this; safe here as the container is isolated.
|
||||||
|
RUN pip3 install --no-cache-dir --break-system-packages ./raspbot_v2_interface
|
||||||
|
|
||||||
|
# Copy the ROS package into the standard colcon src/ layout and build it
|
||||||
|
COPY package.xml setup.py setup.cfg CMakeLists.txt src/my_robot/
|
||||||
|
COPY my_robot/ src/my_robot/my_robot/
|
||||||
|
|
||||||
|
RUN . /opt/ros/${ROS_DISTRO}/setup.sh && \
|
||||||
|
colcon build --packages-select my_robot
|
||||||
|
|
||||||
|
# ── Stage 2: runtime ──────────────────────────────────────────────────────────
|
||||||
|
FROM ros:kilted-ros-core
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ros-${ROS_DISTRO}-rclpy \
|
||||||
|
ros-${ROS_DISTRO}-geometry-msgs \
|
||||||
|
ros-${ROS_DISTRO}-std-msgs \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Bring across the installed Raspbot library and the built ROS overlay
|
||||||
|
COPY --from=builder /usr/local/lib /usr/local/lib
|
||||||
|
COPY --from=builder /ws/install /ws/install
|
||||||
|
|
||||||
|
# Source both ROS base and the workspace overlay on every shell/exec
|
||||||
|
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> /etc/bash.bashrc && \
|
||||||
|
echo "source /ws/install/setup.bash" >> /etc/bash.bashrc
|
||||||
|
|
||||||
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
RUN chmod +x /docker-entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||||
|
CMD ["ros2", "run", "my_robot", "motor_controller"]
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
# 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)
|
||||||
|
```
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source /opt/ros/${ROS_DISTRO}/setup.bash
|
||||||
|
source /ws/install/setup.bash
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
+2
-1
@@ -10,7 +10,8 @@
|
|||||||
<exec_depend>geometry_msgs</exec_depend>
|
<exec_depend>geometry_msgs</exec_depend>
|
||||||
<exec_depend>std_msgs</exec_depend>
|
<exec_depend>std_msgs</exec_depend>
|
||||||
|
|
||||||
<buildtool_depend>ament_python</buildtool_depend>
|
<buildtool_depend>ament_cmake</buildtool_depend>
|
||||||
|
<buildtool_depend>ament_cmake_python</buildtool_depend>
|
||||||
<test_depend>ament_copyright</test_depend>
|
<test_depend>ament_copyright</test_depend>
|
||||||
<test_depend>ament_flake8</test_depend>
|
<test_depend>ament_flake8</test_depend>
|
||||||
<test_depend>ament_pep257</test_depend>
|
<test_depend>ament_pep257</test_depend>
|
||||||
|
|||||||
Reference in New Issue
Block a user