教程 · 入门 · 阅读约 1 小时
在树莓派上安装 ROS 2 并构建工作空间
让 ROS 2 在树莓派上跑起来:选对操作系统,用 apt 安装,构建 colcon 工作空间,并让笔记本和机器人通过 Wi-Fi 互相发现。
简介
ROS 2 本身并不难,直到你把它装到机器人上——这时三件互不相干的事会同时出错:操作系统选错了,工作空间在树莓派的内存里编译不过,笔记本电脑还看不到机器人的节点。严格来说这些都不是 ROS 的问题,这也是官方教程从不提醒你的原因。
本文带你从一张空白 SD 卡走到 ros2 topic list:在笔记本电脑上就能看到机器人上运行的节点。
先选操作系统,再选板子
ROS 2 的二进制包是为 Ubuntu 构建的,而不是 Raspberry Pi OS。Raspberry Pi OS 基于 Debian,apt 仓库里根本没有这些包,任何变通方案——从源码编译、Docker、conda——都会在你写下第一行机器人代码之前耗掉数小时。
所以请安装 Ubuntu Server 24.04 LTS(64 位),并搭配与之对应的 ROS 2 版本。撰写本文时这个组合是 Jazzy Jalisco(支持到 2029 年);阅读时请以安装页面上的当前版本为准,全文替换版本名即可。
烧录系统时要做对三件事:
- 64 位。 ROS 2 只有 arm64 包,没有 armhf 包。
- Server 版,而非 Desktop 版。 机器人没有显示器,桌面环境会吃掉本该留给视觉的内存。
- 4 GB 版 Pi 4 或 Pi 5。 2 GB 的板子运行 ROS 2 没问题,但编译起来会很吃力。
用 apt 安装 ROS 2
添加 ROS 2 的 apt 源,然后安装。仓库配置在不同版本之间会变,所以这里讲的是思路,而不是要背下来的片段:
sudo apt update && sudo apt install -y software-properties-common curl
sudo add-apt-repository universe
# Fetch and install the package that registers the ROS 2 apt repository and key.
export ROS_APT_SOURCE_VERSION=$(curl -s \
https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest \
| grep -F "tag_name" | awk -F\" '{print $4}')
curl -L -o /tmp/ros2-apt-source.deb \
"https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb"
sudo apt install -y /tmp/ros2-apt-source.deb
sudo apt update
sudo apt install -y ros-jazzy-ros-base ros-dev-tools
注意是 ros-base,不是 desktop。desktop 元包会把 RViz 和演示 GUI 一起装进来,无头机器人永远用不上,在 SD 卡上安装还要花很长时间。可视化属于你的笔记本电脑,在那边安装完整的 ros-jazzy-desktop 即可。
ros-dev-tools 提供 colcon 和 rosdep。初始化一次 rosdep:
sudo rosdep init # skip if it says the file already exists
rosdep update
先 source 底层环境,再 source 你的覆盖层
在 source 环境之前,ROS 2 什么都做不了,而大多数早期困惑都出在这里。
source /opt/ros/jazzy/setup.bash
这就是底层环境(underlay)——已安装的发行版。你自己构建的工作空间则是叠在它上面的覆盖层(overlay)。source 一个覆盖层时,会隐式带上它构建时所依赖的底层环境。
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
把底层环境写进 .bashrc 是标准且安全的做法。把你自己的覆盖层写进去则是要尽早避开的陷阱:当你有两个工作空间,或者换了发行版重新编译的那一天,每个新终端都会悄悄载入过期环境,你会为一个幽灵调试一下午。覆盖层要显式 source,每个需要它的终端单独执行。
创建工作空间和功能包
colcon 工作空间就是一个里面带 src/ 的目录,规范就这么简单。
mkdir -p ~/ros2_ws/src && cd ~/ros2_ws
ros2 pkg create --build-type ament_python --license Apache-2.0 my_robot
然后构建并叠加上去:
cd ~/ros2_ws
rosdep install --from-paths src --ignore-src -y
colcon build --symlink-install
source install/setup.bash
一定要用 --symlink-install。 它把你的 Python 文件链接进安装空间,而不是复制进去,这样改完节点下次运行即生效,不用重新构建。在树莓派上,重新构建很慢,这就是“能迭代”和“只能等待”的区别。
构建完成后,~/ros2_ws 里除了 src/ 还会有 build/、install/ 和 log/。只有 src/ 属于版本控制。
不耗尽内存地构建
colcon build 会并行编译,每个核心一个进程,而每个 C++ 编译进程都可能吃掉近 1 GB 内存。在四核树莓派上就是四个重型编译器同时开工,构建会死于“进程被终止”的报错——或者整块板子直接卡死。
MAKEFLAGS="-j1" colcon build --parallel-workers 1 --symlink-install
慢一些,但能编完。纯 Python 包没有这个问题;一旦开始编译含 C++ 的包就会中招。加几 GB 交换分区也行,不过在 SD 卡上会慢得难受。
从笔记本电脑连接机器人
ROS 2 没有主节点。节点通过网络上的 DDS 发现机制互相找到,这意味着同一局域网内的笔记本电脑和树莓派无需任何配置就能组成一张图——顺利的话。
在两台机器上设置相同的域:
export ROS_DOMAIN_ID=42 # any value 0–101; the same on every machine
域把网络隔离开。同一房间里的两台机器人在默认域下会听到彼此的话题并互相干扰,用这种难忘的方式学会这个设置的作用。
在树莓派上运行一个 talker,然后从笔记本电脑验证:
ros2 topic list
ros2 node list
ros2 topic echo /chatter
让发现机制在 Wi-Fi 下存活
树莓派明明在发布,ros2 topic list 却什么都看不到——这是最常见的 ROS 2 网络故障,而几乎从来不是 ROS 的错。
发现机制用的是组播,而很多接入点根本不转发组播。 家用路由器经常限速或丢弃组播,“客户端隔离”或访客网络模式更是直接阻断客户端之间的流量。节点在运行,能介绍它们互相认识的数据包却到不了。
按这个顺序排查:
- 同一子网吗? 笔记本电脑连 5 GHz、树莓派连访客 SSID,看起来地址接近,实际根本不在同一网络。
ROS_DOMAIN_ID相同吗,而且是在真正运行节点的那个终端里设置的吗? 一个终端里的export到不了另一个终端,更到不了 systemd 服务。- 试试有线。 有线通而无线不通,就证明是组播问题,不是 ROS 问题。
- 检查防火墙。 两端执行
sudo ufw allow in proto udp from <subnet>,或测试时先关掉。 - 回退到发现服务器。 Fast DDS 可以用已知端点代替组播——在两台机器上把
ROS_DISCOVERY_SERVER设为机器人地址。在你控制不了的网络上,这是最可靠的答案。
还有一条,很隐蔽:保持时钟同步。 TF2 要比较跨机器的时间戳,而没有 RTC 的树莓派启动时时间是错的。两端都装上 chrony,否则每个坐标变换查询都会报外推错误,看起来像 ROS 的缺陷。
开机自启机器人的节点
一台每次都要 SSH 上去启动的机器人,等于一台关着的机器人。用 systemd 服务解决:
# /etc/systemd/system/my-robot.service
[Unit]
Description=Robot bringup
After=network-online.target
[Service]
User=ubuntu
Environment=ROS_DOMAIN_ID=42
ExecStart=/bin/bash -lc 'source /opt/ros/jazzy/setup.bash && \
source /home/ubuntu/ros2_ws/install/setup.bash && \
ros2 launch my_robot bringup.launch.py'
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now my-robot
journalctl -u my-robot -f
Environment= 这一行比看起来重要得多。systemd 不读你的 .bashrc,服务继承不到交互终端里的任何 ROS 变量——这正是“SSH 上去好好的,开机自启就静默”的原因。
出问题时
| 现象 | 通常原因 |
|---|---|
ros2: command not found |
这个终端里从没 source 过底层环境 |
笔记本电脑上 ros2 topic list 为空 |
组播被拦截,或 ROS_DOMAIN_ID 不一致 |
| 构建到一半被杀,没有报错 | 内存不足——限制并行 worker 数量 |
| 改了代码没效果 | 构建时没加 --symlink-install,或覆盖层没重新 source |
| 构建完找不到功能包 | 覆盖层没 source,或在构建完成前就 source 了 |
| TF 报向未来外推的错误 | 机器之间时钟不同步 |
| SSH 上去正常,开机自启就死 | systemd 服务里缺了 ROS 环境变量 |
环境正常之后,下一个问题就是往里面放什么——这是设计问题而非安装问题。节点、话题、服务与动作讲通信模式的选择,TF2 坐标系讲接下来最容易踩坑的部分。
探索关联图谱
本教程所属的搭建
包含本教程的项目和学习路线。
延伸阅读