最近心血来潮尝试了一下ROS 2,想着看下ROS 2是如何仿真,以及想要看下ROS 2实际应用的可行性。
关于ROS,本人最近才开始了解,之前少有接触。目前我是打算当作仿真工具来使用的,关于其实际运用落地,目前还是比较担心其性能、延时的问题(毕竟在网络上听说过ROS对于时间延时的问题优化不够)。但是仔细想来,应该也没有哪个产品会完全使用ROS吧,毕竟不是所有的Robot都是拥有多个处理单元的(应该考虑到多线程能力弱的处理单元),因此从实际的方面考虑,我目前是把这个ROS当作仿真工具来使用,主要是验证流程、算法等。
因此,我在此处有开了新的专栏来讲述我在学习使用ROS的过程中的心得和遇到的困难。差不多就是想当作一份笔记来进行书写吧。
一、安装ROS记录
选择版本为ROS Foxy LTS,长期支持的版本比较友好些。同时安装过程强烈建议跟着官方进行,虽然在国内因此各种联网的坑而心态爆炸,但是也是最为简单粗暴了。
1.1、确认安装ROS的操作系统
本人实操的系统环境为:Ubuntu20.04、x86_64。
以下是不同的操作系统的所对应的官方链接,均为二进制安装的教程(官网也有源码编译安装的教程,但是不推荐):
1.2、安装步骤(Ubuntu举例)
根据1.1确认自己系统安装ROS的对应教程,这里推荐使用Ubuntu20.04,比较作为仿真工具,在linux下的使用不管是教程还是什么疑难杂症,网络的解决方法都很多。虽然大多数时间都是不一定适合自己的情况,但是至少是跟本人同步了。
1.2.1、设置镜像源,下载公钥
(1)安装必要的下载工具:
sudo apt update && sudo apt install curl gnupg2 lsb-release
(2)下载公钥(有坑):
如果你可以访问外网,那么执行下列命令就好
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
但是如果执行上述命令,出现错误或者一直停顿无法结束,那么大概率是无法按照上面的命令执行了,此时你需要进行如下的操作:
这里我通过访问外网下载到了一份ros.key,这里提供了网盘链接:
链接: https://pan.baidu.com/s/1l4ipwFdB_QTOhRy9RN40iA?pwd=5n6c
提取码: 5n6c
下载好网盘中的ros.key,假设你保存为~/download/ros.key,然后执行如下命令:
sudo cp ~/download/ros.key /usr/share/keyrings/ros-archive-keyring.gpg
(3)设置镜像
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
1.2.2、安装ROS 2
(1)更新apt安装工具的源:
sudo apt update
(2)安装ros2(有坑):
sudo apt install ros-foxy-desktop
执行上述命令,如果最后提示”设置触发器”等字眼,那么就代表一切顺利,但是如果出现一下的情况:
$ sudo apt install ros-foxy-desktop
.
.
.
已下载 404 MB,耗时 4分 29秒 (1,502 kB/s)
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/universe/f/freeglut/freeglut3_2.8.1-3_amd64.deb 连接失败 [IP: 2001:67c:1562::18 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/main/b/boost1.71/libboost1.71-dev_1.71.0-6ubuntu6_amd64.deb 连接失败 [IP: 2001:67c:1562::15 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/main/b/boost-defaults/libboost-dev_1.71.0.0ubuntu2_amd64.deb 连接失败 [IP: 91.189.91.38 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/main/b/boost1.71/libboost1.71-tools-dev_1.71.0-6ubuntu6_amd64.deb 连接失败 [IP: 2001:67c:1562::15 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/universe/f/freexl/libfreexl-dev_1.0.5-3_amd64.deb 连接失败 [IP: 91.189.91.39 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/universe/p/proj/libproj-dev_6.3.1-1_amd64.deb 连接失败 [IP: 2001:67c:1562::18 80]
E: 无法下载 http://cn.archive.ubuntu.com/ubuntu/pool/universe/libg/libgeotiff/libgeotiff-dev_1.5.1-2_amd64.deb 连接失败 [IP: 2001:67c:1562::18 80]
E: 有几个软件包无法下载,要不运行 apt-get update 或者加上 --fix-missing 的选项再试试?
这种情况代表ROS有的库在ubuntu的国内的服务器无法访问,没有办法安装这些依赖库那么ROS也就无法安装。解决办法如下:
打开系统的“软件与更新”,在Ubuntu软件/下载自/其他站点,选择mirrors.tuna.tsinghua.edu.cn这个镜像;点击选择服务器;设置完成后,如下图所示:
接着设置apt的下载源为上述设置的清华源:
sudo sh -c '. /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ $DISTRIB_CODENAME main" > /etc/apt/sources.list.d/ros-latest.list'
再次尝试安装ROS:
sudo apt update
sudo apt install ros-foxy-desktop --fix-missing
二、ROS_DOMAIN_ID的概念
下面的内容都是基于官网关于DOMAIN_ID概念介绍
2.1、介绍
ROS 2中不同组件之间信息交流,其依赖的是一个中间件DDS,而DDS这个中间之所以能够让不同的逻辑网络共享一个物理网络,主要依靠一种叫做Domain ID的领域标识。在ROS 2中,不同的node节点在拥有同一个Domain ID的时候能够无障碍地彼此收发信息,而拥有不同的Domain ID的节点之间则不能收发信息。因此Domain ID相当于把node节点隔离分组了。
2.2、Domain ID的取值范围
从上述的介绍来看,既然不同node节点之间使用DDS来进行信息交流,那么我们很容易想到这个DDS使用了网络通讯的原理(我们可以这样推测:如果一个Robot仿真对象在我们本机运行,存在了多个node,这些不同的node都是在不同的终端单独开启运行的,那么如果保证不同的node节点的唯一性,也就是node需要跟某个设备绑定,如此我们很容易联想到TCP中的网络端口,让node绑定特定的端口从而实现信息交流的可靠性和唯一性)。
因此,根据网络端口的特性,我们知道了不同的Domain ID圈定了不同的网络端口,这样才能够通信分离;同时因为系统本身也在使用部分的网络端口,Domain ID会避让开系统的临时端口范围。linux下系统的临时端口范围可以使用如下命令查看,一般是32768~60999:
$ cat /proc/sys/net/ipv4/ip_local_port_range
32768 60999
另外,系统的网络端口总数是2^16个,也就是最大的端口号码是65535。同时在ROS2中一个Domain ID占用了一块长度为250的网络端口号码;ROS2的第0个Domain ID是从7400端口开始分配的。因此,自然我们知道了Domain ID的取值范围为:0~101、215~232。
2.3、单独的Domain ID内部的端口分配
我们知道一个Domain ID拥有最多250个端口号码,同时拥有多个node节点(通俗地解释,也可以叫做多个任务或者是线程)。那么这250个端口各自的含义是怎样的,同时node和端口的对应关系是如何的?
一个Domain ID拥有两个多播端口,分别是Discovery Multicast Port、User Multicast Port;一个Domain ID最多拥有120个node节点,一个node链接了该Domain ID的两个多播端口和该node所分配到的单播端口,分别是Discovery Unicast Port、User Unicast Port。因此一个Domain ID下的所有node共享了两个多播端口,拥有各自对应的单播端口。
我在这里提供了这样的一个python计算工具来进行计算第i个domain id中的第j个node所连接的端口:
import sys
def domain_id2port(domain_id=0, participant_id=0):
domain_id = int(domain_id)
participant_id = int(participant_id)
# assert(type(domain_id) == int)
# assert(type(participant_id) == int)
dmp = 7400 + domain_id * 250
ump = dmp + 1
dup = dmp + 10 + participant_id * 2
uup = dup + 1
print("Domain ID:", domain_id)
print("Participant ID:", participant_id)
print("Discovery Multicast Port:", dmp)
print("User Multicast Port:", ump)
print("Discovery Unicast Port:", dup)
print("User Unicast Port:", uup)
if __name__ == "__main__":
if (len(sys.argv) >= 3):
domain_id2port(sys.argv[1], sys.argv[2])
else:
print("Usage: python3 cal_id.py domain_id participant_id")
print("Eg:python3 cal_id.py 215 2")
domain_id2port(215, 2)
使用方法:上述代码保存为cal_id.py;然后根据所需要计算的domain_id和node_id(也叫做participant_id)进行传参;实例结果如下所示:
// Domain ID = 4, Node ID = 110
$ python3 cal_id.py 4 110
Domain ID: 4
Participant ID: 110
Discovery Multicast Port: 8400
User Multicast Port: 8401
Discovery Unicast Port: 8630
User Unicast Port: 8631
至此,对于ROS 2的Domain ID的概念也介绍完毕了。