NVIDIA Jetson Xaiver NX通过Docker安装任意版本ROS(Jetson Container)
前言
众所周知,ROS由于对于系统软件包版本的高度依赖,导致每个大版本ROS都必须绑定着新版Ubuntu系统发布,例如:
这里只是简单列出一些,并不完整,详细可以参考:
但是某些嵌入式设备有时并没有最新的Ubuntu版本支持(没错,说的就是你NVIDIA),例如我手上这台Jetson Xavier NX,其官方提供的最新的JetPack 5.1.3
版本也仅仅停留在Ubuntu 20.04
,要想用上22.04或更高的版本,要么自己折腾,要么换新设备例如Orin系列……
因此为了在这种几乎无法更新到新版本Ubuntu发行版的设备上安装ROS2的新版本,使用Docker安装是一个更好的选择。
但这里还有一个坑,在Jetson上不推荐直接使用公共Docker Image,NVIDIA为一些常用的镜像编译了适合Jetson的版本,仓库:
配置系统环境
此部分对官方教程作简单提取翻译,但可能过时(截至2024/08/14),建议有能力者直接阅读原文教程。
检查系统版本
官方建议安装当前型号支持的最新的JetPack版本(使用官方工具刷入),目前最新版本如下:
Nano/TX1/TX2:JetPack4
Xavier:JetPack5
Orin:JetPack6
需求的最低版本:
JetPack 4.6.1+ (>= L4T R32.7.1)
JetPack 5.1+ (>= L4T R35.2.1)
JetPack 6.0 DP (L4T R36.2.0)
如果你不确定当前的JetPack版本,可以用jtop
工具看一下(安装jtop)
确定系统版本之后,就可以继续配置了,否则你可能需要重新刷一下系统镜像。
安装Jetson Containers
先把仓库克隆到本地,再执行安装:
git clone https://github.com/dusty-nv/jetson-containers
bash jetson-containers/install.sh
安装过程中会询问密码,并且安装Python依赖(通过链接到/usr/local/bin
下的文件)
注意:
git config --global https.proxy https://x.x.x.x:7890 # 替换成你代理的地址和端口
git config --global http.proxy http://x.x.x.x:7890 # 替换成你代理的地址和端口
如果你移动了
jetson-containers
仓库文件夹,你需要重新运行安装脚本如果你的系统安装在了eMMC上而不是直接运行在NVMe固态硬盘上,建议参考这部分教程将Docker移动到外部磁盘上
更改Docker默认运行时
如果要在Jetson上构建镜像,需要将默认运行时设为nvidia
,只有这样NVCC编译器和GPU才可以在docker build
过程中被使用。
编辑/etc/docker/daemon.json
,添加以下内容
{
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
},
"default-runtime": "nvidia"
}
(默认情况下,只需要添加"default-runtime"
那一行就可以了)
如果你之前没有更换过Docker国内源,推荐在这里顺便更换一下:
禁用系统桌面GUI(可选)
如果你使用的是内存较小的设备(例如JetsonNano),可以尝试禁用GNOME桌面来增加可用内存(Unity/GNOME 约 800MB,LXDE 约 250MB)
暂时禁用
sudo init 3 # 停止桌面
# 禁用桌面之后,使用 Ctrl+Alt+F1, F2, 等快捷键让当前用户登陆到终端
sudo init 5 # 重启桌面
永久禁用
sudo systemctl set-default multi-user.target # 禁用桌面开机自启
sudo systemctl set-default graphical.target # 启用桌面开机自启
我手头的Xavier NX禁用桌面自启之后,直接从1.6G左右的内存占用降低到了0.8G左右,效果还是非常明显的,尤其是这类小内存设备上,此举可以大幅提高可用内存
添加用户到Docker组
默认情况下,Ubuntu用户不属于docker
用户组,执行docker命令都需要使用sudo,这会造成一些麻烦,添加到用户组即可解决:
sudo usermod -aG docker 你的用户名
记得注销重新登录来使设置生效。
测试一下:
docker info
如果配置正确,不需要sudo
也能正常运行此命令。
设置Jetson性能模式
如果你关闭了桌面自启(或者你切换到了其他桌面环境),你就需要通过命令行工具来切换性能模式了:
sudo nvpmodel -q # 显示当前的性能模式
sudo nvpmodel -m 0 # 通常是最高性能模式
安装ROS2
拉取镜像
由于硬件SDK需要,我这里选择humble版本安装,如果你需要安装其他版本,依然可以参考此教程。
Dokcer Hub仓库:
在里面找到适合自己的版本,然后Copy命令到Jetson上执行即可。
注意:镜像结尾标明最低需要的L4T版本,请确认你的系统版本高于此版本,否则无法正常运行
启动容器
注意:经过测试发现,使用MobaXTerm以及XShell+XManager的远程X11转发方案,在Jetson重启之后都无法继续正常转发Docker内的GUI程序,建议使用VNC或NX(NoMachine)远程桌面继续下面的步骤,以及后续使用ROS2的GUI程序。
先使用jetson-containers
命令启动,而不是一开始就直接使用docker(需要添加巨量参数,而jc会自动为你做这件事):
jetson-containers run -v /主机/路径:/容器/路径 <镜像名称>
# 例如我想要:
# 启动这个镜像 dustynv/ros:humble-desktop-pytorch-l4t-r35.4.1
# 挂载 /home/jetson/ros2_ws 到容器内的 /home/workspace
# 那么命令将是:
# jetson-containers run -v /home/jetson/ros2_ws:/home/workspace dustynv/ros:humble-desktop-pytorch-l4t-r35.4.1
提示:使用jetson-containers run
命令时可以添加任何docker run
支持的参数,都会传递给docker run
执行结果:
(可以看到确实有非常多的参数……包括文件夹链接和设备挂载等)
并且,运行之后,当前终端会默认进入到这个容器内(当前身份已经变成了root@ubuntu:/#
)
但当你退出(exit
)之后,会发现这个容器直接消失了,这是因为jetson-containers默认创建的命令中带有了--rm
参数,这使得容器会在关闭之后销毁,因此我们需要修改之前jetson-containers
给出的命令,
将之前给出的命令复制下来,然后去掉--rm
,再加上一条--restart=always
,确保容器能一直运行。
建议加一个参数指定容器名称,例如--name ros2_humble
,注意必须是英文,这样后续使用exec
时会方便一些。
修改后的命令如下(不同版本Jetson/ROS/Docker镜像都有可能导致命令不同,不建议套用,仅供参考):
docker run --runtime nvidia -it --rm --network host \
--volume /tmp/argus_socket:/tmp/argus_socket \
--volume /etc/enctune.conf:/etc/enctune.conf \
--volume /etc/nv_tegra_release:/etc/nv_tegra_release \
--volume /tmp/nv_jetson_model:/tmp/nv_jetson_model \
--volume /var/run/dbus:/var/run/dbus \
--volume /var/run/avahi-daemon/socket:/var/run/avahi-daemon/socket \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /home/jetson/jetson-containers/data:/data \
--device /dev/snd \
--device /dev/bus/usb \
-e DISPLAY=:0 \
-v /tmp/.X11-unix/:/tmp/.X11-unix \
-v /tmp/.docker.xauth:/tmp/.docker.xauth \
-e XAUTHORITY=/tmp/.docker.xauth \
--device /dev/i2c-0 \
--device /dev/i2c-1 \
--device /dev/i2c-2 \
--device /dev/i2c-3 \
--device /dev/i2c-4 \
--device /dev/i2c-5 \
--device /dev/i2c-6 \
--device /dev/i2c-7 \
--device /dev/i2c-8 \
--device /dev/i2c-9 \
-v /home/jetson/ros2_ws:/home/workspace \
dustynv/ros:humble-desktop-pytorch-l4t-r35.4.1
若要再退出后再次进入容器,使用:
docker exec -it <容器ID或名称> /bin/bash
# 例如 docker exec -it ros2 /bin/bash
容器内配置
补全~/.bashrc
如果退出容器之后再进入,你可能会发现,在这里无法执行ros2
命令,按下TAB发现只有这三个命令:
root@ubuntu:/# ros
rosdep rosdep-source rosinstall_generator
这是因为默认的.bashrc
文件没有source
ROS2的setup.bash
,在初次启动的时候docker自动为你做了这件事,后续手动进入bash则没有,因此需要修改一下.bashrc
:
nano ~/.bashrc
在最底部添加:
source /opt/ros/humble/install/setup.bash
这里的路径每个ROS版本会不同,我这里安装的是humble
版本便在此路径下,如果你安装的是其他版本镜像,手动进/opt/ros
目录找一下具体的路径即可。
让设置生效(也可以退出重进):
source ~/.bashrc
这时你就能发现,ROS2的其他命令都有了:
root@ubuntu:/# ros
ros2 rosdep rosdep-source ros-discovery rosidl rosinstall_generator
重启后ROS容器无法启动
重启之后,使用docker ps -a
查看所有容器,会发现原本设置了--restart=always
的容器此时却没有启动
如果尝试使用docker start ros2
手动启动,会直接报错:
搜索了一下,发现有很多人遇到这个问题:
总结一下,解决办法就是重新创建/tmp/.docker.xauth
这个文件,但直接执行原issue solution中那几行也是不行的,这里给出一个完整脚本:
sudo rm -rf /tmp/.docker.xauth
touch /tmp/.docker.xauth
sudo xhost +si:localuser:root
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
执行完这些,就可以正常运行容器了,并且GUI程序也可以正常运行:
为了让每次开机容器都能正常启动,直接将脚本写入/etc/rc.local
,就会在docker启动之前执行这些脚本,以便让容器正常启动。
sudo nano /etc/rc.local
填入以下内容
#!/bin/bash
sudo rm -rf /tmp/.docker.xauth
touch /tmp/.docker.xauth
sudo xhost +si:localuser:root
XAUTH=/tmp/.docker.xauth
xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -
chmod 777 $XAUTH
exit 0
保存后,添加执行权限
sudo chmod +x /etc/rc.local
接下来,试着重启一下Jetson,看看容器能否自动启动:
注意:使用MobaXTerm或XShell+XManager进行远程转发的方案(从创建容器开始一直使用,因此不是$DISPLAY设置错误的问题)在重启Jetson后就无法正常转发GUI程序了(即便已经执行了上面的脚本),错误如下,暂时还没找到解决办法,但使用VNC或NX远程桌面则没有问题。
ROS教程推荐
顺带推荐一下小鱼家的ROS教程,个人觉得还是很不错的:
- 感谢你赐予我前进的力量