ros分布式多机通信完整教程

本文详细介绍了使用ROS在多台机器间进行通信的过程,包括设置IP、配置.bashrc文件、解决从机发布topic到主机的问题及图像话题丢包、延迟的应对策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ros多机通信完整试坑教程

前言:

  1. 老规矩,先讲讲为啥要做这个项目。因为机器人上的工控机没有办法加显卡,所以无法跑TensorFlow-GPU,如果用CPU的话,一个是时间太长,二是消耗资源太多。
    所以必须得将图像处理模块,放到笔记本上。刚好看到古月居大佬的书《ros机器人开发实践》中,介绍了如何设置。emmm,下面会介绍书上的一些bug。。。。
  2. 感觉现在的时间越来越不够用了,连写博客的时间都不想花了。今天抽一个小时简单记录一下吧,希望我走过的坑,能够给大家节约一些时间。
  3. 遇到的坑有:
    1 . 根据古月大佬书上的教程,只能实现从机的订阅,从机并不能发布topic到主机(host)上
    2 . 我用的是路由器无线通信,通信速度有问题,订阅的话题是Kinect V2相机的数据,数据量大,频率高,通过这种通信方式,经常会丢包
    3 . 订阅压缩话题:/kinect2/hd/image_color_rect/compressed

系统介绍:

  1. 主机:移动机器人,控制主机是工控机,CPU是i5 6200U
  2. 从机:笔记本,CPU是i7 6500 ,GPU GT960M
  3. 相机:Kinect V2,连接在工控机上。
  4. 软件包:基本的Kinect V2的库函数,具体安装,可以参考这个教程:ubuntu16.04+ros kinetic+kinect2.0错误解决方案+小技巧
  5. 工控机连的是一个无线路由,然后让笔记本也连上,使二者处于同一个局域网
  6. 都需要安装openssh,这个直接 sudo apt-get install openssh 就好了
  7. 在笔记本上远程操作工控机(主机),需要这样:
  8. ssh iim@192.168.31.182
  9. 上面的iim是你被远程操作的工控机的用户名,换成你自己的就好了,后面的IP是工控机在局域网中的IP
  10. 登录后,就可以操作launch 相机了——
  • roslaunch kinect2_bridge kinect2_bridge.launch
  • 就可以拿到话题了。接下来就可以想办法做你需要做的事儿了

1、设置IP和~/.bashrc文件:

  1. 先找两台机子的IP地址:
    ifconfig

根据这篇教程的介绍,可以知道这些东西的含义:

  • 如果电脑连接的时有线网,则显示结果中,etho 部分的 inet addr 后面就是该电脑的 IP 地址;

  • 如果电脑连接的是无线,则 wlan0 部分的 inet addr 后就是 IP 地址。

    在这里插入图片描述

  1. 分别了解了之后,需要在两台机子上的/etc/hosts文件加入对方的IP地址和对应的计算机名字
    举例如下:
    在工控机上终端操作,或者远程操工控机(计算机名为iim):
sudo vim /etc/hosts

这里的笔记本(计算机名为 lyl )的IP地址是192.168.199.124,其实连到局域网的时候不是这个,因为编辑博客,所以连的是外网。假设就是上面的那个。
然后加上这段:

192.168.199.124  lyl

前面的IP,后面的是名字,尽量对齐

之后的笔记本端的文件整体如下:

127.0.0.1       localhost
127.0.1.1       lyl
192.168.31.124  lyl
192.168.31.182  iim
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

工控机的就不放了,差不多。
古月大佬说,最好ping一下,看看通信是否正常:
在笔记本上

ping iim

正常就好了

  1. 设置ROS_MASTER_URI,这里的就比较复杂。
    我刚开始按照古月的教程,首先文件就写错了一个:bashrc 写成了 bzshrc
    更重要的是,他只是在从机,也就是我这里的笔记本上设置了这个参数,这样的话,只能保证笔记本可以订阅主机(工控机)的话题,但是无法发布话题出去!
    具体的表现,可以用这个显示:
rostopic echo /yourtopic_name

可以发现,咦,一个东西都打印不出来!
遇到这个问题,我搜了很多教程,我刚开始都以为ros不支持从机发布话题,后来直接看了官方教程,提到了双方都得设置ROS_MASTER_URI,我才知道被坑了。

列一波搜到的有用的教程:

接着上面的来

  1. 从机-笔记本上需要设置的内容,可以看看:
    把这段加到笔记本中的 ~/.bashrc 文件中
export ROS_HOSTNAME=lyl
export ROS_MASTER_URI=http://iim:11311

同理在主机-工控机上这个文件中加上这段:

export ROS_HOSTNAME=iim
export ROS_MASTER_URI=http://iim:11311

设置好IP后,最好 source ~/.bashrc 刷新一下,就可以通信了。

第二个bug——图像话题会丢包、延迟等问题

主要原因,可能是因为用的是WiFi无线网络,带宽有限,视频流数据庞大,所以经常会丢包。获取不到话题。
所以没办法,我只能订阅压缩过的话题——
/kinect2/hd/image_depth_rect/compressed
整体思路大致如下,分析直接看注释吧,要吃饭了,我不想继续写了

#导入消息类型,压缩的和未压缩的
    from sensor_msgs.msg import CompressedImage
    from cv_bridge import CvBridge, CvBridgeError
    from sensor_msgs.msg import Image
    import rospy
    import numpy as np
    import cv2
    
    class Detect(object):
        def __init__(self):
            self.start_time = time.time()
            #订阅网络压缩话题,并且,消息类型换成压缩的
            self.image_sub = rospy.Subscriber("/kinect2/hd/image_color_rect/compressed",CompressedImage, self.rgb_callback,queue_size=1)
            self.depth_sub = rospy.Subscriber("/kinect2/hd/image_depth_rect/compressed",CompressedImage, self.depth_callback,queue_size=1)
            self.bridge = CvBridge()
    
        def rgb_callback(self,image):
            try:
            #尝试转换压缩图片信息到CV2可以直接用的格式。后面加不加bgr8好像都行。
                self.rgb_image = self.bridge.compressed_imgmsg_to_cv2(image, "bgr8")
                #用上面的和下面的都行,都是会转成rgb的图,但是对于第二个深度图就不合适了,只能用上面的
    #            np_arr = np.fromstring(image.data,np.uint8)
    # opencv3 is this ,and opencv2 is cv2.CV_LOAD_IMAGE_COLOR
    #            self.rgb_image = cv2.imdecode(np_arr,cv2.IMREAD_COLOR)
            except CvBridgeError as e:
                print(e)
                rospy.loginfo('convert rgb image error')
    
        def depth_callback(self,depth):
            try:
                print("depth_start:")
                self.depth_image = self.bridge.compressed_imgmsg_to_cv2(depth)
                print(self.depth_image.shape)
            except CvBridgeError as e:
                print(e)
                rospy.loginfo('convert depth image image error')

这里的代码,只提供参考,应该是很难直接运行的,具体直接运行的代码有机会再贴出来吧。

反正通过这样,基本上可以实现少丢包,低延迟的效果。
具体的数字,可以看看:

  1. 在工控机本地,rostopic hz /kinect2/hd/image_color_rect/compressed
  2. 大概是30Hz
  3. 在笔记本上我订阅的频率大概是14Hz,然后订阅两个的话,应该会降低一些。
  4. 而我的识别模块,大概是3Hz的频率,所以完全可以等得起这个。
### ROS分布式通信协议的实现方式 ROS(Robot Operating System)通过其内置的分布式通信机制,支持节点间的跨进程、跨机器通信。这种通信主要依赖于发布/订阅模型和中间件协议[^1]。在ROS中,每个节点可以作为消息的发布者或订阅者,它们通过话题(Topic)进行数据交换。为了确保通信的正确性,发布者和订阅者必须使用相同类型的消息格式[^3]。 #### 1. 发布/订阅模型 在ROS中,发布/订阅模型是节点间通信的核心机制之一。该模型允许一个或多个节点(发布者)将消息发送到特定的话题,而其他节点(订阅者)可以从该话题接收消息。这种松耦合的设计使得节点之间的依赖关系最小化,从而提高了系统的灵活性和可扩展性[^1]。 #### 2. 中间件协议 ROS的通信依赖于一种名为TCPROS的中间件协议。TCPROS基于TCP/IP协议栈,提供了可靠的点对点通信。此外,对于实时性要求较高的场景,ROS还支持UDPROS协议,这是一种基于UDP的轻量级传输方式。 #### 3. 消息格式 为了确保不同节点之间能够正确地解析和处理数据,ROS定义了一套标准化的消息格式。这些消息格式被封装为`.msg`文件,并可以通过命令如`rosmsg show`查看其详细信息[^3]。每个消息类型都有明确的字段定义,例如整数、浮点数、字符串等,确保了数据在不同节点间的互操作性。 #### 4. 跨机器通信 当ROS节点分布在不同的计算机上时,通信需要借助ROS Master进行协调。ROS Master充当名称注册服务的角色,负责将节点的发布者和订阅者信息进行匹配[^2]。一旦匹配成功,节点之间可以直接建立点对点连接,而无需持续依赖ROS Master。 #### 5. ROS2中的DDS 在ROS2中,分布式通信机制得到了进一步增强,引入了DDS(Data Distribution Service)作为底层中间件[^4]。DDS提供了一种高效、可靠的数据分发服务,支持多种传输协议和质量保证策略(QoS)。通过设置域ID(ROS_DOMAIN_ID),可以控制哪些节点能够相互发现并通信。默认情况下,所有节点共享同一个域ID(0),因此在同一网络中无需额外配置即可实现分布式通信。 ```python # 设置ROS2域ID的示例 export ROS_DOMAIN_ID=10 ``` ### 示例代码:创建一个简单的发布者和订阅者 以下是一个简单的ROS2节点示例,展示了如何通过话题实现节点间的通信: ```python # 发布者节点 import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalPublisher(Node): def __init__(self): super().__init__('minimal_publisher') self.publisher_ = self.create_publisher(String, 'topic', 10) timer_period = 0.5 # seconds self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): msg = String() msg.data = f'Hello World: {self.i}' self.publisher_.publish(msg) self.get_logger().info(f'Publishing: "{msg.data}"') self.i += 1 def main(args=None): rclpy.init(args=args) minimal_publisher = MinimalPublisher() rclpy.spin(minimal_publisher) minimal_publisher.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` ```python # 订阅者节点 import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') self.subscription = self.create_subscription( String, 'topic', self.listener_callback, 10) self.subscription # prevent unused variable warning def listener_callback(self, msg): self.get_logger().info(f'I heard: "{msg.data}"') def main(args=None): rclpy.init(args=args) minimal_subscriber = MinimalSubscriber() rclpy.spin(minimal_subscriber) minimal_subscriber.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ```
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehedadaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值