☰
deepin下用Docker搭建ROS2开发环境完整指南
2026/10/6 3:53:40 网站建设 项目流程

先说个背景。我这台deepin本来是用来日常办公的,后来想折腾机器人开发,就动了装ROS2的心思。一开始我很天真,直接在系统里照着Ubuntu的教程用apt装ros-humble-desktop,结果装到一半提示要升级一堆系统基础库,我没多想就回车,然后重启之后桌面直接进不去了。那次之后我彻底明白了一个道理:在deepin上折腾ROS2,最好别碰系统原生的依赖树,老老实实走docker才是正路。

现在这套方案我已经用了挺长时间了,deepin系统本身干干净净,ROS2环境全在容器里,搞坏了就重来,再也不怕把系统搞崩。这篇文章就把完整的流程记录下来:deepin上怎么装docker、怎么选ROS2镜像、容器里怎么把rviz2和gazebo的画面调出来,以及我踩过的那些坑。如果你也是deepin用户,又想玩ROS2,这篇文章可以直接照着抄。

1. 为什么非得绕Docker这一圈:ROS2版本与系统版的硬绑关系

1.1 ROS2不是"装个包就行":版本和Ubuntu是一一绑定的

先看一组ROS2官方版本支持表。ROS2每个发行版都绑定一个Ubuntu LTS版本,这个绑定关系是写死在构建脚本里的:

ROS2版本对应Ubuntu版本发布时间维护状态
Foxy FitzroyUbuntu 20.04 (Focal)2020年6月已EOL
Humble HawksbillUbuntu 22.04 (Jammy)2022年5月当前LTS
Iron IrwiniUbuntu 22.04 (Jammy)2023年5月已EOL
Jazzy JaliscoUbuntu 24.04 (Noble)2024年5月当前LTS

为什么会有这种硬绑定?因为ROS2的二进制包依赖某个特定版本的Boost、Python、OpenSSL、OpenCV等库,而这些库在Ubuntu版本间差异很大。官方只对指定Ubuntu版本做完整测试,所以预编译包只发对应版本。如果你用不匹配的系统版本强行装,apt会告诉你依赖冲突,你只能看到一串又一串"需要但无法安装"的报错。

deepin本质上基于Debian系,和Ubuntu同源但库版本又不一样。ROS2官方并没有发布deepin版本,所以要装只能引入Ubuntu源,而这一引入,就把整个系统的APT依赖树搅浑了。这就是我在开头说的桌面崩掉事故的根本原因。

1.2 在deepin上硬装的真实下场

可能有人会说:"我手动从源码编译ROS2不行吗?" 可以,但代价非常大。源码编译一个humble-desktop全量版本,光是依赖就能折腾一整天,而且编译过程中可能因为deepin的编译器版本、CMake版本和Ubuntu镜像不一致,出现各种奇奇怪怪的编译错误。更别提编译到一半发现某个ROS2包依赖的系统库版本不对,又得手动去搞。

网上还有那种一键安装脚本,在Ubuntu上确实方便,但拿到deepin上跑,脚本会识别系统信息,发现不是Ubuntu就拒绝执行,或者干脆硬着头皮去改系统的Python和pip版本,最后把系统搞得更乱。我见过不止一次有人把系统Python换成3.10之后,整个deepin的应用商店和桌面组件全部罢工。

所以我最终放弃直接在deepin上装ROS2,转向docker。这是目前最稳的路:不碰宿主系统的依赖树,ROS2需要什么库就装在容器里,容器用的镜像基于Ubuntu 22.04,和ROS2 humble的官方发布版本完全一致。

1.3 Docker解决的不只是"安装",还有"可复现"

除了避免系统崩溃,docker这套方案还有个很大的好处:可复现。我可以把整个ROS2环境(包括装好的turtlesim、rviz2插件、自己编译的功能包)打包成一个镜像,拷到另一台电脑上直接跑,完全不需要重新配置。对于做机器人开发的人来说,这个特性太重要了——你换电脑、换系统、带学生做实训,一套镜像全搞定。

另外,docker容器的隔离性也给了你随便折腾的底气。容器里装坏了,删掉容器重新create一份,几十秒又是一条好汉。这种试错成本是直接在deepin系统上操作没法比的。

2. deepin上装Docker Engine:源、驱动与用户组三件套

2.1 为什么不用Docker Desktop:先解开那个virtualization报错

很多从Windows转过来的朋友第一反应是装Docker Desktop,然后大概率会撞上这个报错:virtualization support not detected。这个报错的意思是Docker Desktop需要在你的机器上启用CPU虚拟化扩展(VT-x或AMD-V),它本质上是一个跑在轻量虚拟机里的方案,对宿主要求高,而且deepin对Docker Desktop的兼容性并不好。

但做ROS2开发,我们根本不需要Docker Desktop。我们用Docker Engine就够了,就是纯命令行工具,不依赖任何虚拟机技术,在deepin上非常稳。我自己一直用的是CLI方式,日常开发完全够用,甚至比Desktop更清爽、更省资源。

2.2 添加Docker源时最容易卡住的一步

deepin上装Docker Engine,最麻烦的不是执行apt install,而是添加docker官方软件源。docker官方源是按Debian版本提供apt仓库的,它需要你明确告诉它"我是Debian哪个版本"。deepin的/etc/os-release里写的版本代号是deepin自己的名字(比如amber或beige),docker源不认这个,如果你直接照抄官方文档的命令,apt update会报错说找不到Release文件。

正确做法是去看/etc/debian_version,这个文件里写的是deepin底层对应的Debian版本号。我的机器上对应的是bullseye,所以docker源要按Debian 11来配。如果你的deepin版本不同,以你机器上/etc/debian_version的值为准。

操作步骤比较简单:

# 安装依赖工具 sudo apt update sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release # 添加docker官方GPG密钥 curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # 添加docker仓库(注意bullseye换成你的debian版本代号) echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/linux/debian bullseye stable" | sudo tee /etc/apt/sources.list.d/docker.list # 刷新索引 sudo apt update

这里我刻意用了阿里云的镜像地址而不是docker官方地址,因为在国内直接访问docker官方仓库经常超时,换成镜像源能省掉很多等待时间。

2.3 安装、启动、免sudo三步走

源配好之后,安装就很直接了:

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo systemctl enable --now docker docker version

docker version能看到客户端和服务端版本号,说明服务已经正常运行。如果是第一次装,可以先跑一下sudo docker run hello-world验证整个链路能通。

最后一步是免sudo。docker CLI默认需要root权限,每次敲sudo docker很烦,而且后面挂载目录、跑容器时权限问题会很多。把当前用户加进docker组就能解决:

sudo usermod -aG docker $USER newgrp docker docker run hello-world

newgrp docker让当前终端立即生效,不用重新登录。如果重启终端之后docker还是报权限拒绝,那就是用户组没刷新成功,重新登录一次就好。

2.4 配一个国内镜像加速器,否则拉镜像会怀疑人生

docker装好后第一件要做的事是配镜像加速器。不配的话,直接拉ros镜像可能卡在下载层动弹不得。在/etc/docker/daemon.json里配置镜像源:

{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.nju.edu.cn" ] }

配好之后重启docker:

sudo systemctl restart docker

加速器的作用相当于给Docker Hub加了一层国内缓存,拉同名的镜像时直接从镜像站取,速度快很多。注意镜像站有时候会调整域名,配多个备选可以减少拉取失败的次数。

3. 拿官方镜像先跑一个最小ROS2环境

3.1 镜像版本怎么选:从Humble到Jazzy

docker官方仓库里有ROS2镜像,最常见的镜像名是ros和osrf/ros。选镜像版本的时候,跟着你实际需要的ROS2版本走即可。做机器人开发,目前我还是推荐Humble,原因很简单:生态最成熟,资料最多,绝大多数教学和开源项目都用humble。如果你的项目明确要求Jazzy或者更新的版本,再根据下表选择:

镜像标签对应ROS2版本对应Ubuntu适用场景
ros:humble-ros-core-jammyHumble22.04最小核心,适合网关类设备
ros:humble-ros-base-jammyHumble22.04基础通信+常用工具,建议日常开发用这个
ros:humble-desktopHumble22.04带图形工具(rviz2等),体积大
ros:jazzy-ros-base-nobleJazzy24.04新项目、新硬件平台
ros:jazzy-desktopJazzy24.04新平台带桌面工具

我实际用的是ros:humble-ros-base,因为desktop版本体积太大,而rviz2这些图形工具自己可以按需安装,用到什么装什么反而更清爽。

3.2 docker run参数逐个拆解

启动一个ROS2容器,我会用这样一串参数:

docker run -it --name ros2_humble \ --network host \ -e DISPLAY=$DISPLAY \ -e LIBGL_ALWAYS_SOFTWARE=1 \ -e TZ=Asia/Shanghai \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -v $HOME/ros2_ws:/ros2_ws \ ros:humble-ros-base

逐个说说为什么这么配:

  • --network host:ROS2的通信依赖DDS,DDS默认用UDP多播做节点发现。用host网络模式,容器直接共享deepin主机的网络栈,多播包畅通无阻,节点互相发现零障碍。这步是ROS2容器能否正常通信的关键。
  • -e DISPLAY=$DISPLAY:把宿主的图形显示环境变量传进容器,让rviz2知道往哪里画窗口。
  • -e LIBGL_ALWAYS_SOFTWARE=1:强制OpenGL走软件渲染。deepin上如果显卡驱动没处理好,rviz2很可能黑屏崩溃,先从软渲染跑通,后面再优化硬件加速。
  • -e TZ=Asia/Shanghai:容器默认UTC时区,不设的话日志时间差8小时,后面排错时容易混淆。
  • -v /tmp/.X11-unix:/tmp/.X11-unix:挂载X11的socket文件,这是图形程序从容器连回宿主桌面的通道。
  • -v $HOME/ros2_ws:/ros2_ws:共享工作目录,宿主机上写代码,容器里编译运行。

注意我加了--name但没加--rm,因为我想保留这个容器,容器里安装的额外包不会丢。如果只想临时用用,加--rm更干净。

3.3 容器里的第一行命令:source

启动容器后,你会直接进入一个root shell。第一件事是加载ROS2环境变量:

source /opt/ros/humble/setup.bash ros2 --help

如果没有这步source,直接敲ros2百分百是command not found。为了方便,把source写进容器的bashrc:

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc

之后再进容器就不用每次手动source了。这也是新手最容易忽略的细节——网上很多教程直接让你敲ros2命令,却没人告诉你前提是source环境。

3.4 把启动命令固化成脚本

手动敲docker run那一长串参数终究不是长久之计。我在deepin主机的~/bin/下放了一个启动脚本:

#!/bin/bash # 启动ROS2 humble容器 xhost +local:root docker start ros2_humble 2>/dev/null || docker run -it --name ros2_humble \ --network host \ -e DISPLAY=$DISPLAY \ -e LIBGL_ALWAYS_SOFTWARE=1 \ -e TZ=Asia/Shanghai \ -v /tmp/.X11-unix:/tmp/.X11-unix \ -v $HOME/ros2_ws:/ros2_ws \ ros:humble-ros-base

脚本做了一个判断:如果容器已经存在,直接docker start启动;不存在才docker run创建。xhost +local:root这句放在脚本里,省得每次都手动授权X11。

4. 画面要出来:让rviz2在容器里"看见"deepin桌面

4.1 X11授权三连:DISPLAY、xhost、/tmp/.X11-unix

容器里跑图形程序,本质是让容器内的进程把画面画到deepin桌面上。Linux的X11显示协议走的是socket通信,进程要连上X server,必须同时满足三个条件:知道X server的地址、有访问socket的权限、能通过X server的授权校验。

对应的三个操作我已经在前面布置好了:

  1. -e DISPLAY=$DISPLAY把显示地址传进去。deepin默认是:0,代表本机的第一个显示设备。
  2. -v /tmp/.X11-unix:/tmp/.X11-unix把X11的socket目录挂进容器。
  3. 在deepin主机上执行xhost +local:root,允许本地的root用户连接X server。

xhost +local:root这条命令每次重启机器后都需要重新执行,所以我把它直接写进了启动脚本里。如果你发现容器里启动rviz2时报cannot open display,优先检查这三项是否都到位了。

4.2 显卡渲染的两条路:软渲染与挂载/dev/dri

图形界面能打开,不代表渲染正常。deepin的机器配置差异很大,有的用N卡闭源驱动,有的用Intel核显。N卡驱动在容器里很容易出问题,因为容器内没有宿主的X驱动和CUDA库,直接调用GPU渲染会黑屏或者花屏。

我的建议是先走软渲染:LIBGL_ALWAYS_SOFTWARE=1这个环境变量会让OpenGL全部走CPU渲染。缺点是帧率低,但rviz2这种界面程序不是游戏,CPU渲染足够了。如果你做仿真(比如gazebo带模型加载),CPU渲染可能卡,那就把宿主机的GPU设备挂进容器:

docker run ... --device=/dev/dri ...

/dev/dri是Linux的DRM设备节点,挂进去之后容器里的用户态驱动就可以直接访问GPU了。这个方法对Intel核显和AMD卡效果很好,N卡反而没有在X11下直接挂CUDA走OpenGL的通用做法,还是软渲染更省心。

4.3 实测:在容器里跑turtlesim和rviz2

验证图形链路是否通了,最轻量的方法是跑turtlesim。在容器里执行:

apt update && apt install -y ros-humble-turtlesim ros2 run turtlesim turtlesim_node

如果一切正常,deepin桌面上会弹出一个小海龟的窗口,你甚至可以再开一个终端用键盘控制它。这个看似简单的窗口能弹出来,说明DISPLAY、X11授权、渲染环境三个环节全部打通,后面跑rviz2换汤不换药。

接着验证rviz2:

apt install -y ros-humble-rviz2 rviz2

rviz2的界面比turtlesim复杂得多,如果能正常显示3D场景且拖动不崩溃,那这套容器图形方案就完全成立了。

5. 让容器和deepin主机真正协同工作:网络、共享目录与用户映射

5.1 host网络模式为什么是ROS2的默认首选

ROS2的通信机制和ROS1不同,它底层用DDS,DDS的节点发现靠UDP多播。默认情况下,同一个局域网内的ROS2节点会通过多播地址互相宣告存在。这个特性在家用路由器环境里很方便,但到了容器就要注意:docker默认的bridge网络模式会隔离多播包,两个容器用默认网络跑ROS2节点,经常互相看不见。

用--network host就一劳永逸了:容器和deepin主机共享同一个网络栈,多播包直接走宿主物理网卡,节点发现和通信跟直接在宿主机上跑没区别。这也是为什么我强烈建议开发阶段用host网络模式。

代价是端口隔离失效。比如容器里起了一个监听固定端口8080的服务,它就占用了宿主机的8080。ROS2用的端口是动态分配的,冲突概率很低,但如果你在容器里跑其他Web服务,心里得有个数。

5.2 用共享目录做开发工作流

容器里写代码最大的痛点是文件隔离。我通常在deepin主机上用VS Code打开~/ros2_ws写代码,容器里编译运行,两者通过挂载共享同一份文件。这样文件操作在宿主侧,权限管理直观,还能用宿主上熟悉的编辑器。

共享目录的工作流需要一套固定的操作:

# 在deepin主机创建并进入工作目录 mkdir -p ~/ros2_ws/src cd ~/ros2_ws # 在容器内进入同样的路径(因为挂载,两边看到的是同一份文件) cd /ros2_ws colcon build --symlink-install source install/setup.bash

我在容器里还装了个ros-humble-demo-nodes-cpp之类的示例包,确认编译后的install目录能被source。--symlink-install这个参数很实用,它用符号链接替代文件拷贝,改完源码不用重新编译,对频繁调试特别友好。

5.3 容器内用户的坑与解决

默认容器里是root用户,root在共享目录里创建的文件,宿主侧属主也是root。这会导致一个很尴尬的情况:你在deepin主机上想改容器里生成的文件,没有sudo权限就得干瞪眼。

我的解决办法是让容器内用户和宿主用户保持一致的UID。deepin主机上先看自己的UID:

id -u

然后用-u参数指定容器内以哪个UID运行:

docker run -it \ --network host \ -u $(id -u):$(id -g) \ -v $HOME/ros2_ws:/ros2_ws \ ros:humble-ros-base

这样容器内创建的文件UID和宿主用户一致,宿主侧随手就能改。但要注意,容器内很多系统操作需要root权限,用普通UID运行时会受限。我的折中方案是:日常编译和运行节点用-u指定普通用户,需要装包时才进root容器。实际操作里我会准备两个启动脚本,一个是root版用来apt install,一个是普通用户版用来日常开发,简单粗暴但很好用。

6. 实战验证与常见翻车点排查

6.1 跑通talker/listener:验证核心链路

图形环境验证过了,再验证通信链路。开两个终端进入同一个容器(或者分别进两个容器,host网络下都一样),一边跑发布者,一边跑订阅者:

# 终端A ros2 run demo_nodes_cpp talker # 终端B ros2 run demo_nodes_cpp listener

listener能持续打印I heard: Hello World,说明DDS通信链路正常。接着可以看话题列表:

ros2 topic list ros2 topic echo /chatter

如果topic能看到但订阅没数据,大概率是DDS发现协议出了问题,按下面的排查表处理。

6.2 全身排查:七类典型故障及修复

我把实操中容易踩的坑整理成一张表,按现象排错就行:

现象根因修复方案
docker: permission denied用户不在docker组或组未生效newgrp docker或重新登录
拉镜像超时未配镜像加速器修改/etc/docker/daemon.json后重启
ros2: command not found未source环境source /opt/ros/humble/setup.bash
两个容器互相看不到节点DDS多播被bridge网络隔离改用--network host
rviz2报cannot open displayX11授权或DISPLAY未传递检查xhost +local:root和-e DISPLAY
rviz2黑屏或崩溃GPU渲染问题设LIBGL_ALWAYS_SOFTWARE=1软渲染
容器时间差8小时未设时区-e TZ=Asia/Shanghai重新运行

这里面最隐蔽的是DDS多播问题,因为报错不直观。多个容器跑起来后,ros2 node list只能看到自己,别人一概报"NODE NOT FOUND"。新手很难想到是网络模式的问题,我还见过有人怀疑是防火墙,在deepin上折腾半天,最后发现只是docker bridge网络把多播包挡住了。换成host网络模式立刻解决。

6.3 把容器固化下来:docker commit与Compose

开发过程中你会装一堆额外的包,比如turtlesim、rviz2、gazebo插件、自己的功能包。如果容器删了,这些全得重装,所以务必在环境搭好后固化镜像:

docker commit ros2_humble ros2_humble:dev-v1

docker commit会把当前容器状态打包成新镜像,下次直接用这个镜像创建容器,所有已装包都在。我习惯每稳定一个阶段就commit一次,版本号递增,出问题可以回滚。

更现代的做法是写docker-compose.yml,把启动参数文件化:

services: ros2: image: ros:humble-ros-base network_mode: host environment: - DISPLAY=${DISPLAY} - LIBGL_ALWAYS_SOFTWARE=1 - TZ=Asia/Shanghai volumes: - /tmp/.X11-unix:/tmp/.X11-unix - $HOME/ros2_ws:/ros2_ws stdin_open: true tty: true command: bash

用docker compose up -d一条命令启动,参数全在文件里,换机器也方便。我个人觉得,docker compose虽然比纯命令多一步学习成本,但长期收益非常大,尤其是项目环境要给别人复现的时候。

最后再分享一个实际工作中的体会:deepin下这套docker+ROS2方案,真正舒服的地方在于deepin桌面本身对开发环境干预少、系统干净,容器跑起来几乎感觉不到性能损耗。我用了半年多,系统一直稳定,再也没出现依赖风暴把桌面搞崩的情况。如果你是deepin用户又想在机器人开发这条路上走远一点,强烈建议花一晚上把docker环境搭好,后面你会感谢这个决定的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询