04. Arduino UNO Q ROS 2 双核通信与话题控制
目录
1 项目介绍
1.1 这是什么项目
本项目演示了如何将 Arduino UNO Q 变成一台支持 ROS 2 的嵌入式机器人控制器。通过 ROS 2 的话题发布/订阅机制,你可以用一条命令行远程控制 UNO Q 的板载 LED 亮灭。
虽是"点灯",但走通的链路是:ROS 2 话题 → Docker 容器 → Bridge RPC → STM32 微控制器 → 硬件 GPIO。这套架构可以无缝扩展到电机控制、传感器数据上报、多节点协同等真实机器人场景。
1.2 项目亮点
| 亮点 | 说明 |
|---|---|
| ROS 2 原生支持 | UNO Q 直接运行 ROS 2 Jazzy,可作为标准 ROS 2 节点加入机器人系统 |
| 双核协同架构 | Qualcomm MPU 跑 Linux + ROS,STM32 MCU 管硬件 I/O,分工明确 |
| Docker 容器化部署 | 打包完整 ROS 2 环境,即开即用,不污染宿主机 |
| Bridge RPC 桥接 | Linux 侧通过 RouterBridge RPC 直接调用 MCU 函数,毫秒级响应 |
| 话题驱动控制 | 任何 ROS 2 节点都可以发布 /led_status 话题来控制 LED,解耦灵活 |
| 可扩展性强 | 架构支持添加更多 RPC 函数(舵机、传感器、PWM),不改动核心框架 |
1.3 为什么用 Docker
UNO Q 的 Linux 环境是精简版 Debian,没有 sudo、没有 pip3,无法直接安装 ROS 2 全家桶。Docker 容器内是完整的 Ubuntu 系统,可以自由安装任何依赖。镜像构建一次,后续启动只需两秒。
2 项目实操
2.1 实验简介
本节将一步步完成 ROS 2 环境的搭建和 LED 远程控制。你需要准备:
- Arduino UNO Q 开发板 × 1(已连接 WiFi)
- 电脑 × 1(通过 SSH 连接 UNO Q)
- UNO Q 已初始化 Linux 环境(Debian 13,Docker 已预装)
提示: 首次构建 Docker 镜像需要下载约 1~2GB 数据,耗时约 8~30 分钟。请确保 UNO Q 网络畅通,磁盘可用空间 > 4GB。国内用户需要配置 Docker 镜像源(教程中已处理)。
2.2 环境检查
SSH 登录 UNO Q,确认环境就绪:
ssh arduino@192.168.x.x
检查三个关键条件:
uname -a # 查看内核版本。UNO Q 必须 ≥ 6.x,否则 Docker 跑不了。
docker --version # 查看 Docker 有没有装。UNO Q 出厂自带,如果没有说明 Linux 环境没初始化。
df -h / # 查看磁盘还剩多少。Avail 低于 4GB 就装不下 ROS 镜像了。
2.3 克隆项目并修复镜像源
进入 App Lab 项目目录,克隆项目:
cd ~/ArduinoApps
git clone https://github.com/miguelgonrod/ros_arduino_uno_Q.git
cd ros_arduino_uno_Q
关键步骤: 国内网络无法直接访问 Docker Hub,必须将镜像源改为 DaoCloud。
sed -i 's|docker.io/ros:jazzy-ros-base|docker.m.daocloud.io/ros:jazzy-ros-base|' python/Dockerfile
head -1 python/Dockerfile # 确认已改为 docker.m.daocloud.io
2.4 烧录固件并构建镜像
运行一键脚本,自动完成固件烧录和镜像构建:
chmod +x run.sh
./run.sh
镜像构建完成
2.5 启动 ROS 2 环境
注意
在编译构建好之后就自动启动了,当用户是第一次构建时且构建成功则可以直接到步骤二;
步骤一:启动容器(第二次及以后打开时启动容器)
docker rm -f ros_jazzy_container 2>/dev/null
docker run -d --net=host --name ros_jazzy_container \
-v /var/run/arduino-router.sock:/var/run/arduino-router.sock \
ros_jazzy_ws tail -f /dev/null
步骤二:启动 ROS 2 节点
(1)第一次构建时启动节点:
ros2 run ros_led led
(2)第二次及以后启动节点:
docker exec -d ros_jazzy_container bash -c \
"source /opt/ros/jazzy/setup.bash && \
source /ros2_ws/install/setup.bash && \
ros2 run ros_led led > /tmp/ros_node.log 2>&1"
2.6 发送话题控制 LED
注意:需要重开一个窗口
# LED 亮
docker exec ros_jazzy_container bash -c \
'source /opt/ros/jazzy/setup.bash && \
ros2 topic pub /led_status std_msgs/msg/Bool "{data: true}" --once'
# LED 灭
docker exec ros_jazzy_container bash -c \
'source /opt/ros/jazzy/setup.bash && \
ros2 topic pub /led_status std_msgs/msg/Bool "{data: false}" --once'
注意: UNO Q 板载 LED 是低电平有效(Active Low)。
data: true→LOW→ LED 亮;data: false→HIGH→ LED 灭。这与标准 Arduino 相反。
3 运行效果
发布消息后在终端打印LED状态和板载LED的亮灭
亮灯
灭灯
4 项目技术介绍
4.1 系统架构
UNO Q 采用双处理器架构,本项目的通信链路如下:
数据流步骤:
- 用户执行
ros2 topic pub /led_status std_msgs/msg/Bool "{data: true}" - ROS 2 DDS 发现协议将消息发送到容器内的
led.py节点 led.py的listener_callback()被触发,执行Bridge.call("set_led_state", True)- Bridge 模块通过 Unix Socket(
/var/run/arduino-router.sock)发送 MsgPack-RPC 请求 - RouterBridge 服务将 RPC 请求转发给 STM32 MCU
- MCU 执行
set_led_state(True)→digitalWrite(LED_BUILTIN, LOW)→ LED 亮
4.2 双核分工
| 处理器 | 角色 | 承担任务 |
|---|---|---|
| Qualcomm MPU | “大脑” | 运行 Linux + Docker + ROS 2,处理通信协议、话题订阅 |
| STM32U585 MCU | “肢体” | 控制 GPIO 等硬件 I/O,执行 RPC 函数,响应实时指令 |
这种分工是嵌入式机器人开发的标准模式:MPU 负责"想",MCU 负责"做"。
4.3 Bridge RPC 通信机制
UNO Q 的 RouterBridge 提供了 Linux ↔ MCU 的 RPC 通信通道。通信基于 MsgPack 二进制协议,通过 Unix Socket 传输。
MCU 侧(注册函数):
#include "Arduino_RouterBridge.h"
void setup() {
Bridge.begin();
Bridge.provide("set_led_state", set_led_state); // 注册 RPC 函数
}
void set_led_state(bool state) {
digitalWrite(LED_BUILTIN, state ? LOW : HIGH);
}
| 代码 | 作用 |
|---|---|
Bridge.begin() |
初始化 RouterBridge 通信 |
Bridge.provide(name, func) |
将 C++ 函数注册为 RPC 服务,供 Linux 侧远程调用 |
Linux 侧(调用函数):
from ros_led.app_utils import Bridge
from std_msgs.msg import Bool
class MyRos2Node(Node):
def __init__(self):
self.subscription = self.create_subscription(
Bool, 'led_status', self.listener_callback, 10)
def listener_callback(self, msg):
Bridge.call("set_led_state", msg.data) # RPC 调用 MCU
| 代码 | 作用 |
|---|---|
create_subscription(Bool, 'led_status', ...) |
订阅 /led_status 话题,消息类型为 Bool |
Bridge.call("set_led_state", msg.data) |
通过 RPC 调用 MCU 上的同名函数 |
4.4 Dockerfile 解析
FROM docker.m.daocloud.io/ros:jazzy-ros-base # 基础镜像:ROS 2 Jazzy
RUN apt-get install tmux python3-pip # 装工具
RUN pip install msgpack watchdog # 装 Python 依赖
COPY ./src /ros2_ws/src # 复制 ROS 2 包源码
RUN colcon build # 编译 ROS 2 包
4.5 ROS 2 话题通信
| 概念 | 说明 | 本项目中的实例 |
|---|---|---|
| Node(节点) | 独立的 ROS 2 进程 | ros_led_node(led.py) |
| Topic(话题) | 节点间通信的通道 | /led_status |
| Message(消息) | 话题中传输的数据 | std_msgs/Bool(True/False) |
| Publisher(发布者) | 向话题发送消息的节点 | 你的 ros2 topic pub 命令 |
| Subscriber(订阅者) | 从话题接收消息的节点 | ros_led_node |
| 文件 | 运行位置 | 语言 | 作用 |
|---|---|---|---|
sketch/sketch.ino |
STM32 MCU | C++ | 注册 set_led_state RPC 函数 |
python/src/ros_led/ros_led/led.py |
Docker 容器 | Python | ROS 2 节点,订阅话题并调用 RPC |
python/Dockerfile |
构建时 | Docker | 定义容器环境的构建步骤 |
python/src/ros_led/setup.py |
构建时 | Python | 注册 led 可执行入口 |
附录:ROS 2 在机器人开发中的意义
本项目的核心价值不在于"点灯"本身,而在于验证了 UNO Q 作为 ROS 2 原生边缘节点的可行性。基于此架构可以扩展:
- 传感器数据上报:I2C/SPI 传感器 → MCU 读数 → Bridge RPC → ROS 2 话题发布
- 执行器控制:ROS 2 话题 → Bridge RPC → MCU PWM → 舵机/电机
- 多节点协同:多个 ROS 2 节点在容器内并行运行,通过话题交换数据
- 跨设备通信:UNO Q 作为 ROS 2 节点,与电脑上的 SLAM、导航等节点无缝通信
这为 UNO Q 进入现代机器人开发生态铺平了道路。











