> For the complete documentation index, see [llms.txt](https://hanxu.gitbook.io/blogs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hanxu.gitbook.io/blogs/ros/ros-jiao-cheng-hua-ti-tong-xin.md).

# 【ROS教程】话题通信

@[TOC](https://github.com/UnderTurrets/notes/blob/master/ROS/文章目录/README.md)

***

## 1.流程

话题通信是ROS中使用频率最高的一种通信模式，话题通信是基于发布订阅模式的，也即:一个节点发布消息，另一个节点订阅该消息。在ROS中，实现话题通信只需要如下几步：

1. **确定要发布的数据类型，一般都需要自定义.msg文件，修改好CMakeLists.txt文件和package.xml文件并重编译**
2. **编写发布方和订阅方的cpp文件，修改好CMakeLists.txt文件并重编译**
3. 分别启动发布方节点和订阅方节点，**顺序无所谓**

## 2.自定义发布数据

在 ROS 通信协议中，数据载体是一个较为重要组成部分，ROS 中通过 std\_msgs 封装了一些原生的数据类型。但是，这些数据一般只包含一个 data 字段，结构的单一意味着功能上的局限性，当传输一些复杂的数据， std\_msgs 由于描述性较差而显得力不从心，这种场景下必须通过编写.msg文件来自定义消息类型。

### 2.1 std\_msgs内置类型

* 内置类型与 C++ 和 Python 中的对应关系:

| Primitive Type | C++           | Python         |
| -------------- | ------------- | -------------- |
| bool           | uint8\_t      | bool           |
| int8           | int8\_t       | int            |
| uint8          | uint8\_t      | int            |
| int16          | int16\_t      | int            |
| uint16         | uint16\_t     | int            |
| int32          | uint32\_t     | int            |
| uint64         | uint64\_t     | long int       |
| float32        | float         | float          |
| float64        | double        | float          |
| string         | std::string   | str bytes      |
| time           | ros::Time     | rospy.Time     |
| duration       | ros::Duration | rospy.Duration |

* 内置类型的数组与 C++ 和 Python 中的对应关系:

| Primitive Type  | C++                                   | Python |
| --------------- | ------------------------------------- | ------ |
| variable-length | std::vector                           | tuple  |
| fixed-length    | `boost::array<T, length>`或std::vector | tuple  |

### 2.2 编写.msg文件

示例如下：

```txt
#文件名Person.msg
string name
uint16 age
float64 height
```

### 2.3 修改package.xml文件

* 查看是否存在如下编译依赖

```xml
<build_depend>message_generation</build_depend>
```

* 查看是否存在如下执行依赖

```xml
<exec_depend>message_generation</exec_depend>
```

#### 2.3.1 完整的package.xml文件

```xml
<?xml version="1.0"?>
<package format="2">
  <name>chat</name>
  <version>0.0.0</version>
  <description>The chat package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <!-- Example:  -->
  <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
  <maintainer email="xu736946693@todo.todo">xu736946693</maintainer>


  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>TODO</license>


  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <!-- Example: -->
  <!-- <url type="website">http://wiki.ros.org/chat</url> -->


  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <!-- Example: -->
  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->


  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <!-- Examples: -->
  <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
  <!--   <depend>roscpp</depend> -->
  <!--   Note that this is equivalent to the following: -->
  <!--   <build_depend>roscpp</build_depend> -->
  <!--   <exec_depend>roscpp</exec_depend> -->
  <!-- Use build_depend for packages you need at compile time: -->
  <!--   <build_depend>message_generation</build_depend> -->
  <!-- Use build_export_depend for packages you need in order to build against this package: -->
  <!--   <build_export_depend>message_generation</build_export_depend> -->
  <!-- Use buildtool_depend for build tool packages: -->
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  <!-- Use exec_depend for packages you need at runtime: -->
  <!--   <exec_depend>message_runtime</exec_depend> -->
  <!-- Use test_depend for packages you need only for testing: -->
  <!--   <test_depend>gtest</test_depend> -->
  <!-- Use doc_depend for packages you need only for building documentation: -->
  <!--   <doc_depend>doxygen</doc_depend> -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>message_generation</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>message_generation</exec_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>
```

### 2.4 修改CMakeLists.txt文件

#### 2.4.1 修改find\_package指令

```cpp
# 需要加入 message_generation,必须有 std_msgs
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
```

#### 2.4.2 添加add\_message\_files指令

```cpp
## 配置 msg 源文件
add_message_files(
  FILES
  Person.msg
)
```

#### 2.4.3 添加generate\_messages指令

```cpp
generate_messages(
  DEPENDENCIES
  std_msgs
)
```

#### 2.4.4 修改catkin\_package指令

```cpp
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)
```

其中，**add\_message\_files指令必须要在generate\_messages指令的前面**，然后在工作空间目录编译即可。

### 2.5 查看头文件

经过以上几步，在`${workspace}/devel/include/${package}/`目录下应该会出现一个头文件，如图：

```shell
xu736946693@ubuntu:~/Desktop/ROS-exercise/devel/include/chat$ tree
.
└── Person.h
```

* **如果没有出现，是无法进行接下来的步骤的。这时，只需要把`${workspace}/`目录下的build目录和devel目录全部删除，然后重新编译即可。**

```shell
rm -rf build/
rm -rf devel/
catkin_make
```

## 3.编写cpp文件

### 3.1 功能包目录文件树

```shell
xu736946693@ubuntu:~/Desktop/ros-test/src/chat$ tree
.
├── CMakeLists.txt
├── include
├── msg
│   └── Person.msg
├── package.xml
└── src
    ├── listener.cpp
    └── publisher.cpp

3 directories, 5 files
```

### 3.2 修改CMakeLists.txt文件

#### 3.2.1 添加add\_executable指令

```cpp
add_executable(publisher
        src/publisher.cpp)
add_executable(listener
        src/listener.cpp)
```

#### 3.2.2 添加add\_dependencies指令

```cpp
add_dependencies(publisher ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(listener ${PROJECT_NAME}_generate_messages_cpp)
```

#### 3.2.3 添加target\_link\_libraries指令

```cpp
target_link_libraries(person_talker
  ${catkin_LIBRARIES}
)
target_link_libraries(person_listener
  ${catkin_LIBRARIES}
)
```

#### 3.2.4 完整的CMakeLists.txt

* 其中很多语句都是catkin\_make自动生成的

```cpp
cmake_minimum_required(VERSION 3.0.2)
project(chat)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)


## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

# Generate messages in the 'msg' folder
 add_message_files(
   FILES
   Person.msg
 )

## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
# 生成消息时依赖于 std_msgs
generate_messages(
        DEPENDENCIES
        std_msgs
)


################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
#执行时依赖
catkin_package(
        #  INCLUDE_DIRS include
        #  LIBRARIES demo02_talker_listener
        CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
        #  DEPENDS system_lib
)


###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/pkg1.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/pkg1_node.cpp)
add_executable(publisher
        src/publisher.cpp)
add_executable(listener
        src/listener.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(publisher ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(listener ${PROJECT_NAME}_generate_messages_cpp)

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_pkg1.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
target_link_libraries(publisher
        ${catkin_LIBRARIES}
        )

target_link_libraries(listener
        ${catkin_LIBRARIES}
        )

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
```

### 3.3 发布方cpp

示例如下：

```cpp
/*
    需求: 实现基本的话题通信，一方发布数据，一方接收数据，
         实现的关键点:
         1.发送方
         2.接收方
         3.数据

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 发布者 对象
        5.组织被发布的数据，并编写逻辑发布数据

*/
// 1.包含头文件
#include "ros/ros.h"
#include "chat/Person.h"

int main(int argc, char  *argv[])
{
    //设置编码
    setlocale(LC_ALL,"");

    //2.初始化 ROS 节点:命名(唯一)
    // 参数1和参数2 后期为节点传值会使用
    // 参数3 是节点名称，是一个标识符，需要保证运行后，在 ROS 网络拓扑中唯一
    ros::init(argc,argv,"talker");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能

    //4.实例化 发布者 对象
    //泛型: 发布的消息类型
    //参数1: 要发布到的话题
    //参数2: 队列中最大保存的消息数，超出此阀值时，先进的先销毁(时间早的先销毁)
    ros::Publisher pub = nh.advertise<chat::Person>("chatter",10);

    //5.组织被发布的数据，并编写逻辑发布数据
    //数据(动态组织)
    chat::Person p;
    p.name = "sunwukong";
    p.age = 2000;
    p.height = 1.45;

    //逻辑(一秒1次)
    ros::Rate r(1);

    //节点不死
    while (ros::ok())
    {
        //发布消息
        pub.publish(p);
        //加入调试，打印发送的消息
        ROS_INFO("我叫:%s,今年%d岁,高%.2f米", p.name.c_str(), p.age, p.height);p.age++;
        //根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率；
        r.sleep();
        //暂无应用
        ros::spinOnce();
    }


    return 0;
}
```

### 3.4 订阅方cpp

示例如下：

```cpp
/*
    需求: 实现基本的话题通信，一方发布数据，一方接收数据，
         实现的关键点:
         1.发送方
         2.接收方
         3.数据


    消息订阅方:
        订阅话题并打印接收到的消息

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 订阅者 对象
        5.处理订阅的消息(回调函数)
        6.设置循环调用回调函数

*/
// 1.包含头文件
#include "ros/ros.h"
#include "chat/Person.h"

void doMsg(const chat::Person::ConstPtr& person_p){
    ROS_INFO("订阅的人信息:%s, %d, %.2f", person_p->name.c_str(), person_p->age, person_p->height);
}
int main(int argc, char  *argv[])
{
    //设置编码
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;

    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<chat::Person>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //     6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据，并调用回调函数处理

    return 0;
}
```

## 4.效果

![](https://cdn.jsdelivr.net/gh/UnderTurrets/ImageHosting@master/ros_topicChat.png)

* 如果找不到可执行文件，就把工作目录下的build文件夹和devel文件夹删了重新编译。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hanxu.gitbook.io/blogs/ros/ros-jiao-cheng-hua-ti-tong-xin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
