您的位置:首页 > 运维架构 > 网站架构

ROS学习之 roscpp内部架构概况

2016-07-19 16:59 330 查看
wiki链接:http://wiki.ros.org/roscpp/Internals

    这部分包括一个对roscpp内部架构组织高度概括的审视,从最底层开始。

    1.通用哲学

    roscpp的哲学是仅仅使一部分API对外可见。这意味着公共的头文件不允许包含私有的头文件。

    这意味着在最初时发生很多类型擦除。(???)

    通常我们会尽量减小包含的外部头文件数目。例如,普通公共头文件不允许包含Boost.Thread头文件(CallbackQueue例外)。未来将去除花费很多编译时间的Boost.Bind。

    2.xmlrpc(xml远程过程调用)

    xml rpc是使用http协议做传输协议的rpc机制,使用xml文本的方式传输命令和数据.

    ros使用一个名叫xmlrpcpp的修改的第三方库件。但它不支持在socket里实现用户轮询代码,用户要轮转一个额外的线程去做这部分工作。

    使用xmlrpcpp时,所有使用都通过XMLRPSManager组件,所有与master的通信都通过master::execute函数。

    3.PollSet/PollManager

    在最底层,所有非xmlrpc网络通过poll()实现,被PollSet类管理。

    4.连接和传输

    传输系统试图使底层的传输原型(TCP、UDP)变得通用。

    有一个基本抽象类Transport(http://docs.ros.org/api/roscpp/html/classros_1_1Transport.html)它被TransportTCP类和TransportUDP类所继承,它们实现了read()方法和write()方法。

    在更高层,Connection类(http://docs.ros.org/api/roscpp/html/classros_1_1Connection.html)提供了一个基于回调的方式像传输层读写数据。这个类管理有多少数据被发送和接收,当操作完成时,调用回调函数。

    Connection类被ConnectionManager类管理,这个类主要负责管理TCP/UDP连接,并且保持Connection对象的指针,直到它们被delete掉。当ConnectionManager接到一个新来到的TCP或UDP连接时,它将以话题连接还是服务连接创建一个TransportSubscriberLink或是ServiceClientLink

    5.初始化

    这个主题在节点初始化部分已经讲过,这里再深入一下。roscpp有两部分的初始化,init()方法和start()方法。init()方法做很少工作,主要就是解析一下环境和命令行参数,init()不允许参与实际的连接,因而,用户可以手动检测像是master启动没有,有没有按自定义的行为启动这样的事。init()方法也不会启动任何线程。

    start()方法参与大部分实际的初始化工作。start()可以手动调用,这样之后要调用shutdown()。或者在第一个节点句柄被创建时自动被调用,当最后一个节点句柄被销毁时,shutdown()被自动调用。

    start()将1.初始化所有的组件

        2.安装SIGINT信号句柄,如果需要的话

        3.创建rosout log4cxx添加器

        4.创建logger服务

        5.检查/use_sim_time参数,如果被设置,订阅/clock话题

    6.话题

    话题被TopicManager管理,它维护Subscription和Pulication列表。

    1)话题订阅

    Subscription类管理话题的订阅。一个话题只能有一个Subscription类,如果很多消息订阅者共同订阅一个话题,则它们共享一个连接,以节省带宽资源。

    Subscription类对象管理N个PublisherLink对象,每一个PubllisherLink对象连接着一个该话题的不同的Publisher.

    有两种不同的PublisherLink对象,TransportPublisherLink通过传输层连接着一个publisher,而IntraprocessPublisherLink连接一个本地、内部线程的publisher,传输消息数据时可以跳过传输层(可以的话提供免复制消息传递)。

    

    publisherUpdate:当一个话题发布者在master注册之后,这个节点将接受一个“publisherUpdate” xmlrpc调用,它使Subscription::pubUpdate指向正确的话题。对于非内部线程连接,这将启动一个对新publisher的异步的xmlrpc请求,当请求通过时,Subscription::pendingConnectionDone()方法被调用。一旦这个请求完成,一个新的TransportPublisherLink对象为这个publisher建立。

    Retry:重试。如果一个TCP TransportPublisherLink对象与它的publisher连接中断,它将尝试重新与之连接。它的第一次重试被设置为连接断开之后的100ms,以后重试时间间隔将加倍,直到20s后它被盖掉。

    消息订阅回调和去序列化:当一个消息接收时,它被压入一个消息订阅队列SubscriptionQueue,这个队列将在队列满时抛弃旧消息。值的注意的是,这时消息还没有被去序列化。一个MessageDeserializer对象将被压入队列,而非消息本身,这个对象被所有消息回调所共有,这意味着:

    因为队列满而被抛弃的消息将不会被去序列化。

    直到第一次与订阅相关的回调函数被调用时消息才会被去序列化。

    2)话题发布

    对一个话题的发布,由Publication类来管理。对于每一个话题,仅能有一个Publication对象。

    对于相同话题的许多个话题发布者,它们共享连接。

    Publication对象管理N个SubscriberLink对象,每一个对象连接这个话题的一个订阅者。与消息发布相似,消息订阅SubscriberLink对象也有两种类型,使用传输层的TransportSubscriberLink和不经过传输层的IntraprocessSubscriberLink.

    3)内部线程无复制支持

    roscpp使用boost::shared_ptr和rtti(Runtime Type Information,运行时类型信息)的组合提供基本安全的无复制内部线程间的消息传递。基本安全是指消息发布者对于自己已经发布的消息没有修改的能力。注意,这仅仅适用于消息被作为boost::shared_ptr类型传递的情况。否则,它将还会经历序列化/去序列化的过程,即使它已经不经过传输层。

    内部线程消息所经历的过程:

        Publisher::publish()  被发布

        TopicManager::publish()

            调用Publication::getPublishTypes()判断消息订阅者(发布消息针对的对象)的类型

            如果存在‘需要序列化’的消息订阅者,序列化消息

            如果不存在任何‘免复制’的消息订阅者,立即清除消息指针和rtti信息

        Publication::publish()

            寻找这个Publication中是否有InteraprocessSubscriberLink

            如果有,直接封装消息给它.    

        IntraprocessSubscriberLink::enqueueMessage()

        IntraprocessPublisherLink::handleMessage()

        Subscription::handleMessage()

            对于每一个subscriber,检查rtti信息是否一致,如果是,则将消息添加进其订阅队列,直到ros::spin()调用时,调用回调函数处理消息。

        这些中的有些部分是不必须的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: