ROS机器人004-ROS通信编程(话题编程,自定义话题消息)
一、ROS通讯编程类型
分为以下三种
1.话题编程
2.服务编程
3.动作编程
二、话题编程
三、创建发布者
在功能包learn_communication/src/目录下添加talker.cpp文件:
1.添加都文件
//添加ROS包
#include "ros/ros.h"
//添加ROS string变量
#include "std_msgs/String.h"
2.Ros结点初始化,定义结点名称
ros::init(argc,argv,"talker");
3.创建结点句柄
通过创建结点句柄,方便我们对结点进行管理(包括发布者,订阅者等等的管理)
//创建结点句柄
ros::NodeHandle n;
4.创建发布者
创建一个Publisher的对象,发布名为chatter的话题,消息类型为std_msg::String,发布者队列长度为1000(将数据放入队列,避免发布过快,接受者收不到)
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000);
5.发布消息
//发布消息
chatter_pub.publish(msg);
6.完整代码
//添加ROS包
#include "ros/ros.h"
//添加ROS string变量
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
//ROS结点初始化
//创建talker结点
ros::init(argc,argv,"talker");
//创建结点句柄
ros::NodeHandle n;
//创建一个Publisher,发布名为chatter的话题,消息类型为std_msgs::String
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000);
//设置循环的频率 10HZ
ros::Rate loop_rate(10);
int count = 0;
while(ros::ok())
{
//初始化std_msgs::String 类型的消息
std_msgs::String msg;
std::stringstream ss;
ss<<"hello world"<<count;
msg.data = ss.str();
//发布消息
ROS_INFO("%s",msg.data.c_str());
chatter_pub.publish(msg);
//循环等待回调函数
ros::spinOnce();
//注意在循环中一定要有延时函数
//按照循环频率延时
loop_rate.sleep();
++count;
}
return 0;
}
四、创建订阅者
1.初始化ROS结点
//初始化ROS结点
ros::init(argc,char **argv,"listrner");
2.创建结点句柄
//创建结点句柄
ros::NodeHandle n;
3.创建一个订阅者
并且绑定订阅的话题,收到消息的回调函数
//创建一个接受者,订阅名为chatter的话题,注册回调函数chatterCallback
ros::Subscriber sub = n.subscribe("chatter",1000,chatterCallback);
4.创建回调函数
//接收消息的回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard :{%s}",msg->data.c_str());
}
5.循环等待回调函数
//循环等待回调函数
ros::spin();
6.完整代码
#include "ros/ros.h"
#include "std_msgs/String.h"
//接收消息的回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard :{%s}",msg->data.c_str());
}
int main(int argc,char **argv)
{
//初始化ROS结点
ros::init(argc,argv,"listrner");
//创建结点句柄
ros::NodeHandle n;
//创建一个接受者,订阅名为chatter的话题,注册回调函数chatterCallback 1000代表消息队列的大小
ros::Subscriber sub = n.subscribe("chatter",1000,chatterCallback);
//循环等待回调函数
ros::spin();
return 0;
}
五.编译代码
当我们用c++写完这两个结点功能后,需要将代码编译为可执行文件(用python写就不用编译,Python本身就是可执行文件)
1.更改编译配置文件
更改功能包目录下的CMakeLists.txt文件:
将add_executable(${PROJECT_NAME}_node src/learing_communication_node.cpp)取消注释,并更改内容
更改为:
意思是将src目录下的talker.cpp编译为talker可执行文件
add_executable(talker src/talker.cpp)
链接第三方库:
因为这里没用到其他库,只有ROS默认的库,所以我们链接catkin_LIBRARIES
target_link_libraries(talker ${catkin_LIBRARIES})
更改后的完整内容为:
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
2.编译
在工作空间目录 catkin_ws下打开终端:
命令:
catkin_make
编译完成:
可以在devl目录下看见我们编译完成的可执行程序:
devel/lib/learing_communication/
六,运行可执行文件
1.启动rosMaster
新建终端
roscore
2.启动listener
用rosrun命令
rosrun功能包名 结点名
rosrun learing_communication talker
3.启动listener
rosrun learing_communication listener
我们可以发现发送和接收保持同步
七、自定义话题
前面的话题我们都是发送一个string类型的数据,但是我们想要发送一组有类型的数据怎么办呢?德国自定义话题来解决
1.定义msg文件
在功能包目录下创建msg文件夹(存放msg文件)
在文件夹下创建person.msg文件
定义person相关信息,并且可以定义常量(unknown,male,female):
string name
uint8 sex
uint8 age
uint8 unknown = 0
uint8 male = 1
uint8 female = 2
2.在package.xml中添加功能包依赖
打开功能包目录下的package.xml添加以下:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
注意:
3.在Cmakelist.txt添加编译选项
打开功能包目录下的cmakelist.txt做以下更改
• find_package( …… message_generation)
• catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime)
• add_message_files(FILES Person.msg) generate_messages(DEPENDENCIES std_msgs)
添加功能包:
find_package( …… message_generation)
添加编译时的依赖:
catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime)
指定消息文件的目录:
add_message_files(FILES Person.msg) generate_messages(DEPENDENCIES std_msgs)
4.编译
将msg文件编译成对应语言的头文件:
在工作空间根目录进行编译
catkin_make
可以看见他编译成不同语言接口供我们使用
5.查看自定义消息
rosmsg show person
证明该消息编译没问题,可以使用头文件包含进行使用了