A-A+

老王说ROS(1-5)

2015年03月31日 robotics, ROS 评论 4 条 阅读 1,952 次

老王:王宏明,一代机器人天“牛”也。

此为微信聊天的杂谈,经同意整理如下,与大家分享,按老王说法是“随便聊聊,已完不续,如有续集,纯属巧合” 呵呵。

(版权所有,转载需征求老王同意)

1.   ROS结构

今天扯一下ros吧。抛开ros生态圈不讲,单表ros软件。(1)作为通信中间件的ros是怎么玩的。ros软件提供了一个通信中间件来实现分布式系统的构建。那么既然是通信中间件,ros的通信机制是怎么实现的呢?单从文档上看,ros提供了订阅发布的通信机制,也就是有个发布者发布一个topic,订阅者订阅这个topic,当有发布者就某个topic 发布message的时候,订阅这个topic的订阅者就可以收到这个message 了。当订阅者收到这个消息的时候,通过一个自定义的回调函数来处理消息,来完成自己的业务。那么这个过程是具体是如何实现的呢?那就需要看源代码了。看源代码的过程中你会很容易骂人的,这个要忍住,但千万不要抱着看佛祖的心态看,从通信中间件或者说软件设计的角度来讲,ros绝非一流水平。话归正传,据我有限的认知,ros是这样实现订阅发布机制的。第一个概念,就是交易,订阅者和发布者是如何达成交易的呢?当然是在市场上呀,市场在哪里,当然就是master这个程序了,没有master就没有交易。那么第一个问题来了,订阅者和发布者怎么去的市场呢?从外表看不出来呀,没错,外表看不出来,你在源码里搜master-uri就找到了,当你在环境变量里搜master-uri也能找到。这个常量包含了ip和port的信息。有了这个信息你就会找到master 了。有了地址下一步是怎么去,肯定是socket了,并且不是普通的socket而是经过打扮的xmlrpcclient,既然做买卖那就得像个买卖人,要不然master那里的xmlrpcserver不搭理你。于是你打扮好了,到了master,你说我要adviser 一个topic,xmlrpcserver是个好公仆,有求必应,来做个登记吧,ip,port,upd or tcp,topic,登记完卖家就回家了,建立了一个tcp or udp的server,守在电话旁等买家。买家也是这么去的市场,到那先登记自己的信息,然后就问都是谁卖闺女,查到地址后,回家就去建立与卖家的链接,闺女就源源不断的送了过来。说完做买卖,再说仓库,也就是消息缓冲区模型,送来这么多闺女我该怎么放,怎么用呢?说实话,我还没看到,但无外乎如下几种,先进先出队列,后进先出队列,后进先出的环形队列,再有就是没有队列只能放一个只要最新的;ros是那一种呢?记住这个仓库在发送和接收端都存在,要并发不阻塞实现异步并发就得这么处理,收支两条线。加个评论:ros的这种做法,既不是完全得data centric,也不是完全的event driven,因为它兼有两者得特性。所以就缺少了dds的qos,随便交易,是个闺女都有人卖,至少也要看个年龄吧。再有就是不能把event driven的实时性彻底发挥出来。这个不怪它,机器人的信息交互本来就复杂,想要易用只能这样了,但无论如何也比傻乎乎的player/stage强,也比韩国人民在opros里设计的美好实现靠笨拙的cobra更好用。

2.   ROS消息

(2)ros里的消息

通信中间件的目的有很多,其中一个就是将易错的部分封装,让使用者避免出错,socket编程是容易出错的,容易出错的地方也有很多,其中一个就是将带结构的数据序列化为无结构的字节流以及从无结构的字节流里重构数据结构,这两个行为,你可以叫它编码和解码,也可以叫打包和解包。

为什么说这个易错呢?很多人不理解,不就是把各种数据类型放到一个字符型数组里吗?不就是再从字符型数组弄出各种数据类型吗?小心点,就行了。那我只能告诉你,你用这样的态度能够解决问题靠的是幸运,而不是技术。这里我就不阐述容易错的地方在哪里了。直接说ros是怎么干的吧。那就是抽象,用中性语言定义消息,让交互双方在文本空间达成一致,记住是文本空间,仅供人类阅读,协议结果就是.msg和.srv那些文件(我们常用的裸奔做法是,写个文本文件,画个两行的表格,第一行每个格子里写个名字叫字段名,对应的第二行标示数据类型,如果穿上内裤的话,就加点注释解释每一个字段的含义,不过这跟裸奔也差不多,我看过太多这样的美其名曰的应用层协议了,很多人没感觉有问题,那我告诉你,那是没出门,你可能还要辩解说,我和别人通信的好好的呀,那我也只能说,那是你俩都在澡堂子,一起出门试试)。好了,有了这个协议,这个穿着西装革履谈做出的协议,ros会帮你脱衣服的,第一步,利用代码生成工具帮你生成在代码空间一致的依赖编程语言的消息定义头文件,如.h文件,好了,大家放心的编码吧;第二步,ros在运行期间帮你解码编码,在运行期间达成一致。最后需要解释的是有两点:

(1)ros肯定是生成了自定义消息的cpp文件的,要不然谁来打包和解析呀,至于什么时候自动生成而又删除了的,这个读makefile什么的应该不难发现,当然我还没发现。

(2)ros在实际传送的每个消息里都包含了一个消息头,这个头至少包含了一个包标示字段、一个校验码段、一个包大小段。这是裸奔的内容,想在其他不支持ros的系统上做rosnode,必须要了解这个,当然还要理解美其名曰naming service的闺女交易。

3.   ROS Node

(3)node

在一个机器人软件中,有各种各样的node。别抢答,各种各样不是指localization,slam ,action这些功能的多种多样,而是指有消息的生产者,消费者,以及消费又生产的深加工者。先插一句吧,这次讨论涉及到这样的一个问题,在用ros构建一个机器人软件时,仅仅从功能划分的角度来构建node是否合适。是否还需要考虑让消息的流动更顺畅,让node更简洁高效呢?先说node是怎么跑的吧。当你建立一个node后,要么它按照一个固定的周期运行,要么它阻塞等待事件发生,由事件驱动运行。无论是那种,node都要干活,node干的什么活呢?callback queue里的活。这个callback queue里的callback是哪里来的呢?常见的是subscriber的callback,当然还有其他的,包括publisher的,service的。那这些callback是什么时候被调用的呢。那就是spin()或者spinonce()。spin调用在queue 里所有的availiable的callback,如果没有availible的,它就阻塞。spinonce,显然只调用一次,看看有没有准备好的callback,有就调用,没有就返回。那什么是availible的、准备好的呢?对于subscribe,准备好的就是那些有新消息的subscriber的callback。现在如果你明白了上述node的运行机理。你在感到舒服之余,请准备接收心烦的问题。1)第一个问题就是,如果subscriber没有收到新消息,那么它的callback就不会被执行。如果你想每次都运行callback呢?对不起,没办法,不是virgin我不娶。

2)第二个问题,callback执行有个timeout值,如果设置不合理,要么费时,要么callback被中止。被中止呀,兄弟们,多可怕,万一是一个重要逻辑的一环呢?今天就聊到这里,最后送点福利,如果你想实现事件触发的node就用spin;如果你要固定周期的node,那就用spinonce+sleep,但一定要牢记,在每个周期里不是所有callback被执行。

4.   ROS Master

(4)master

前三次说了通信中间件、消息、node,这次聊貌似高大上实际很鳖孙的master。

ros吸引人的地方,两个,一、有各种现成的解决领域问题的软件包,二、有一个现成的构建分布并发系统软件的方案,既通信中间件。

先提一个问题,为什么要并发呢?并发的意义在哪呢?请想好了再回答。

在很久很久以前,计算机里的程序是顺序执行的,在很久很久以后计算机里的程序还是顺序执行的。

不同的是以前计算机解决的问题比较简单,现在有点复杂。人类已经很难用结构化的思维来描述系统,很难用面向过程的语言把系统描述给计算机了。

于是,面向对象思维登场,把世界抽象为对象以及对象间的交互。这个说法没错,但牛叉的说一切皆对象并奉若神明否定过程,这是不对的。因为,对象从生到死是个过程,对象间交互也是个过程,没有面向过程的思维也是不行的。

但面向过程思维不能太结构化,特别是解决复杂系统问题的时候,要面向多进程并发,把世界抽象为过程以及过程间的交互。当然如果你作为使用者,不关心过程如何实现,也希望更时髦点,那你就叫它面向服务吧。

这个思想在很久很久以前就有,叫做中断。试想一下,如果你写了一个主程序,写了一坨中断程序,主程序只是阻塞等待,如果合理安排中断程序的运行,这就是多进程并发嘛,中断程序有用还干活你就叫它中断服务程序嘛。

用并发的多个过程来抽像系统,特别适合用于机器人系统,因为机器人在人脑子里比较复杂(当然,也未必真不复杂,但的确也没那么复杂)。

说到这里,答案就有了,并发对于顺序执行代码的计算机来说没有意义,它不会更快,并发的意义在于用过程以及过程间的交互来抽象系统。

如何抽象出多个过程,建个什么样的node跟master无关,但node间如何交互却跟他有关。可惜,master实在是没提供太多基础服务,整个ros的通信机制也没提供良好的交互机制。

master提供两种基础服务,naming service和parameter server。这两个服务本质上没有什么不同,就是注册和查询,只不过一个是地址,一个是参数。

这些是远远不够的。动态发现discovery,生存检测liveness,访问控制access control,这些基本的都没有。

于是会有这样的问题

1)master死了会怎样?

2)node死了会怎样?

3)怎么知道master死了?怎么知道node死了?

先说第一个master死了,可以重启嘛,只是重启之后,以前的node注册信息会丢失,你无法和以前的node建立交互关系。node只在实例化nodehandler的时候注册信息。

第二个,node非正常死亡后,master是不知道的,rosnode list的时候,你还会看到它。

第三个问题,答案很简单。自己想办法!

当然你也可以边骂“你个鳖孙”,边想办法。

5.   ROS node里的线程

2015.4.4

ros不能丢,继续扯。

(5)node里的线程

先说第一个我不太懂的问题,那就是多线程和多进程怎么选择的问题。

在系统分析和设计时是不用考虑这些问题的,但要动手干了,那就不得不选择了。可惜,我也没有很好的建议。我只谈谈我的看法吧。我不在乎多线程和多进程。

第一,我觉着cpu哪里不区分他们。第二,我很少操刀写代码,哪个写起来复杂,哪个写起来简单我不在乎;第三、是否多占资源、是否需要在物理内存那跳来跳去影响速度,我也不在乎,我玩的还没高深到需要考虑那点差异。

不过有人在乎,一种是水平比较高,现实有约束,必须要考虑;一种是很有意识,思想有洁癖觉,觉着必须要在意,还有一种,在意只在书本上。最后一种,为了方便,急于求成,避开多进程,爱搞多线程,用ros的不乏这样的小伙伴。今天主要就说这个。

ros说得很明白,它设计时是有意的不让你看到线程的存在的,是不希望用户考虑这些事情的。说得朴素点,那就是node里开用户自定义的线程是不受鼓励的。

为什么要这样呢?这样的思路有没有问题?有问题又该如何解决呢?

为什么要这样,第一,估计跟我对进程线程的看法差不多,不区分线程进程,想并行处理,就建node;第二,ros还是很用心的为用户着想的,不想让你费劲。

可这样做是有问题的。一个突出的问题是有时候,一个系统需要好多好多node来实现,切的很细碎,实现起来、用起来好麻烦;当然如果都tm有现成的也无所谓,攒起来就得了呗。可惜,哪有那么现成,只好硬着头皮自己做node,设计msg,实现交互。这样也行,累就累点。

但当你把系统开发的差不多了,突然发现某一个node里还藏着一个需要并行处理的过程时,就不是累点的问题了。你有点想骂人了,动架构,一个不结实的架构,跟从头干有啥区别。天无绝人之路,灵光一现,我弄个线程就得了呗,交互起来还方便,还不用写msg,还能把多线程得各种交互机制用上,不用只靠一个pub/sub,于是轻车熟路得开始加线程。

哇,我好幸福呀,系统功能实现了,于是你在接下来得日子过上了能不加node就不加node,能不写msg就不写msg,能靠全局变量就靠全局变量的的好日子。

我也恭喜你,但同时也提醒一下这样的坏处。第一个,你在意的,你可能已经给自己埋下了地雷;第二个,你不在意的,你失去了一个锻炼系统设计能力的机会。

我只谈第一个,为什么说有可能是地雷,第一,因为ros不鼓励这么做,它有可能在应付node里用户自定义的线程时,考虑不周。第二,用户自定义的线程与node的线程间如何交互协调时,你只能大胆的去猜,你根本不清楚node在干嘛。

所以,我的建议是,既然ros了,就嫁鸡随鸡嫁狗随狗,按ros的想法来,人家不是已经提供nodelet了嘛。

最后,说点有用的,在一个node里除了主线程外,至少有两个线程,一个好像是pollmanager里有个类似于select()的线程,负责poll各个socket的读写事件;另外一个,我忘了在哪了,功能是监听、建立新的pub/sub链接。

标签:

4 条留言  访客:4 条  博主:0 条

  1. 上海骷髅

    首先,Message不是Topic发出的
    其次,不想再往下看了

    • poyoten

      我想问message是什么?看看别人用另一种角度看问题,且不论对错,至少我看到了不一样的东西。

  2. 上海骷髅

    一派胡言

    • libing

      王宏明是谁?天“牛”????是哪国人?有这么牛,中国的机器人跟国外差距还那么大

给我留言

Copyright © ExBot易科机器人实验室 保留所有权利.   Theme   Robin modified by poyoten

用户登录

分享到: