前面写了一篇文章专门讲述了在ros2中使用fastdds的共享内存中间件实现的一些配置项及参考资料,下面我们来通过一个案例来说明共享内存的配置使用
共享内存配置
根据前面的笔记及文档资料总结,要使用共享内存必须要配置好相关的profile,下面提供的一个案例:
需要注意几个点:
- 必须配置default profile,否则会报错;
- xml profile的格式,字段不能有问题,否则会报错,是固定的;
- 官网文档不详尽,还是得结合源码来推断一些字段的名称,比如,historyMemoryPolicy如果配置成DYNAMIC实际上是对应官方的DYNAMIC_RESERVE_MEMORY_MODE;而官方还有一种DYNAMIC类型是DYNAMIC_REUSABLE_MEMORY_MODE;内存分配策略如果内存资源充足,或对平响有较高要求,推荐PREALLOCATED_MEMORY_MODE,折衷选PREALLOCATED_WITH_REALLOC_MEMORY_MODE,内存资源较少则选DYNAMIC
- fastdds官网没有publisher类型的profile attribute说明,但实际是可以用的,publisher/subscriber可以对应多个data_writer或data_reader;
- 根据fastdds的官方文档各配置项都可以加,注意好层级关系即可,如在ros2的rtps案例中data_sharing只写了kind,但fastdds介绍了datasharing有domain_ids,shared_dir等属性,这里shared_dir也得从源码看,官网文档也没举例,改目录也没用,可能还得配置环境变量,具体就没再仔细研究了;
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles"><profiles><!-- Default publisher profile --><publisher profile_name="default publisher profile" is_default_profile="true"><qos><publishMode><kind>SYNCHRONOUS</kind></publishMode><data_sharing><kind>AUTOMATIC</kind></data_sharing></qos><historyMemoryPolicy>PREALLOCATED_WITH_REALLOC</historyMemoryPolicy></publisher><subscriber profile_name="default subscriber profile" is_default_profile="true"><qos><data_sharing><kind>AUTOMATIC</kind></data_sharing></qos><historyMemoryPolicy>PREALLOCATED_WITH_REALLOC</historyMemoryPolicy></subscriber><publisher profile_name="count"><qos><publishMode><kind>SYNCHRONOUS</kind></publishMode><data_sharing><kind>AUTOMATIC</kind><domain_ids><domainId>123</domainId></domain_ids><shared_dir>"/home/lzs/docker/robo_volume/desertrescue/output/ros2"</shared_dir></data_sharing></qos><historyMemoryPolicy>PREALLOCATED_WITH_REALLOC</historyMemoryPolicy></publisher><!-- Default subscriber profile --><subscriber profile_name="count"><qos><data_sharing><kind>AUTOMATIC</kind><domain_ids><domainId>456</domainId></domain_ids><shared_dir>"/home/lzs/docker/robo_volume/desertrescue/output/ros2"</shared_dir></data_sharing></qos><historyMemoryPolicy>PREALLOCATED_WITH_REALLOC</historyMemoryPolicy></subscriber></profiles>
</dds>
配置好共享内存后,编译出可执行文件然后通过指定环境变量就可以使用了,指定环境变量按前面的文章也有两种方式。一般来说多个应用,统一配置在一个xml文件中,统一指定环境变量;
#include "rclcpp/loaned_message.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/int32.hpp"
#include "common_proto_data/msg/pubapp2_subapp.hpp"
#include <chrono>
#include <iostream>class ZeroCopyPublisher : public rclcpp::Node {
public:ZeroCopyPublisher() : Node("ZeroCopyPublisher") {//countPub = this->create_publisher<std_msgs::msg::Int32>("count", 10);countPub = this->create_publisher<common_proto_data::msg::Pubapp2Subapp>("count", 10);}void pub_count() {auto count_ = countPub->borrow_loaned_message();count_.get().count = i;std::cout << "publishing count:" << count_.get().count << std::endl;countPub->publish(std::move(count_));++i;}
private://rclcpp::Publisher<std_msgs::msg::Int32>::SharedPtr countPub;rclcpp::Publisher<common_proto_data::msg::Pubapp2Subapp>::SharedPtr countPub;int i = 0;
};int main(int argc, char **argv) {rclcpp::init(argc, argv);auto node = std::make_shared<ZeroCopyPublisher>();rclcpp::Rate rate(std::chrono::seconds(1));while(rclcpp::ok()) {node->pub_count();rate.sleep();rclcpp::spin_some(node);}return 0;
}
cmake如下:
cmake_minimum_required(VERSION 3.8)
project(pub_app)#set(CMAKE_C_COMPILER /usr/bin/gcc-12)
#set(CMAKE_CXX_COMPILER /usr/bin/g++-12)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-Wall -Wextra -Wpedantic)# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(common_proto_data REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)message("project source:${PROJECT_SOURCE_DIR}")
include_directories(${PROJECT_SOURCE_DIR}/../../../third_party/toml/include)
include_directories(${PROJECT_SOURCE_DIR}/../../../third_party/ullib/include)if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# the following line skips the linter which checks for copyrights# comment the line when a copyright and license is added to all source filesset(ament_cmake_copyright_FOUND TRUE)# the following line skips cpplint (only works in a git repo)# comment the line when this package is in a git repo and when# a copyright and license is added to all source filesset(ament_cmake_cpplint_FOUND TRUE)ament_lint_auto_find_test_dependencies()
endif()
#target_link_libraries(publisher PUBLIC ${PROJECT_SOURCE_DIR}/third_party/toml/lib/libtoml11.a)
#add_executable(publisher src/pub_example.cpp)
add_executable(publisher src/shared_mem_pub.cpp)
ament_target_dependencies(publisher rclcpp std_msgs common_proto_data)
#target_link_directories(publisher PUBLIC ${PROJECT_SOURCE_DIR}/../../../third_party/toml/include)
target_link_libraries(publisher ${PROJECT_SOURCE_DIR}/../../../third_party/toml/lib/libtoml11.a${PROJECT_SOURCE_DIR}/../../../third_party/ullib/lib/libullib.a)#target_link_libraries(publisher PUBLIC ${PROJECT_SOURCE_DIR}/../../../third_party/toml/lib)install(TARGETS publisher DESTINATION ${PROJECT_SOURCE_DIR}/../../../output/ros2/${PROJECT_NAME}/lib/${PROJECT_NAME})
#install(DIRECTORY ${PROJECT_SOURCE_DIR}/../../../third_party/toml/lib DESTINATION ./)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/cfg DESTINATION ${PROJECT_SOURCE_DIR}/../../../output/ros2/pub_app/)ament_package()
案例的代码利用了编写的msg文件来指定发布的内容,这里用此案例是为了说明共享内存和msg文件的兼容性,可以直接调用msg中定义的count变量
msg文件如下, 命名是Pubapp2Subapp.msg
int64 count
ros2的msg文件有很多坑,我们在下一篇文章会仔细讲解;