ROS是机器人操作系统的简称,本文介绍ROS应用开发入门,话题消息的定义和使用。发布者Publisher 和订阅者Subscribler 之间通讯都使用话题消息(topic message)。本文先定义一个话题消息,然后一个发布者发送,另一个订阅者接收。先是c++代码,然后是python 代码,你也可选择只看一种你熟悉的。
在前面2文(ROS 应用开发入门 发布者Publisher的编程 和 ROS 应用开发入门 订阅者Subscribler的编程)中,为了减少复杂性,我们使用了和仿真小乌龟通讯,话题消息也是现成的,但本文就要介绍自定义话题消息,这是符合我的实际应用需求。希望你看过前面2文,至少定义好了功能包。
自定义话题消息
在工程包目录下新建一个目录msg
cd ~/catkin_ws/src/learning_topic
mkdir msg
cd msg
然后新建一个Person.msg 文件
nano Person.msg
文件内容为:
string name
uint8 age
uint8 sex
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
回到工程包目录下,打开并编辑package.xml:
cd ~/catkin_ws/src/learning_topic
nano package.xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
添加的位置是参考下面位置,就是<export> 前面注释上面:
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>turtlesim</exec_depend>
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
然后打开并编辑CMakeLists.txt,总共3处
nano CMakeLists.txt
先在find_package 里添加 message_generation
添加后如下所示:
find_package(catkin REQUIRED COMPONENTS
geometry_msgs
roscpp
rospy
std_msgs
turtlesim
message_generation
)
添加如下代码,也可以作为2行,
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
添加位置参看如下:
## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# geometry_msgs# std_msgs
# )
add_message_files(
FILES
Person.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
在catkin_package里,取消 CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim行的注释,并添加 message_runtime
效果如下:
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES learning_topic
CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
# DEPENDS system_lib
)
修改完这2个文件后,就在工作空间编译。
编译必须回到 ~/catkin_ws 目录下
cd ~/catkin_ws
catkin_make
这样话题信息就定义准备好了。
看看编译最后阶段信息
Scanning dependencies of target learning_topic_generate_messages_py
[ 36%] Generating Python from MSG learning_topic/Person
[ 45%] Linking CXX executable /home/leon/catkin_ws/devel/lib/learning_topic/pose_subscriber
[ 54%] Generating Python msg __init__.py for learning_topic
[ 54%] Built target pose_subscriber
Scanning dependencies of target learning_topic_generate_messages_cpp
[ 63%] Generating C++ code from learning_topic/Person.msg
[ 63%] Built target learning_topic_generate_messages_py
Scanning dependencies of target learning_topic_generate_messages_eus
[ 72%] Generating EusLisp code from learning_topic/Person.msg
[ 72%] Built target learning_topic_generate_messages_cpp
Scanning dependencies of target learning_topic_generate_messages_lisp
[ 81%] Generating Lisp code from learning_topic/Person.msg
[ 90%] Generating EusLisp manifest code for learning_topic
[ 90%] Built target learning_topic_generate_messages_lisp
Scanning dependencies of target learning_topic_generate_messages_nodejs
[100%] Generating Javascript code from learning_topic/Person.msg
[100%] Built target learning_topic_generate_messages_nodejs
[100%] Built target learning_topic_generate_messages_eus
Scanning dependencies of target learning_topic_generate_messages
[100%] Built target learning_topic_generate_messages
leon@ubuntu:~/catkin_ws$
看来是为多种语言都准备好了。
c++ 验证源代码
要使用话题信息来验证,需要发布者和订阅者2个程序,我们都放在 工程目录的src目录下
转到src 目录下,分别建立文件person_publisher.cpp和 person_subscriber.cpp
~/catkin_ws/src/learning_topic/src
nano person_publisher.cpp
文件内容是:
/**
* 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "person_publisher");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
// 设置循环的频率
ros::Rate loop_rate(1);
int count = 0;
while (ros::ok())
{
// 初始化learning_topic::Person类型的消息
learning_topic::Person person_msg;
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = learning_topic::Person::male;
// 发布消息
person_info_pub.publish(person_msg);
ROS_INFO("Publish Person Info: name:%s age:%d sex:%d",
person_msg.name.c_str(), person_msg.age, person_msg.sex);
// 按照循环频率延时
loop_rate.sleep();
}
return 0;
}
nano person_subscriber.cpp
文件内容是:
/**
* 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "person_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
2个文件准备好了,下面
配置cmake文件
在 ~/catkin_ws/src/learning_topic/ 目录下,有个CMakeLists.txt 文件,我们需要修改这个文件
cd ~/catkin_ws/src/learning_topic/
nano CMakeLists.txt
在这个文件中添加下面几行,
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
添加的位置是参考下面位置,就是 ## install ## 前面,然后上一个发布者,订阅者添加各2行的后面:
## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
add_executable(velocity_publisher src/velocity_publisher.cpp)
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})
add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
#############
## Install ##
#############
保存,退出
这样编译配置就完成了。
编译和运行测试
编译必须回到 ~/catkin_ws 目录下
cd ~/catkin_ws
catkin_make
编译后应该source 一次:
source devel/setup.bash
如果编译有错,就要排除错误,然后就运行测试。
打开一个终端,启动ros,执行
roscore
再打开一个终端,启动订阅者,执行
rosrun learning_topic person_subscriber
再打开一个终端,启动发布者:
rosrun learning_topic person_publisher
程序运行结果如下:
c++ 验证成功。
python 验证源代码
要使用话题信息来验证,需要发布者和订阅者2个程序,我们都放在 工程目录的scripts 目录下
转到scripts 目录下,分别建立文件person_publisher.py 和 person_subscriber.py
~/catkin_ws/src/learning_topic/scripts
nano person_publisher.py
文件内容是:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
import rospy
from learning_topic.msg import Person
def velocity_publisher():
# ROS节点初始化
rospy.init_node('person_publisher', anonymous=True)
# 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
person_info_pub = rospy.Publisher('/person_info', Person, queue_size=10)
#设置循环的频率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
# 初始化learning_topic::Person类型的消息
person_msg = Person()
person_msg.name = "Tom";
person_msg.age = 18;
person_msg.sex = Person.male;
# 发布消息
person_info_pub.publish(person_msg)
rospy.loginfo("Publsh person message[%s, %d, %d]",
person_msg.name, person_msg.age, person_msg.sex)
# 按照循环频率延时
rate.sleep()
if __name__ == '__main__':
try:
velocity_publisher()
except rospy.ROSInterruptException:
pass
nano person_subscriber.py
文件内容是:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
import rospy
from learning_topic.msg import Person
def personInfoCallback(msg):
rospy.loginfo("Subcribe Person Info: name:%s age:%d sex:%d",
msg.name, msg.age, msg.sex)
def person_subscriber():
# ROS节点初始化
rospy.init_node('person_subscriber', anonymous=True)
# 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
rospy.Subscriber("/person_info", Person, personInfoCallback)
# 循环等待回调函数
rospy.spin()
if __name__ == '__main__':
person_subscriber()
运行测试
因为python 不需要编译,但需要设置为可执行文件
chmod +x *.py
打开一个终端,启动ros,执行
roscore
先source 一下:
source ~/catkin_ws/devel/setup.bash
再打开一个终端,启动订阅者,执行
rosrun learning_topic person_subscriber.py
再打开一个终端,启动发布者:
rosrun learning_topic person_publisher.py
程序运行结果如下:
python 验证成功。
显示信息流关系
再打开一个新的终端,并输入命令:
rqt_graph
可以看到如下信息关系图:
源代码也可以在 https://github.com/huchunxu/ros_21_tutorials 下载
全文介绍到此。