ROS2 话题通信——自定义消息类型(C++)

1、创建工作空间

        首先,在根目录下面创建一个新的工作空间test_w和新的功能包topic_pkg,然后新建脚本文件topic_interface_pub.cpp和topic_interface_sub.cpp,然后用VScode打开test_w文件夹。

mkdir -p ./text_w/src
cd text_w/src
ros2 pkg create topic_pkg --build-type ament_cmake --dependencies rclcpp std_msgs --node-name topic_interface_pub
touch ./topic_pkg/src/topic_interface_sub.cpp

2、测试一下简单的话题通信

        编写发布者节点,复制下面代码到 topic_interface_pub.cpp

/***
功能:以某个固定频率发布 hello world 
***/


#include "rclcpp/rclcpp.hpp"          // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"    // 字符串消息类型

using namespace std::chrono_literals;//设置持续时间相关

class PublisherNode : public rclcpp::Node
{
    public:
        PublisherNode(): Node("topic_helloworld_pub") // ROS2节点父类初始化
        {
            RCLCPP_INFO(this->get_logger(), "Create Publisher Finished !");
            // 创建发布者对象(消息类型、话题名、队列长度)
            publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10); 
            // 创建一个定时器,定时执行回调函数
            timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this));        
        }

    private:
        // 创建定时器周期执行的回调函数
        void timer_callback()                                                       
        {
          // 创建一个String类型的消息对象
          auto mssage = std_msgs::msg::String();   
          // 填充消息对象中的消息数据                                    
          mssage.data = "Hello World " + std::to_string(count++);
          // 发布话题消息                                                 
          RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", mssage.data.c_str()); 
          // 输出日志信息,提示已经完成话题发布   
          publisher_->publish(mssage);                                                
        }
        
        rclcpp::TimerBase::SharedPtr timer_;                             // 定时器指针
        rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;  // 发布者指针
        size_t count;                                                    // 计数器
};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                      
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                
    
    // 创建ROS2节点对象并进行初始化          
    rclcpp::spin(std::make_shared<PublisherNode>());  //挂起 
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                

    return 0;
}

        编写订阅者节点,复制下面代码到 topic_interface_sub.cpp

/***
功能:订阅发布者发布的 hello world 
***/



#include "rclcpp/rclcpp.hpp"                  // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"            // 字符串消息类型

using std::placeholders::_1;

class SubscriberNode : public rclcpp::Node
{
    public:
        SubscriberNode(): Node("topic_helloworld_sub")        // ROS2节点父类初始化
        {
            RCLCPP_INFO(this->get_logger(), "Create Subscriber Finished !");
            subscription_ = this->create_subscription<std_msgs::msg::String>(       
                "chatter", 10, std::bind(&SubscriberNode::topic_callback, this, _1));   // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
        }

    private:
        void topic_callback(const std_msgs::msg::String::SharedPtr msg) const                  // 创建回调函数,执行收到话题消息后对数据的处理
        {
            RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());       // 输出日志信息,提示订阅收到的话题消息
        }
        
        rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;         // 订阅者指针
};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                         
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                 
    
    // 创建ROS2节点对象并进行初始化            
    rclcpp::spin(std::make_shared<SubscriberNode>());     
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                  
    
    return 0;
}

       发现头文件报错

       点击编辑"includePath"设置

        添加/opt/ros/humble/**,点击空白处保存

        添加完后可以看见左边目录生成了.vscode文件夹,该文件夹里面存在一个c_cpp_properties.json文件,里面内容对应上面添加的路径

        然后修改CMakeListst.txt,如箭头所示

cmake_minimum_required(VERSION 3.8)
project(topic_pkg)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(topic_interface_pub src/topic_interface_pub.cpp)

add_executable(topic_interface_sub src/topic_interface_sub.cpp)


target_include_directories(topic_interface_pub PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
target_compile_features(topic_interface_pub PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17


ament_target_dependencies(
  topic_interface_pub
  "rclcpp"
  "std_msgs"
)

ament_target_dependencies(
  topic_interface_sub
  "rclcpp"
  "std_msgs"
)


install(TARGETS topic_interface_pub
  DESTINATION lib/${PROJECT_NAME})

install(TARGETS topic_interface_sub
  DESTINATION lib/${PROJECT_NAME})


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 files
  set(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 files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

         编译测试一下,分别打开两个终端,在text_w目录下,进行编译和运行对应节点

colcon build
source install/setup.bash
ros2 run topic_pkg topic_interface_pub 



colcon build
source install/setup.bash
ros2 run topic_pkg topic_interface_sub 

        测试成功 

3、自定义接口功能包

        进入src目录,然后创建接口功能包interface_pkg,然后在 interface_pkg功能包下创建msg文件夹,并且创建一个Student.msg文件,注意该文件名首字母必须大写

cd ./src/
ros2 pkg create interface_pkg --build-type ament_cmake
mkdir -p ./interface_pkg/msg
touch ./interface_pkg/msg/Student.msg

        在Student.msg文件下自定义数据格式

string   name
int32    age
float64  height

         在CMakeLists.txt中添加以下代码

# 编译的依赖包
find_package(rosidl_default_generators REQUIRED)


#为接口文件生成源代码,接口名字首字母必须大写
rosidl_generate_interfaces(${PROJECT_NAME}
    "msg/Student.msg" 
)

        在package.xml中添加以下代码

  <!--编译依赖-->
  <build_depend>rosidl_default_generators</build_depend>
  <!--执行依赖-->
  <exec_depend>rosidl_default_runtime</exec_depend>
  <!--声明当前所属的功能包组-->
  <member_of_group>rosidl_interface_packages</member_of_group>

        编译一下interface_pkg功能包

cd ..
colcon build --packages-select interface_pkg

         打开install/interface_pkg目录,可以看见在include目录下面生成了C++对应的自定义接口头文件,在local目录下生成了Python对应的自定义接口库

         重新编写发布者节点

/***
功能:以某个固定频率发布学生信息
***/


#include "rclcpp/rclcpp.hpp"                // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"          // 字符串消息类型
#include "interface_pkg/msg/student.hpp"    // 自定义接口

using namespace std::chrono_literals;       //设置持续时间相关

class PublisherNode : public rclcpp::Node
{
    public:
        PublisherNode(): Node("topic_interface_pub") // ROS2节点父类初始化
        {
            // 创建发布者对象(消息类型、话题名、队列长度)
            publisher_ = this->create_publisher<interface_pkg::msg::Student>("topic_interface", 10); 
            // 创建一个定时器,定时执行回调函数
            timer_ = this->create_wall_timer(500ms, std::bind(&PublisherNode::timer_callback, this));        
        }

    private:
        // 创建定时器周期执行的回调函数
        void timer_callback()                                                       
        {
          // 创建一个Student类型的消息对象
          auto stu = interface_pkg::msg::Student(); 

          stu.name = "小明" ; 
          stu.age = 8;
          stu.height = 1.8;

          // 发布话题消息 
          publisher_->publish(stu);                        
                                 
          // 输出日志信息,提示已经完成话题发布   
          RCLCPP_INFO(this->get_logger(), "发布的消息: '%s','%d','%.2f'",stu.name.c_str(),stu.age,stu.height);                                             
        }
        
        rclcpp::TimerBase::SharedPtr timer_;                                   // 定时器指针

        rclcpp::Publisher<interface_pkg::msg::Student>::SharedPtr publisher_;  // 发布者指针

};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                      
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                
    
    // 创建ROS2节点对象并进行初始化          
    rclcpp::spin(std::make_shared<PublisherNode>());  
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                

    return 0;
}

         重新编写订阅者节点

/***
功能:订阅发布者发布的学生信息
***/


#include "rclcpp/rclcpp.hpp"                  // ROS2 C++接口库
#include "std_msgs/msg/string.hpp"            // 字符串消息类型
#include "interface_pkg/msg/student.hpp"      // 自定义接口


using std::placeholders::_1;

class SubscriberNode : public rclcpp::Node
{
    public:
        SubscriberNode(): Node("topic_interface_sub")        // ROS2节点父类初始化
        {
            subscription_ = this->create_subscription<interface_pkg::msg::Student>(       
                "topic_interface", 10, std::bind(&SubscriberNode::topic_callback, this, _1));   // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
        }

    private:
        void topic_callback(const interface_pkg::msg::Student &stu)                     // 创建回调函数,执行收到话题消息后对数据的处理
        {
            RCLCPP_INFO(this->get_logger(), "订阅到的消息: name='%s',age='%d',height='%.2f'",stu.name.c_str(),stu.age,stu.height); // 输出日志信息,提示订阅收到的话题消息
        }
        
        rclcpp::Subscription<interface_pkg::msg::Student>::SharedPtr subscription_;     // 订阅者指针
};

// ROS2节点主入口main函数
int main(int argc, char * argv[])                         
{
    // ROS2 C++接口初始化
    rclcpp::init(argc, argv);                 
    
    // 创建ROS2节点对象并进行初始化            
    rclcpp::spin(std::make_shared<SubscriberNode>());     
    
    // 关闭ROS2 C++接口
    rclcpp::shutdown();                                  
    
    return 0;
}

         修改topic_pkg目录下面的CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(topic_pkg)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

find_package(interface_pkg REQUIRED)

add_executable(topic_interface_pub src/topic_interface_pub.cpp)

add_executable(topic_interface_sub src/topic_interface_sub.cpp)


target_include_directories(topic_interface_pub PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)
target_compile_features(topic_interface_pub PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17


ament_target_dependencies(
  topic_interface_pub
  "rclcpp"
  "std_msgs"
  "interface_pkg"
)

ament_target_dependencies(
  topic_interface_sub
  "rclcpp"
  "std_msgs"
  "interface_pkg"
)


install(TARGETS topic_interface_pub
  DESTINATION lib/${PROJECT_NAME})

install(TARGETS topic_interface_sub
  DESTINATION lib/${PROJECT_NAME})


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 files
  set(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 files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

         修改topic_pkg目录下面的package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>topic_pkg</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="saw@todo.todo">saw</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>rclcpp</depend>
  <depend>std_msgs</depend>

  <depend>interface_pkg</depend>  <!--告诉colcon,编译之前要确保有interface_pkg存在-->

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

        分别开两个终端编译测试一下

colcon build --packages-select topic_pkg
source install/setup.bash
ros2 run topic_pkg topic_interface_pub 


source install/setup.bash
ros2 run topic_pkg topic_interface_sub

        可以看到测试成功,发布者成功发布消息,订阅者成功订阅到消息。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在ROS2中,如果要发布自定义消息类型,需要进行以下几个步骤。首先,需要在消息定义文件中定义自定义消息类型,该文件需要保存在消息目录中,并使用.msg文件扩展名。\[1\]然后,在发布节点的源代码中,使用ROS2的Publisher类来创建发布者对象,并指定自定义消息类型作为模板参数。例如:ros2::Publisher<my_msgs::MyMessage> pub = node.create_publisher<my_msgs::MyMessage>("my_topic", 10);\[2\]接下来,编写发布消息的逻辑,并使用发布者对象的publish方法来发布消息。最后,运行发布节点,即可发布自定义消息类型消息。 #### 引用[.reference_title] - *1* *2* [ROS笔记(6)——自定义消息类型](https://blog.csdn.net/weixin_40582034/article/details/129041958)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [ROS实践07 自定义消息调用python](https://blog.csdn.net/qq_42227760/article/details/130013586)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值