04. Arduino UNO Q ROS 2 双核通信与话题控制

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 采用双处理器架构,本项目的通信链路如下:

数据流步骤:

  1. 用户执行 ros2 topic pub /led_status std_msgs/msg/Bool "{data: true}"
  2. ROS 2 DDS 发现协议将消息发送到容器内的 led.py 节点
  3. led.py 的 listener_callback() 被触发,执行 Bridge.call("set_led_state", True)
  4. Bridge 模块通过 Unix Socket(/var/run/arduino-router.sock)发送 MsgPack-RPC 请求
  5. RouterBridge 服务将 RPC 请求转发给 STM32 MCU
  6. 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 进入现代机器人开发生态铺平了道路。