针对ros机器人开发同学的docker入门教程

前言
基于本文的讲解视频如下,结合视频学习效果更佳!
【TODO:录好视频之后放上视频地址】
调ros的人虽然没必要精通docker,但对docker需要有一些最基本的理解,我总结如下:
基本概念:docker是虚拟机的青春版,运行起来的青春版虚拟机称为容器(container,注意容器这个词,这个词会在本文频繁出现),因为其不虚拟硬件,只虚拟linux内核和文件系统,所以性能远高于虚拟机。
创建容器:docker run是把一个别人提供的镜像运行成一个容器(这个过程相当于类的实例化,其中镜像相当于类,容器相当于对象)。如果想让容器有特殊的功能(包括挂载设备,同步文件夹下的文件)都需要在docker run的时候配置相应的参数,一经配置,无法更改。
文件系统:docker容器内的文件系统与宿主机的文件系统“一般”是毫无关系的。至于为什么fishros创建的ros容器能访问物理机的主目录,因为docker有 volumn 机制,可完成宿主机与容器之间文件的实时同步,这个 volumn 就需要在docker run 的时候进行配置。
软件应用:docker容器内安装的应用也和宿主机互不影响,无论是通过源码编译安装还是apt二进制安装的应用都需要从头安装。
挂载USB设备:ubuntu的 /dev文件夹存放了所有的设备,只有在 docker run的时候为容器挂载设备,才能在容器中访问到这些设备。
fishros相关:fishros安装的docker能通过容器名运行容器,是对docker命令的又一次封装。
注:本文将针对上述结论进行详细讲解~

1.前提
给非root的用户uart权限,否则之后的运行都报错

2.docker指令解说
使用docker run 指令可以创建容器。
docker run是把一个镜像创建成一个容器(这个过程相当于类的实例化,其中镜像相当于类,容器相当于对象)。这里的镜像一般是别人提供好的,如ros官方的docker镜像,也可以自己制作。特殊的功能(包括挂载设备,同步目录)都需要在docker run的时候添加相应的参数。
每个容器只能被实例化一次,如果再次运行docker run,会创建一个新的容器,与之前创建的容器独立存在,互不影响。实例化后,则可通过docker exec 进入容器。
一个最平平无奇的创建容器的指令需要包括容器名与镜像名,如下:
其中的 -dit 是 -d -i -t 的结合,是一些命令行参数,这个不用管,每次都带上就行。
fishros本质上就是根据你的选择运行对应的命令行指令,fishros运行的docker创建容器指令如下,我先把他copy下来供大家观赏一下,之后将详细解析。
相比最基础的创建容器指令,这个指令添加了很多初始化配置信息。
这一长串命令涉及几条特别重要的配置:
-v volumn 通道。一般而言,docker容器内与宿主机的文件系统是单独存在,互不干扰的,而使用volumn,就可以将两者的指定文件夹内的文件实时同步,例如
-v /home/[your_username]:/home/[your_username]
这一句,把主目录直接同步到容器内了,那么你在宿主机改完代码就不需要再粘贴到容器内,在容器内也不需要想着过一会就备份一份代码了,非常方便。--device 挂载设备,
--device /dev/snd
这一句是挂载声卡的设备,其实ubuntu的所有外围设备,比如摄像头,USB设备,GPU,声卡等都在 /dev 文件夹下,/dev下的文件夹与物理意义的外围设备是一一对应的关系。把宿主机相应文件夹同步到容器内,容器就可以访问到宿主机相应的硬件设备了。-e 设置环境变量。
-e DISPLAY=unix$DISPLAY
这一句是专门和显示相关的,指定容器内显示的相关端口,如果不加这一句,就只能在容器中运行命令行程序,无法打开容器中的 gedit rviz gazebo ros小乌龟等窗口形式的应用程序。加这一句之后还需要每次开机在宿主机的终端里运行一句xhost +local: >> /dev/null
命令,来打开宿主机的相关服务,才能在容器中正常使用窗口化应用程序。
举一反三,对于上述命令中的: -v /dev/dri:/dev/dri
,他和 --device /dev/dri
是完全等价的。

3.容器挂载USB设备
docker容器如果想要访问到宿主机的设备,比如插在USB接口上的串口转TTL模块(如CH340),需要在docker run 运行容器的时候把CH340挂载到容器上。这个是一个巨坑,因为fishros给你创建容器的时候并没有挂载USB设备,所以这个得你自己捣鼓。

如第二节所述,不就是挂载设备嘛,加上一句 --device /dev/ttyUSB0
不就行了吗?这么说docker run 的指令就可以写成如下这种形式:
但是这种写法不够灵活,初学者不推荐使用。为什么呢?因为你这么做需要确保你创建容器与进入容器的全过程中USB口上是插有CH340的(即在/dev文件夹下能找到ttyUSB0),否则docker就会报错说找不到设备。
这种一般适合什么人用呢?产品级的机器人,比如稚晖君的哪吒!这种机器人一般内部机载电脑的接线已经完全确定,并且早就用外壳把电路部分封死到里面了,此时是可以确定机载电脑只要是开机的, ttyUSB0 就一定已经连到机载电脑上了。那当然这么运行自然是没有问题的。


-v /dev:/dev
直接把宿主机的 /dev 文件夹全部通过volumn同步到容器里,简单粗暴,全部宿主机的device都会直接挂载到容器中。
此乃究极解决方案,最终命令如下:
注意这里的 --privileged
相当于给了容器操作宿主机的管理员权限,必须加上。
其中,--privileged
与-v/dev:/dev
这两句是精髓,关于--device的命令多余去掉即可,其他命令和fishros保持一致。
网上有的博客说这么做不安全,那都是说给互联网专业的,学javaweb的人听的,咱们学机器人的不用管。
另外,--net=host
这个配置先按下不表,下文再说。

4 容器内局域网通信
众所周知,ros可以很便捷地在局域网内进行多机通信,尤其是ros2甚至可以称为零配置无缝衔接。那么,在docker容器内中的ros是否还有这种优势呢?
事实上,通过在docker run的时候进行配置,可以完成docker容器内ros同样无缝局域网通信。其实这也是第二个巨坑,因为fishros给你创建的容器的时候同样没考虑这个问题。
第三节有一个没有介绍的参数:--net=host
。这个参数意思就是让容器与宿主机的网络以host模式连接,host模式下,容器没有自己独立的网络空间,而是完全依附与宿主机的网络空间,所以和局域网下其他机器通信当然没有问题了。docker的host模式可以类比虚拟机的桥接模式。(如果你没听说过桥接模式,当我没说)
网上也有的博客说这么做不安全,那都是说给互联网专业的,学web的人听的,咱们学机器人的不用管。

5 更改已创建容器的配置的方法
由上述可知,docker容器只有在docker run 的时候才能决定该容器的配置,一旦创建好则不可更改。如果你先前通过fishros建立好了docker容器,并且已经在里面仿真开发了很长时间,现在遇到了实物实验连不上USB,或者连不上局域网内别的机器的问题,应该怎么更改之前创建好的容器的配置呢?
咱们需要:先把原来的容器打包成一个镜像,然后再用docker run指令把刚刚打包好的镜像运行成一个新的容器,这样新的容器和原来的容器是完全一样的,差别只是你在 docker run 的时候添加上的挂载/dev设备和关于网络的配置。
接下来,我们就实操一下。
docker将容器打包成镜像的命令如下:
例如,咱们把名称为noetic1的容器打包成一个叫ltdz的镜像,标签名瞎起一个就行了:
然后查看是否打包成功,列举本地的所有镜像:
若有你刚才打包的那个,说明打包成功。
最后将这个镜像运行起来,命令与第三节的究极解决方案保持一致,并将镜像名改成你刚才打包成的那个镜像名:
最后,docker,启动!!!赢!结束!

6 补充:fishros的奇技淫巧与容器内使用可视化窗口应用的细节
你也许比较纳闷,docker进入容器的命令不是 docker exec 吗,为什么使用fishros创建的docker容器,直接输入容器名,就可以进去呢?

这就属于fishros的奇技淫巧了,如果你之前使用fishros创建了一个容器,你可以打开宿主机下的~/.bashrc
文件,看看有没有这么一句话:

~/.fishros/bin/

如果有,你用记事本打开他看看他长啥样,是不是挺熟悉的?聪明的你是不是不需要再解释了?

xhost +local: >> /del/null

结语
也许折腾这么一大堆你会感觉docker非常繁琐,但不知道你有没有注意到,使用docker安装ros根本没有敲一连串的繁琐的命令,你做的只是把别人提供给你的镜像下载下来,并docker run 成了自己的容器而已。这就是docker最大的优势,docker将配环境变成了一个共享式的工作。
fishros官方给你提供了一个安装好ros的镜像,你下载下来直接docker run,就不需要在这个容器中安装ros了。那么有没有一种可能,你在这个镜像的基础上安装一些别的东西,比如gedit mysql python pytorch tensorflow tensorrt java cmake gcc gdb opencv,然后再打包成自己的镜像发布出去,别人拿到你的镜像之后直接docker run,你已经安装好的那一堆东西,别人也再也不需要管了。这种体验是奇妙的,对于量产化的配环境工作是革命的(尤其对于互联网行业)。希望大家都能掌握这个革命性的工具。

参考文献
docker容器打包成镜像: https://blog.csdn.net/qq_14945437/article/details/106135369
深入了解docker推荐尚硅谷的教程: 尚硅谷在B站的docker教程


题外话
2023年8月17号之前 epic可以免费领取 欧陆风云4!!!这是个和钢铁雄心4一模一样的P社游戏,只不过时间放在了1400奥斯曼崛起~1800法国大革命期间。亲爱的宝哥老早就推荐我玩玩这个游戏,还好没买,不然血亏。对近代世界历史感兴趣的同学赶紧抓紧时间白嫖吧!

