ROS2中Publisher和Subscriber的创建方法和函数调用

ROS2中Publisher和Subscriber的创建

ROS2的概念和术语与ROS1基本保持一致,对于消息话题,存在话题的发布方Publisher和接收方Subscriber

如果没有使用过ROS2,建议先把官网教程中Tutorials章节下的Beginner: CLI tools目录下的内容学习一遍,最好能将所有Demo跑通

具体网站链接如下;

ROS2新手教程

rclcpp::Publisher创建流程

在ROS2中自定义的Publisher类要继承自rclcpp::Node类,rclcpp::Node类中定义了create_publisher()模板函数,可以用来创建一个rclcpp::Publisher实例,本文以std_msgs::msg::String作为模板类型为例,对ros2源码中提供的minimal_publisher/lambda.cpp进行分析,该示例创建了一个publisher并使用lambda函数进行std_msg::msg::String格式的消息发送,该程序源码位于ros2源码src/ros2/examples/rclcpp/topics/目录下。

       Node::create_publisher()中调用rclcpp::create_publisher()函数,调用detail::create_publiser()函数。上面的示例的模板类型std_msgs::msg::String对应MessageT模板。

       detail::create_publiser()最终调用的是NodeTopics::create_publisher(),该函数实现是调用了第二个传入参数publisher_factory.create_typed_publisher()函数,publisher_factory由rlcpp::create_publisher_factory()创建,其中会将一个lambda匿名函数初始化给publisher_factory的create_typed_publisher成员变量,因此,publisher_factory.create_typed_publisher()调用的就是lambda匿名函数。

       lambda匿名函数中创建了<PublisherT>的实例,该模板在这里对应rclcpp::Publisher<MessageT, AllocatorT>类型,上面示例得知这里的MessageT模板对应std_msgs::msg::String类型,因此rclcpp::Publisher的真正创建正是通过这个lambda匿名函数,lambda匿名函数最后将创建的rclcpp::Publisher实例指针返回给用户。

接下来看一下创建rclcpp::Publisher实例时,其构造函数都干了什么。 rclcpp::Publisher类继承自PublisherBase类,在rclcpp::Publisher类的构造函数中调用的rclcpp::get_message_type_support_handle<MessageT>()函数内部会调用rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),该函数声明是一个函数模板,其实现位于msg消息生成的rosidl_typesupport_cpp/目录中的*__type_support.cpp

上述示例中std_msgs::msg::String类型的get_message_type_support_handle()函数实现位于std_msgs/rosidl_typesupport_cpp/std_msgs/msg/string__type_support.cpp,函数返回一个static的rosidl_message_type_support_t实例,包含该消息类型支持的信息,作为PublisherBase类构造函数的参数。

 上一步得到的rosidl_message_type_support_t结构体作为参数赋值给PublisherBase类,接着看PublisherBase类的构造函数,将上述结构体赋值到type_support_成员变量,并传入给rcl_publisher_init()函数,这里就从rclcpp层调用到rcl层的方法了。

rcl_publisher_init()也不处理该结构体,而是调用rmw_create_publisher()函数,这个函数就从rcl层深入到rmw层了。

注:rmw层直接与DDS对接的,ROS2面向不同厂家的DDS实现,都提供了一套rmw接口,因此rmw层的函数名虽然相同,但对于不同厂家的DDS有不同的实现,本文中涉及的rmw函数均以适配Fast DDS的rmw_fastrtps为例。

       rmw_fastrtps_cpp::create_publisher()中调用get_message_typesupport_handle(),来获取对应的fastrtps_cpp类型的结构体实例,该函数中传入的type_supports参数就是之前rosidl_typesupport_cpp/目录中的get_message_type_support_handle()函数返回的static的rosidl_message_type_support_t实例。

       接下来看一下get_message_typesupport_handle()函数实现,上图最后调用的func()对应rosidl_typesupport_cpp:: get_message_typesupport_handle_function(),进而调用了核心函数rosidl_typesupport_cpp::get_typesupport_handle_function()。

       rosidl_typesupport_cpp::get_typesupport_handle_function()函数中,定义了动态库rcpputils::SharedLibrary的指针变量lib,并以“%s__%s”的格式由包名map->package_name和标识identifier合成动态库的名字,比如“std_msgs__rosidl_typesupport_fastrtps_cpp”。

       接下来就可以使用动态库路径library_name,得到动态库实例赋值给lib变量,继而借助lib->get_symbol()函数从动态库中查找指定函数(例如rosidl_typesupport_fastrtps_cpp__get_message_type_support_handle__std_msgs__msg__String)。

       指定函数会返回消息类型这一关键结构体的实例,其定义位于rosidl_typesupport_fastrtps_cpp/目录中的msg/detail/dds_fastrtps/目录的*__type_support.cpp文件。仍然以std_msgs::msg::String消息为例,看一下返回的rosidl_message_type_support_t类型的实例,其data成员被赋予了一个message_type_support_callbacks_t的实例,该实例有命名空间、消息类型名、消息序列化函数等信息。

      让我们回到rmw_fastrtps_cpp::create_publisher()函数,将刚刚得到的rosidl_message_type_support_t类型实例赋给了type_support变量,其data成员包含了消息类型的关键信息。

之后就是借助type_support->data获取消息类型的关键信息,调用fastdds的相关接口进行DDS传输所需的操作。最后创建一个rmw_publisher_t的实例返回给rclcpp::PublisherBase 类型的publisher_handle_ ->impl->rmw_handle变量,type_support->data也会保存在rmw_publisher_t实例的data->type_support_impl_成员。至此,publisher创建流程结束。

rclcpp::Subscription创建流程

ROS2代码框架实现了底层代码在使用上的高度一致性和复用性,rclcpp::Subscription的创建流程与rclcpp::Publisher类似,有兴趣的coder可以尝试自己分析一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值