ROS 多版本管理 —— 一台机器跑多个 ROS 的完整方案
ROS 多版本管理 —— 一台机器跑多个 ROS 的完整方案
适用场景:团队 / 个人同时维护 ROS 1 与 ROS 2 项目,或需要运行不同发行版(如 Humble 与 Jazzy)的 ROS 2 项目,但只有一台开发机。
核心结论先行:
- ROS 1 与 ROS 2 可以装在同一台机器上,通过
source不同的 setup 文件切换使用。 - ROS 2 不同发行版(Humble / Jazzy)不建议直接共存在宿主机,推荐用 Docker 容器 隔离。
- 最干净、最可复现的通用方案是 Docker + 容器化开发,环境互不干扰,项目间一键切换。
目录
- 为什么 ROS 版本会”打架”
- ROS 发行版与 Ubuntu 的对应关系
- 关键概念:ROS 环境变量体系
- 方案一:环境切换法(ROS 1 + ROS 2 共存)
- 方案二:Docker 容器化(推荐,支持任意版本组合)
- 方案三:Python 工具链隔离
- ROS 1 与 ROS 2 互通:ros1_bridge
- 日常检查与排查命令
- 方案对比总结
一、为什么 ROS 版本会”打架”
ROS 通过环境变量来决定当前终端使用哪一套工具链和库。当你 source 一个发行版的 setup 文件时,实际修改了这些环境变量:
| 环境变量 | 作用 | 示例(Jazzy) |
|---|---|---|
ROS_DISTRO |
当前 ROS 发行版标识 | jazzy |
ROS_VERSION |
ROS 大版本号 | 2 |
AMENT_PREFIX_PATH |
ROS 2 包查找路径 | /opt/ros/jazzy |
CMAKE_PREFIX_PATH |
编译时依赖查找路径 | /opt/ros/jazzy |
PYTHONPATH |
Python 模块查找路径 | /opt/ros/jazzy/lib/python3.12/site-packages |
LD_LIBRARY_PATH |
动态库查找路径 | /opt/ros/jazzy/lib |
PATH |
可执行文件查找路径 | /opt/ros/jazzy/bin |
RMW_IMPLEMENTATION |
中间件实现(ROS 2) | rmw_fastrtps_cpp |
冲突的本质:
- 两个版本的 setup 文件不能同时 source,否则后面的会覆盖前面的
PYTHONPATH/LD_LIBRARY_PATH/CMAKE_PREFIX_PATH,导致找错库、找错包。 - ROS 1(Noetic)是单进程架构;ROS 2 是 DDS 分布式架构,两者通信协议完全不同。
- ROS 2 各发行版的系统级 Python 工具(
colcon、rosdep、vcs)通过 apt 安装在同一位置,Humble 和 Jazzy 会互相覆盖,这是它们难以在宿主机共存的直接原因。
二、ROS 发行版与 Ubuntu 的对应关系
| ROS 版本 | 类型 | 官方支持的系统 | Python |
|---|---|---|---|
| Noetic | ROS 1 | Ubuntu 20.04 (Focal) | Python 3.8 |
| Foxy | ROS 2 | Ubuntu 20.04 (Focal) | Python 3.8 |
| Humble | ROS 2 LTS | Ubuntu 22.04 (Jammy) | Python 3.10 |
| Iron | ROS 2 | Ubuntu 22.04 (Jammy) | Python 3.10 |
| Jazzy | ROS 2 LTS | Ubuntu 24.04 (Noble) | Python 3.12 |
关键点:每个 ROS 发行版与特定 Ubuntu 版本、特定 Python 版本强绑定。 一台宿主机的 Ubuntu 版本一旦固定,能直接通过 apt 安装的 ROS 2 发行版基本只有一个。 想要运行其它发行版,要么源码编译(费时且易出问题),要么用 Docker 跑对应系统的容器(推荐)。
三、关键概念:ROS 环境变量体系
每个 ROS 环境都有各自的 setup 文件:
/opt/ros/<distro>/setup.bash # ROS 1 和 ROS 2 通用入口
/opt/ros/<distro>/local_setup.bash # 当前工作空间安装的环境
切换到某个 ROS 版本,本质就是在终端里执行对应的 source。不同终端相互独立,因此:
- 终端 A source 了 Jazzy,终端 B source 了 Humble,两者互不影响。
- 同一终端内反复 source 不同版本,会残留/污染环境,建议开新终端再切换。
检查当前环境的黄金命令:
echo $ROS_DISTRO # 显示当前发行版
printenv | grep -E 'ROS|AMENT|COLCON' # 查看全部相关环境变量
四、方案一:环境切换法(ROS 1 + ROS 2 共存)
适用于 ROS 1 与 ROS 2 在同一 Ubuntu 上共存(例如 Ubuntu 20.04 同时装 Noetic 与 Foxy), 因为 ROS 1 与 ROS 2 的安装路径、工具链基本不重叠,可以真正装在同一台宿主机上。
4.1 安装
# 安装 ROS 1 Noetic(Ubuntu 20.04)
sudo apt install ros-noetic-desktop
# 安装 ROS 2 Foxy(Ubuntu 20.04,可与 Noetic 共存)
sudo apt install ros-foxy-desktop
注意:如果你在 Ubuntu 22.04/24.04 上想同时用 ROS 1 与 ROS 2,ROS 1 Noetic 没有官方二进制包, 需要源码编译或使用容器,因此更推荐直接跳到 Docker 方案。
4.2 在 .bashrc 中配置快速切换
不要在 .bashrc 里同时 source 两个版本,而是定义切换函数,按需调用:
# 编辑 ~/.bashrc
cat >> ~/.bashrc <<'EOF'
# ===== ROS 版本切换 =====
export ROS_WS=~/ros_ws # 你的工作空间路径
use_ros1() {
# 若当前已 source 过 ROS 2,建议开新终端或先执行 env -i bash
source /opt/ros/noetic/setup.bash
if [ -f "$ROS_WS/devel/setup.bash" ]; then
source "$ROS_WS/devel/setup.bash" # ROS 1 使用 catkin 生成 devel
fi
echo ">>> 已切换到 ROS 1 (Noetic)"
}
use_ros2() {
source /opt/ros/foxy/setup.bash
if [ -f "$ROS_WS/install/setup.bash" ]; then
source "$ROS_WS/install/setup.bash" # ROS 2 使用 colcon 生成 install
fi
echo ">>> 已切换到 ROS 2 (Foxy)"
}
EOF
source ~/.bashrc
4.3 使用方式
use_ros1 && roscore & # 运行 ROS 1 master
rosrun turtlesim turtlesim_node # 运行 ROS 1 节点
# ---------- 另开一个终端 ----------
use_ros2 && ros2 run turtlesim turtlesim_node # 运行 ROS 2 节点
4.4 重要注意事项
- 不要同时
sourceROS 1 与 ROS 2 的 setup 文件,PYTHONPATH/LD_LIBRARY_PATH会互相污染。 - ROS 1 命令(
rospack、rosrun、catkin_make)与 ROS 2 命令(ros2、colcon)分属不同工具链。 - 同一终端内切换版本后,最好
echo $ROS_DISTRO确认,必要时直接开新终端。
五、方案二:Docker 容器化(推荐,支持任意版本组合)
为什么最推荐:每个 ROS 发行版对应不同的 Ubuntu/Python,宿主机只能装一个; Docker 把”操作系统 + ROS 版本 + 依赖”整体打包,彻底解决所有冲突,且可复现、可分发。
5.1 常用官方镜像
| 需求 | 镜像 |
|---|---|
| ROS 1 Noetic | ros:noetic-ros-base / ros:noetic-ros-core |
| ROS 2 Humble | osrf/ros:humble-desktop / osrf/ros:humble-ros-base |
| ROS 2 Jazzy | osrf/ros:jazzy-desktop / osrf/ros:jazzy-ros-base |
| ROS 2 Foxy | osrf/ros:foxy-desktop |
5.2 基本运行命令(带 GUI 与共享目录)
# 允许 X11 转发(宿主机执行一次即可)
xhost +local:docker
# 运行 Humble 容器
docker run -it --rm \
--name ros_humble \
--network host \
-e DISPLAY=$DISPLAY \
-e LIBGL_ALWAYS_SOFTWARE=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ~/humble_ws:/root/ws \ # 挂载项目目录
-v /dev:/dev --privileged \ # 访问 USB / 串口等硬件(按需)
osrf/ros:humble-desktop
GUI 透传说明(详细方案见 5.5 运行仿真:图形界面(GUI)完整解决方案):
- X11:
-e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix- Wayland(新 Ubuntu):
-e WAYLAND_DISPLAY=$WAYLAND_DISPLAY -v $XDG_RUNTIME_DIR:/tmp/runtime-$USER -e XDG_RUNTIME_DIR=/tmp/runtime-$USER- 若 RViz/Gazebo 黑屏或闪退,加
-e LIBGL_ALWAYS_SOFTWARE=1(软件渲染)。
5.3 在容器内构建与运行
# 进入容器后(bash 交互)
source /opt/ros/humble/setup.bash
cd /root/ws
colcon build --symlink-install
source install/setup.bash
ros2 launch my_pkg my_launch.launch.py
5.4 用 docker-compose 管理多版本项目(推荐)
写一个 docker-compose.yml,为每个项目定义独立容器:
version: "3.8"
services:
humble:
image: osrf/ros:humble-desktop
container_name: ros_humble_dev
network_mode: host
environment:
- DISPLAY=${DISPLAY}
- LIBGL_ALWAYS_SOFTWARE=1
- ROS_DOMAIN_ID=42
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- ./humble_ws:/root/ws
stdin_open: true
tty: true
jazzy:
image: osrf/ros:jazzy-desktop
container_name: ros_jazzy_dev
network_mode: host
environment:
- DISPLAY=${DISPLAY}
- ROS_DOMAIN_ID=43 # 不同域,避免与 humble 容器互相发现
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- ./jazzy_ws:/root/ws
stdin_open: true
tty: true
docker compose up -d humble # 启动 Humble 容器
docker compose up -d jazzy # 启动 Jazzy 容器
docker exec -it ros_humble_dev bash
docker exec -it ros_jazzy_dev bash
Tips:
- 给不同版本容器设置不同的
ROS_DOMAIN_ID,避免两者 DDS 互相发现。- 用
--network host便于容器内与宿主机/其它容器通信。
5.5 运行仿真:图形界面(GUI)完整解决方案
核心问题:容器默认是”无头”(headless)的,没有显示器。要运行 Gazebo / RViz / rqt 等仿真 GUI,必须把图形环境「透传」进容器。根据宿主机是否有显示器、是否需要硬件加速,分为以下四种方案。
5.5.1 方案 A:X11 转发(本地开发机,最常用)
适用于宿主机自带显示器(桌面机 / 带屏的开发板)。原理:容器把窗口绘制请求转发给宿主机的 X Server 显示。
# ① 宿主机允许容器访问 X Server(每次重启后执行一次即可)
xhost +local:docker
# 更精确写法:xhost +local:root (因为容器内用户通常是 root)
# ② 启动容器,带上 X11 透传参数
docker run -it --rm \
--name ros_humble \
--network host \
-e DISPLAY=$DISPLAY \
-e QT_X11_NO_MITSHM=1 \
-e LIBGL_ALWAYS_SOFTWARE=1 \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ~/.Xauthority:/root/.Xauthority:rw \
-v ~/humble_ws:/root/ws \
osrf/ros:humble-desktop
# ③ 容器内启动仿真
source /opt/ros/humble/setup.bash
gazebo # 或 ros2 launch <pkg> <launch>.launch.py
rviz2
参数说明:
| 参数 | 作用 |
|---|---|
-e DISPLAY=$DISPLAY |
把宿主机的显示编号(如 :0)传进容器 |
-v /tmp/.X11-unix:/tmp/.X11-unix |
共享 X Server 的 Unix socket |
-v ~/.Xauthority:/root/.Xauthority:rw |
容器内以 root 运行需带授权文件,否则报 No protocol specified |
-e QT_X11_NO_MITSHM=1 |
解决 Qt 程序在容器内的共享内存报错 |
-e LIBGL_ALWAYS_SOFTWARE=1 |
软件渲染(mesa),无 GPU 驱动时防黑屏闪退(性能较差) |
5.5.2 方案 B:VNC 无头方案(服务器 / 无显示器 / 远程访问)
适用于没有显示器的服务器,或需要远程看仿真画面的场景。思路:在容器内跑一个虚拟显示器(Xvfb)+ 轻量桌面 + VNC 服务,通过 VNC 客户端或浏览器访问。
① 在 Dockerfile 中预装 GUI + VNC(在基础 ROS 镜像上追加):
FROM osrf/ros:humble-desktop
RUN apt-get update && apt-get install -y \
xvfb x11vnc fluxbox xterm \
mesa-utils dbus-x11 \
&& mkdir -p /root/.vnc \
&& x11vnc -storepasswd 123456 /root/.vnc/passwd
ENV DISPLAY=:1
② 启动容器并拉起 VNC 服务:
docker run -it --rm \
--name ros_humble_gui \
-p 5900:5900 \
-p 6080:6080 \
osrf/ros:humble-desktop \
bash -c "Xvfb :1 -screen 0 1600x900x24 & \
fluxbox & \
x11vnc -display :1 -forever -usepw -rfbport 5900 & \
source /opt/ros/humble/setup.bash && \
bash"
③ 连接方式(二选一):
方式一:VNC 客户端连接 <宿主机IP>:5900,密码 123456
方式二:容器内再装 noVNC,浏览器访问 http://<宿主机IP>:6080/vnc.html(无需客户端)
进入 VNC 桌面后,在里面打开终端执行 gazebo / rviz2 即可看到仿真画面。
5.5.3 方案 C:Wayland 直通(新 Ubuntu 24.04+)
若宿主机登录时选择了 Wayland 会话:
docker run -it --rm \
--network host \
-e WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
-v $XDG_RUNTIME_DIR:/tmp/runtime-$USER \
-e XDG_RUNTIME_DIR=/tmp/runtime-$USER \
...
实际经验:多数 ROS 工具(Gazebo / RViz / Qt 应用)是 X11 程序,在 Wayland 下通常通过 XWayland 兼容层运行,所以绝大多数场景直接用 方案 A 的 X11 转发即可。Wayland 原生直通主要用于少数纯 Wayland 客户端,不必强求。
5.5.4 方案 D:GPU 硬件加速(仿真流畅的关键)
Gazebo / RViz 是 3D 应用,LIBGL_ALWAYS_SOFTWARE=1 软件渲染会非常卡。要流畅仿真,必须把 GPU 透传给容器。
① NVIDIA 独立显卡(x86 桌面机):
# 宿主机安装 NVIDIA Container Toolkit(一次)
sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
# 启动容器时加 --gpus 参数
docker run -it --rm --gpus all \
--network host \
-e DISPLAY=$DISPLAY \
-e NVIDIA_DRIVER_CAPABILITIES=all \
-v /tmp/.X11-unix:/tmp/.X11-unix \
osrf/ros:humble-desktop
② Jetson / ARM64 开发板(如你的设备):
Jetson 的 GPU 依赖专用驱动(不能直接用 --gpus all),必须使用 NVIDIA L4T 基础镜像:
# 使用 JetPack/L4T 官方 ROS 镜像(自带 GPU 与 CUDA 加速)
docker run -it --rm \
--runtime nvidia \
--network host \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v /dev:/dev --privileged \
nvcr.io/nvidia/l4t-ros:r35.4.1-ros2-humble-ros-base-l4t-r35.4.1
⚠️ ARM64 关键提示(重点):
- 官方
osrf/ros:*镜像基本只有 amd64。在 Jetson / 树莓派等 ARM64 机器上强行docker pull,会提示platform does not match,运行时走 qemu 模拟,几乎不可用。- 请改用 NVIDIA L4T 镜像,或自行基于
ubuntu:22.04(arm64)构建 —— ROS 2 官方 apt 源对 arm64 有原生二进制包,在容器内apt install ros-humble-desktop是可行且流畅的。
5.5.5 常见问题排查表
| 现象 | 原因 | 解决办法 |
|---|---|---|
No protocol specified |
Xauthority 未正确挂载 | 加 -v ~/.Xauthority:/root/.Xauthority:rw,并执行 xhost +local:docker |
could not connect to display :0 |
DISPLAY 未传或 X 未运行 |
检查 -e DISPLAY;宿主机先执行 echo $DISPLAY 确认值 |
| 黑屏 / 闪退 | 无 GPU 驱动,3D 渲染失败 | 加 LIBGL_ALWAYS_SOFTWARE=1 软件渲染,或按方案 D 透传 GPU |
| 窗口能开但非常卡 | 软件渲染吃 CPU | 使用方案 D 硬件加速;降低 Gazebo 分辨率/画质 |
| Qt 报共享内存错误 | 容器与宿主机 X 的 IPC 限制 | 加 -e QT_X11_NO_MITSHM=1 |
| Gazebo 启动即崩溃 | 3D 上下文初始化失败 | LIBGL_ALWAYS_SOFTWARE=1,或 gazebo --verbose 查看具体报错 |
一句话总结:本地有屏 → 方案 A(X11);远程/无屏 → 方案 B(VNC);要流畅 → 方案 D(GPU);ARM 板子 → 必须用 L4T 或自建 arm64 镜像。
六、方案三:Python 工具链隔离
如果你坚持在宿主机源码编译多个 ROS 2 发行版(不推荐),至少要把构建工具隔离,
因为 colcon、rosdep、vcs 等 Python 工具安装路径是共享的。
6.1 用 pipx 或 venv 隔离工具链
# 方式 A:pipx(每个工具独立环境)
pipx install colcon-common-extensions
pipx install rosdep
pipx install vcstool
# 方式 B:虚拟环境(更可控)
python3 -m venv ~/ros_tools && source ~/ros_tools/bin/activate
pip install colcon-common-extensions rosdep vcstool
6.2 源码编译不同发行版
# 以 Humble 为例(需在 Ubuntu 22.04)
mkdir -p ~/humble_src && cd ~/humble_src
wget https://raw.githubusercontent.com/ros2/ros2/humble/ros2.repos
vcs import src < ros2.repos
rosdep init && rosdep update
rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install
source install/setup.bash
注意:源码编译不同发行版需要不同 Ubuntu/Python 环境,实际中极易因系统依赖冲突而失败, 多数情况下不值得。请优先考虑 Docker。
七、ROS 1 与 ROS 2 互通:ros1_bridge
当你需要让 ROS 1 节点与 ROS 2 节点在同一台机器上互相通信时,使用 ros1_bridge。
7.1 准备工作
- ROS 1(如 Noetic)与 ROS 2(如 Jazzy)分别装在同一台宿主机或两个能互通的主机上。
- 安装 bridge:
sudo apt install ros-jazzy-ros1-bridge
7.2 启动桥接
# 终端 1:启动 ROS 1
source /opt/ros/noetic/setup.bash
roscore
# 终端 2:启动 ROS 2 并运行 bridge(先 source ROS 1 再 source ROS 2)
source /opt/ros/noetic/setup.bash
source /opt/ros/jazzy/setup.bash
ros2 run ros1_bridge dynamic_bridge
# 终端 3:验证(ROS 1 侧发布)
source /opt/ros/noetic/setup.bash
rostopic pub /chatter std_msgs/String "data: 'hello'" -r 1
# 终端 4:在 ROS 2 侧订阅
source /opt/ros/jazzy/setup.bash
ros2 topic echo /chatter
前提:bridge 所在终端先 source ROS 1 再 source ROS 2,这样 bridge 能同时找到两套库。
八、日常检查与排查命令
# 查看当前 ROS 版本
echo $ROS_DISTRO
# 查看工作空间 / 环境前缀
printenv | grep -E 'ROS|AMENT|CMAKE_PREFIX_PATH|COLCON'
# 确认当前是 ROS 1 还是 ROS 2
echo $ROS_VERSION # 1 或 2
# 环境被污染时的急救:开一个干净终端
env -i bash --noprofile --norc
source /opt/ros/jazzy/setup.bash
# Docker 中查看镜像 / 运行中的容器
docker images
docker ps -a
九、方案对比总结
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 环境切换(source) | 宿主机 ROS 1 + 同 Ubuntu 的 ROS 2 | 零额外依赖、轻量 | 只能支持宿主机对应发行版;工具链可能冲突 |
| Docker 容器化 ⭐ | 任意版本组合(Humble/Jazzy/Noetic…) | 完全隔离、可复现、易分发、支持 GUI/硬件 | 需学习 Docker;磁盘占用较大 |
| Python 工具链隔离 | 源码编译多发行版的高级用户 | 保留宿主机原生编译体验 | 系统依赖冲突多、易踩坑 |
推荐实践路线
- 日常开发:优先 Docker,为每个项目写一个
docker-compose.yml,不同项目一键切换容器。 - 宿主机 ROS 1 + ROS 2 共存:用
.bashrc切换函数(use_ros1/use_ros2),开新终端再切换。 - 跨版本通信:用
ros1_bridge(跨 ROS 1 / ROS 2)、不同ROS_DOMAIN_ID隔离 ROS 2 不同容器。 - CI / 团队协作:把 Dockerfile 提交到仓库,任何人
docker build都能得到一致环境。
参考资料:ROS 官方文档 (docs.ros.org)、OSRF Docker Hub (hub.docker.com/r/osrf/ros)、ros1_bridge 官方教程。