环境说明

ROS版本:ROS Kinetic Kame

Ubuntu版本:Ubuntu 16.04.6 LTS,(lsb_release -a

Python版本:Python 2.7.12

CMake 版本:3.5.1

前文已经讲过

  1. ROS 工作空间和包
  2. ROS 消息(msg)和服务(srv)
  3. ROS 概述
  4. 消息的发布者(Publisher)

以上是这篇文章的基础

以下所有文件都创建在 ws 的工作空间下

创建只订阅std消息的节点

c++版本

  1. src目录下创建一个 subscriber.cc 文件,将以下代码添加到该文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 导入 ros 头文件
#include <ros/ros.h>

int main(int argc, char **argv) {
// 创建 ros 的节点名称
char node_name[] = "demo_subscriber";
// 初始化 ros 节点
ros::init(argc, argv, node_name);
// 创建一个节点对象
ros::NodeHandle node;
// TODO 将业务逻辑写在此处

// 开启 ros 的运行时循环
ros::spin();
return 0;
}
  1. CMakeLists.txt 中添加 add_executabletarget_link_libraries
1
2
3
4
5
6
7
8
9
10
# 添加一个可执行程序
add_executable(
demo_subscriber
src/subscriber.cc
)
# 添加该可执行程序的依赖库
target_link_libraries(
subscriber
${catkin_LIBRARIES}
)
  1. 创建 Subscriber 对象

    1. 导入要订阅的消息的头文件
    1
    2
    // 先使用 std 库中的简易消息
    #include "std_msgs/String.h
    1. 创建对象,并订阅消息
    1
    2
    3
    4
    // 创建一个 topic 的名称
    char topic_name[] = "demo_topic";
    // 创建一个 Subscriber 对象
    const ros::Subscriber &sub = node.subscribe(topic_name, 1000, subCallback);
    1. 创建回调函数
    1
    2
    3
    4
    void subCallback(const std_msgs::String::ConstPtr &msg) {
    // 一般写业务逻辑,此处打印接收到的数据
    ROS_INFO_STREAM(msg->data);
    }
  2. 编译该节点,在工作空间目录下

1
catkin_make

python版本

  1. package 目录下创建一个名为 scripts 的文件夹
  2. 创建一个名为 subscriber.py 的文件,将以下代码添加到该文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# coding: utf-8

# 导入 rospy 依赖
import rospy

if __name__ == "__main__":
# 节点名称
node_name = "demo_subscriber"
# 初始化 ros 节点
rospy.init_node(node_name)
# todo 将业务逻辑写在此处
# 开启 ros 运行时循环
rospy.spin()
  1. 创建 Subscriber 对象

    1. 导入要订阅的消息的头文件
    1
    2
    # 先使用 std 库中的简易消息
    from std_msgs.msg import String
    1. 创建对象,并订阅消息
    1
    2
    3
    4
    # 创建一个 topic 名称
    topic_name = "demo_topic"
    # 创建一个 Subscriber 对象
    sub = rospy.Subscriber(topic_name, String, subCallback)
    1. 创建回调函数
    1
    2
    3
    4
    # 注意这个赋默认值是为了使后面有代码提示,实际过程中,msg 被传入的,所以不影响结果
    def subCallback(msg=String()):
    # 一般写业务逻辑,此处打印接收到的数据
    print (msg.data)
  1. 给 subscriber.py 赋予可执行权限
1
chmod a+x src/demo_topic/scripts/subscriber.py

使用 rqt_publisher 工具调试

  1. 将当前工作空间的环境变量添加到bashzsh
1
2
3
# 根据使用不同的 shell 使用不同的环境变量,两者选其一
source devel/setup.bash # 使用 bash
source devel/setup.zsh # 使用 zsh
  1. 运行 demo_publisher

    1. c++ 程序
    1
    rosrun demo_topic demo_subscriber
    1. python 程序
    1
    rosrun demo_topic subscriber.py
  2. 打开 rqt_publisher 工具

1
rosrun rqt_publisher rqt_publisher
  1. 选择我们对应的Topicdemo_topicType选择std_msgs/String
  2. 勾选就会一直发送数据,或者右键点击Publisher Selected Once 可以发送一次数据

创建订阅自定义消息的节点

我们使用在ros消息讲过的demo_msgs包中的Team.msg来传输数据

c++版本

  1. src目录下创建一个 subscriber1.cc 文件,将以下代码添加到该文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 导入 ros 头文件
#include <ros/ros.h>

int main(int argc, char **argv) {
// 创建 ros 的节点名称
char node_name[] = "demo_subscriber";
// 初始化 ros 节点
ros::init(argc, argv, node_name);
// 创建一个节点对象
ros::NodeHandle node;
// TODO 将业务逻辑写在此处

// 开启 ros 的运行时循环
ros::spin();
return 0;
}
  1. CMakeLists.txt 中添加 add_executabletarget_link_libraries
1
2
3
4
5
6
7
8
9
10
# 添加一个可执行程序
add_executable(
demo_subscriber1
src/subscriber1.cc
)
# 添加该可执行程序的依赖库
target_link_libraries(
demo_subscriber1
${catkin_LIBRARIES}
)
  1. CMakeLists.txt文件的find_package中添加demo_msgs的依赖
1
2
3
4
5
6
find_package(catkin REQUIRED COMPONENTS
roscpp
rosmsg
rospy
demo_msgs
)
  1. 创建 Publisher 对象

    1. 导入要发送的消息的头文件

      1
      #include "demo_msgs/Team.h"
    2. 创建对象,并发送消息

    1
    2
    3
    4
    // 创建一个 topic 的名称
    char topic_name[] = "demo_topic";
    // 创建一个 Subscriber 对象
    const ros::Subscriber &sub = node.subscribe(topic_name, 1000, subCallback);
    1. 创建回调函数
    1
    2
    3
    4
    void subCallback(const demo_msgs::Team::ConstPtr &msg) {
    // 一般写业务逻辑,此处打印接收到的数据
    ROS_INFO_STREAM(msg->name);
    }
  2. 编译该节点,在工作空间目录下

1
catkin_make

python版本

  1. package 目录下创建一个名为 scripts 的文件夹
  2. 创建一个名为 subscriber1.py 的文件,将以下代码添加到该文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# coding: utf-8

# 导入 rospy 依赖
import rospy

if __name__ == "__main__":
# 节点名称
node_name = "demo_subscriber"
# 初始化 ros 节点
rospy.init_node(node_name)
# todo 将业务逻辑写在此处
# 开启 ros 运行时循环
rospy.spin()
  1. 创建 Subscriber 对象

    1. 导入要订阅的消息的头文件
    1
    from demo_msgs.msg import Team
    1. 创建对象,并订阅消息
    1
    2
    3
    4
    # 创建一个 topic 名称
    topic_name = "demo_topic"
    # 创建一个 Subscriber 对象
    sub = rospy.Subscriber(topic_name, Team, subCallback)
    1. 创建回调函数
    1
    2
    3
    4
    # 注意这个赋默认值是为了使后面有代码提示,实际过程中,msg 被传入的,所以不影响结果
    def subCallback(msg=Team()):
    # 一般写业务逻辑,此处打印接收到的数据
    print (msg.name)
  1. 给 subscriber.py 赋予可执行权限
1
chmod a+x src/demo_topic/scripts/subscriber1.py

使用 rqt_publisher 工具调试

  1. 将当前工作空间的环境变量添加到bashzsh
1
2
3
# 根据使用不同的 shell 使用不同的环境变量,两者选其一
source devel/setup.bash # 使用 bash
source devel/setup.zsh # 使用 zsh
  1. 运行 demo_publisher

    1. c++ 程序
    1
    rosrun demo_topic demo_subscriber
    1. python 程序
    1
    rosrun demo_topic subscriber.py
  2. 打开 rqt_publisher 工具

1
rosrun rqt_publisher rqt_publisher
  1. 选择我们对应的Topicdemo_topicType选择demo_msgs/Team
  2. 勾选就会一直发送数据,或者右键点击Publisher Selected Once 可以发送一次数据

总结

  1. 两种订阅消息几乎是一样的,因为在我们创建包的时候已经将std_msgs的依赖导入了,所以导致我们就不需要添加依赖。
  2. 使用对应消息的时候要添加对应消息的依赖。
  3. 回调函数默认在主线程中进行,即ros::spin()rospy.spin()会卡死主线程,进入ros的运行时
  4. 如果有别的运行时,如 qt(以后会详细介绍qt中的用法),则需要使用异步接收ros的运行时消息,或者在 qt 中使用spinOnce()(仅在c++中需要)

代码块

C++

ros_create_subscriber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"ros_create_subscriber": {
"prefix": "ros_create_subscriber",
"body": [
"#include \"${1: topic_class}\"",
"",
"void callback(const ${2: topic_class}::ConstPtr &msg) {",
" // 一般写业务逻辑,此处打印接收到的数据",
" ROS_INFO_STREAM(msg);",
"}",
"// 创建一个 topic 的名称",
"// todo 填写 topic 名称",
"char topic_name[] = \"${3}\";",
"// 创建一个 Subscriber 对象",
"const ros::Subscriber &sub = node.subscribe(topic_name, 1000, callback);",
]
}

Python

ros_create_subscriber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"ros_create_subscriber": {
"prefix": "ros_create_subscriber",
"body": [
"# todo 填写 msg 的类型",
"from ${1: msg_class}.msg import ${2: msg_type}",
"",
"",
"def subCallback(msg=${2:msg_type}()):",
" # 一般写业务逻辑,此处打印接收到的数据",
" print (msg)",
"",
"",
"# 创建一个 topic 名称",
"# todo 填写 topic 名称",
"topic_name = \"${3: topic_name}\"",
"# 创建一个 Subscriber 对象",
"sub = rospy.Subscriber(topic_name, ${2: msg_type}, subCallback)",
],
}

附录

创建只发布std消息的节点的完整代码

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "std_msgs/String.h"
// 导入 ros 头文件
#include <ros/ros.h>

void subCallback(const std_msgs::String::ConstPtr &msg) {
// 一般写业务逻辑,此处打印接收到的数据
ROS_INFO_STREAM(msg->data);
}

int main(int argc, char **argv) {
// 创建 ros 的节点名称
char node_name[] = "demo_subscriber";
// 初始化 ros 节点
ros::init(argc, argv, node_name);
// 创建一个节点对象
ros::NodeHandle node;
// 创建一个 topic 的名称
char topic_name[] = "demo_topic";
// 创建一个 Subscriber 对象
const ros::Subscriber &sub = node.subscribe(topic_name, 1000, subCallback);
// 进入一个简单的事件循环
ros::spin();
return 0;
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
# coding: utf-8

# 导入 rospy 依赖
import rospy
from std_msgs.msg import String


def subCallback(msg=String()):
# 一般写业务逻辑,此处打印接收到的数据
print (msg.data)


if __name__ == "__main__":
# 节点名称
node_name = "demo_subscriber"
# 初始化 ros 节点
rospy.init_node(node_name)
# 创建一个 topic 名称
topic_name = "demo_topic"
# 创建一个 Subscriber 对象
sub = rospy.Subscriber(topic_name, String, subCallback)
# 开启 ros 运行时循环
rospy.spin()

创建发布自定义消息的节点

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "demo_msgs/Team.h"
// 导入 ros 头文件
#include <ros/ros.h>

void subCallback(const demo_msgs::Team::ConstPtr &msg) {
// 一般写业务逻辑,此处打印接收到的数据
ROS_INFO_STREAM(msg->name);
}

int main(int argc, char **argv) {
// 创建 ros 的节点名称
char node_name[] = "demo_subscriber";
// 初始化 ros 节点
ros::init(argc, argv, node_name);
// 创建一个节点对象
ros::NodeHandle node;
// 创建一个 topic 的名称
char topic_name[] = "demo_topic";
// 创建一个 Subscriber 对象
const ros::Subscriber &sub = node.subscribe(topic_name, 1000, subCallback);
// 开启 ros 的运行时循环
ros::spin();
return 0;
}

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python
# coding: utf-8

# 导入 rospy 依赖
import rospy
from demo_msgs.msg import Team


def subCallback(msg=Team()):
# 一般写业务逻辑,此处打印接收到的数据
print (msg.name)


if __name__ == "__main__":
# 节点名称
node_name = "demo_subscriber"
# 初始化 ros 节点
rospy.init_node(node_name)
# 创建一个 topic 名称
topic_name = "demo_topic"
# 创建一个 Subscriber 对象
sub = rospy.Subscriber(topic_name, Team, subCallback)
# 开启 ros 运行时循环
rospy.spin()

评论