本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1.创个话题源码包topic_demo
$ cd ~/catkin_ws/src
$ catkin_create_pkg topic_demo roscpp rospy std_msgs
2.在包下新建msg文件夹自定义消息文件gps.msg
$ cd topic_demo/
$ mkdir msg
$ cd msg
$ vi gps.msg
gps.msg
float32 x float32 y string state
3.编写talker.cpp作为publisher节点
#include <ros/ros.h>
#include <topic_demo/gps.h>//自定义msg产生的头文件
int main(int argc, char** argv){
ros::init(argc, argv, "talker"); //解析参数,命名结点
ros::NodeHandle nh; //创建句柄,实例化node
topic_demo::gps msg; //创建gps消息
msg.x = 1.0;
msg.y = 1.0;
msg.state = "working";
ros::Publisher pub = nh.advertise<topic_demo::gps>("gps_info",1); //创建publisher,需要发布的消息类型为topic_demo::gps,往"gps_info"话题上发布消息,发送队列长度为1
ros::Rate loop_rate(1.0); //定义循环发布的频率1Hz,一秒钟变化一次,一秒钟发布一次
while(ros::ok){ //只要ros还没关闭就循环
msg.x = 1.03 * msg.x;
msg.y = 1.01 * msg.y;
ROS_INFO("Talker:GPS:x = %f, y = %f", msg.x, msg.y);//输出当前的msg
pub.publish(msg); //发布消息
loop_rate.sleep(); //根据前面自定义的发布评率(ros::Rate loop_rate(1.0);),sleep,休眠一秒钟,暂停一秒钟
}
return 0;
}
4.编写listener.cpp作为subscribe节点
#include <ros/ros.h>
#include <topic_demo/gps.h>
#include <std_msgs/Float32.h>
void gpsCallback(const topic_demo::gps::ConstPtr &msg)
{
std_msgs::Float32 distance; //计算离原点(0,0)的距离
distance.data = sqrt(pow(msg->x,2)+pow(msg->y,2));
ROS_INFO("Listener:Distance to origin = %f, state = %s", distance.data, msg->state.c_str())
}
int main(int argc, char** argv){
ros::init(argc, argv, "listener"); //解析参数,命名结点
ros::NodeHandle nh; //创建句柄,实例化node
//创建subscribe,1.需要订阅的话题名,2.消息缓存队列长度1(来即处理),3.回调函数(指针),处理收到的消息
ros::Subscriber sub = nh.subscribe("gps_info", 1 , gpsCallback);
ros::spin(); //反复调用当前可触发的回调函数,堵塞,用于触发topic、service的响应队列
//ros::spin()用于调用所有可触发的回调函数,将进入循环,不会返回,类似于在循环里反复调用spinOnce()
//而ros::spinOnce()只会去触发一次
return 0;
}
扩展:回调函数与spin()方法 回调函数在编程中是一种重要的方法,在维基百科上的解释是:
In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. 回调函数作为参数被传入到了另一个函数中(在本例中传递的是函数指针),在未来某个时刻(当有新的message到达),就会立即执行。
Subscriber接收到消息,实际上是先把消息放到一个队列中去,如图所示。队列的长度在Subscriber构建的时候设置好了。当有spin函数执行,就会去处理消息队列中队首的消息。
spin具体处理的方法又可分为阻塞/非阻塞,单线程/多线程,在ROS函数接口层面我们有4种spin的方式:
阻塞与非阻塞的区别我们已经讲了,下面来看看单线程与多线程的区别:
我们常用的spin()、spinOnce()是单个线程逐个处理回调队列里的数据。有些场合需要用到多线程分别处理,则可以用到MultiThreadedSpin()、AsyncMultiThreadedSpin()。
5.修改Cmake编译系统的规则文件CMakeLists.txt
1.添加依赖:message_generation, 第16行左右
## 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
message_generation
)
2.添加宏,第76行左右
## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# std_msgs
# )
#添加自定义的msg文件,告诉cmake编译系统message头文件.h用哪个.msg文件参考生成的,catkin在cmake之上新增的命令,指定从哪个消息文件生成
add_message_files(FILES gps.msg)
#标准消息库依赖,用来生成自定义msg对应的.h头文件
#catkin新增的命令,用于生成消息
#DEPENDENCIES后面指定生成msg需要依赖其他什么消息,由于gps.msg用到了flaot32这种ROS标准消息,因此需要再把std_msgs作为依赖
generate_messages(DEPENDENCIES std_msgs)
3.把俩个注释放开与加上message_runtime
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES topic_demo
#用于配置ROS和package配置文件和Cmake文件
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
#指定C/C++的头文件路径
include_directories(include ${catkin_INCLUDE_DIRS})
#生成可执行目标文件
add_executable(talker src/talker.cpp)
#添加依赖,必须有此局以生成msg
#表明在编译talker前,必须先生编译完成自定义消息
#必须添加add_dependencies,否则找不到自定义的msg产生的头文件
#add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(talker topic_demo_generate_messages_cpp)
target_link_libraries(talker ${catkin_LIBRARIES}) //链接
add_executable(listener src/listener.cpp)
add_dependencies(listener topic_demo_generate_messages_cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
#############
## Install ##
#############
6.修改软件包的描述文件package.xml
新版本format2
<!--编译依赖项 -->
<buildtool_depend>catkin</buildtool_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>
<build_export_depend>message_generation</build_export_depend>
<!-- 运行依赖项 -->
<exec_depend>roscpp</exec_depend>
<exec_depend>rospy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>message_runtime</exec_depend>
7.编译及运行
$ cd ~/catkin_ws
$ catkin_make
编译后生成的gps.h头文件放在
~/catkin_ws/devel/include/topic_demo/gps.h
自定义消息使用方法:
#include <topic_demo/gps.h>
topic_demo::gps msg;
运行发布者和订阅者:
$ cd ~/catkin_ws
$ catkin_make
$ source devel/setup.bash
$ roscore
$ rosrun topic_demo listener
$ rosrun topic_demo talker