前言
本系列博客参照《ROS机器人编程原理与应用》一书,搭载平台为冰达ROS小车,注释详细,持续更新,点个关注吧!
1.编写发布节点
在工作空间下(bingda_practices)新建一个src文件夹,用来保存文件的代码,然后新建talker.cpp的c++文件。
代码注释如下:
#include "ros/ros.h" //第一个头文件是ros下的核心,ros.h
#include "std_msgs/String.h"//第二个头文件,计划发布的是string类型
int main(int argc,char **argv)//编写主函数,标准的主函数写法
{
ros::init(argc,argv,"talker");//先初始化一个ros,第三个参数为节点名称(talker),节点名称在ros网络中是唯一的,不能有俩一样的
ros::NodeHandle n;//Nodehandle建立节点间的网络通讯, 名字n不重要很少用到
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000);
/*
类ROS::Publisher实例化名为chatter_pub的对象,在此对象中,ROS系统接到通知:
创建一个发布器,发布的类型是sting,当前节点(talker)在名为chatter的话题上发布std_msgs::String数据类型的消息。
当消息来不及发送时会先放到缓存区,超过1000个就会覆盖。
*/
ros::Rate loop_rate(10);//用10HZ的频率来循环
int count = 0;//count用来测量发送了多少个话题
while (ros::ok())//创建一个死循环,一直循环无法跳出,直到停止ROS系统(关闭roscore)才关闭。
{
std_msgs::String msg;
/*
程序创建了一个std_msgs::String类型对象,并命名为msg。
需要在 std_msgs中查阅消息定义才能了解如何使用这个对象,他包含一个名为data的成员。
下面代码是让data里存放一个格式:hellow world 计数
*/
std::stringstream ss;//建立一个string类型的变量
ss<< "hello world"<<count;
/*利用c++自带的头文件sstream,来实现利用输入输出流的方式往string里写东西,并且还可以拼接string和其他类型的变量。
该语句实现了string型的数据 hello world和int型变量 count的拼接,形成一个新的string。即如果count是1,那么hello world1会作为string被存放在ss当中!
怎么调用这个string呢? ss.str()就可以了。最后可以用ROS_INFO输出。
*/
msg.data = ss.str();
ROS_INFO("%S",msg.data.c_str());
/*
输出一个字符串变量.ROS_INFO和类似的函数用来替代printf/cout.
其中 ROS_INFO(“INFO message %d”,k),相当于c中的printf;
ROS_INFO_STREAM ( "INFO message." <<k);相当于c++中的cout;
*/
chatter_pub.publish(msg);
/*
调用发布器,chatter_pub会把std_msgs::Stringstream类型的消息发布到名为chatter的话题上。
发布器对象chatter_pub有成员函数publish来调用发布。
*/
ros::spinOnce();//回调函数,处理回调函数队列中的回调函数,此处无用
loop_rate.sleep();//保证用10HZ的频率来执行
count++;
/*code*/
}
}
2.修改CmakeLists
打开CmakeLists.txt
将
find_package(catkin REQUIRED)
改为
find_package(catkin REQUIRED
roscpp
std_msgs
)
roscpp和std_msgs都为明确的依赖项。
roscpp:使用c++编译器来创建ROS代码,我们还需要兼容c++接口(比如ros::Publisher类和ros::subscriber类)。
std_msgs:依赖项表面我们将需要依赖一些已经在ros中预定义的数据类型格式,例如std_msgs::Float64
将
include_directories(
# include
# ${catkin_INCLUDE_DIRS}
)
改为
include_directories(
include
${catkin_INCLUDE_DIRS}
)
在最后加上
add_executable(talker src/talker.cpp)
/*
add_executable(hello_vscode_c src/hello_vscode_c.cpp):
这个函数目的是生成可执行文件。用src文件夹下的hello_vscode_c.cpp文件生成hello_vscode_c可执行文件。
hello_vscode_node.cpp文件是你写的c++源文件,文件名是你创建c++文件时命名的,最终这个文件映射的文件名称为hello_vscode_c,这个名称可以修改,一般源文件和生成的可执行文件的名称设置为一样的,但这两者需要满足计算机语言的命名要求。
可以传入多个源文件,如add_executable(my_node src0.cpp src1.cpp src2.cpp )。函数后面的src0.cpp src1.cpp src2.cpp三个源文件,生成可执行文件my_code这一个可执行文件。
*/
target_link_libraries(talker ${catkin_LIBRARIES})
/*
target_link_libraries(hello_vscode_c ${catkin_LIBRARIES} ):
这个函数为可执行文件或库添加链接库。函数中 hello_vscode_c是上一个函数语句生成的可执行文件,${catkin_LIBRARIES} 是ROS的基本库:catkin库。
把生成的可执行文件和catkin库链接到一起。
同时,在CMakeListd文件里面,这篇文章中所提到的第二个函数要放在第一个函数后面,因为target_link_libraries函数需要用到add_executable生成的可执行文件。
*/
最后在catkin_make 下编译catkin_make
3.运行程序
打开终端运行roscore
再打开一个终端,运行 rosrun bingda_parctices talker
4.编写订阅节点
如同listener.cpp,新建一个listener.cpp
代码注释如下:
#include "ros/ros.h" //第一个头文件是ros下的核心,ros.h
#include "std_msgs/String.h"//第二个头文件,计划发布的是string类型
void chatterCallback(const std_msgs::String::ConstPtr& msg)//回调函数作用是打印出来话题
/*
这个函数(callback)有一个指向std_msgs::String类型对象的引用指针参数(由&符号标识)。这是与chatter关联的消息类型,由最小发布器发布。
回调(callback)函数的重要性在于,当在它的关联话题(在本例中设定为topicl)上有新数据可用时,它会被唤醒。当新数据发布到关联话题时,回调函数开始运行,同时已发布的数据会出现在参message_holder中。
(该信息持有者必须是与发布在兴趣话题上的消息类型相兼容的类型,例如:std_msgs::String)
回调函数做的唯-的事情 就是显示接收到的数据
*/
{
ROS_INFO("I heared:[%s]",msg->data.c_str());
}
int main(int argc,char **argv)//编写主函数,标准的主函数写法
{
ros::init(argc,argv,"talker");//先初始化一个ros,第三个参数为节点名称(talker),节点名称在ros网络中是唯一的,不能有俩一样的
ros::NodeHandle n;//Nodehandle建立节点间的网络通讯, 名字n不重要很少用到
ros::Subscriber sub = n.subscribe("chatter",1000,chatterCallback);
/*
类ROS::Subscriber实例化名为sub的对象,Subscriber是存在于ROS版本的一个类,
他包括三个参数用于实例化订阅器对象:参数名、队列大小、回调函数名。
*/
ros::spin();
/*
每当chatter有新消息时,回调函数会被唤醒,然而,主程序必须为回调函数的响应提供一些时 间。
这可以通过spin()命令完成。虽然spin可以让主程序挂起,但回调函数依旧可以工作。
如果主函数运行结束,回调函数就不再对新消息做出反应。
spin()命令不需要消耗大量Cpu的时间就可以保持main()的运行,非常高校。
*/
return 0;
}
5.修改CmakeLists
在最后加上
add_executable(talker src/talker.cpp)
target_link_libraries(hello_vscode_c ${catkin_LIBRARIES} )
add_executable(listener src/listener.cpp)
target_link_libraries(listenner ${catkin_LIBRARIES} )
最后在catkin_make 下编译catkin_make
先运行发布者,再运行接听者。