topic_demo是ROS 工程中的一个package,即程序写作的最基本单元,ROS文件架构可参照文章:https://blog.csdn.net/DXMARK/article/details/89019477。
此篇文章主要对publisher进行了代码实现,剩下内容(subscribe程序的编写以及cmakelist和package.xml的编写)请参见专栏下一篇文章。
本次学习的前置知识有ROS package文件结构、c++基础、cmake基础、linux基础、ROS基本命令及基本概念( 百度 or Google)
PS:基于中科院项目教学代码进行学习,跟着项目走是最好的学习方法,代码网址:https://github.com/DroidAITech/ROS-Academy-for-Beginners,
另:知乎专栏:《致敬图灵(AI与机器人专栏)》
https://www.zhihu.com/people/li-zhi-hao-32-6/columns
微信公众号:< 致敬图灵 >。
首先要建立topic_demo这个package才能进行后面的代码编写,建立topic分为两步:1.进入src目录2.建立package文件区
cd ~/catkin_ws/src #catkin_ws是工作区域,src是代码写作区域
catkin_create_pkg topic_demo roscpp ros_py msg_std #建立topic_demo package,并依赖于roscpp ros_py msg_std 语法:catkin_create_pkg [depend1] [depend2] [depend3]
topic通讯方式中,msg文件定义了通讯信息的数据格式,因此首先要进行msg文件的编写,并进行编译,编译后会生成一个头文件,这个头文件中含有通讯中所需要用到的命名空间一个刚才定义的数据格式。
cd topic_demo/
mkdir msg#创建msg文件夹
cd msg #进入文件夹
vi gps.msg
#vi gps.msg进入编辑模式, gps.msg文件用于定义数据格式
float32 x
float32 y
string state
catkin_make #写完后一定要编译,才能生成头文件,编译完后,会产生一个以package名称为名的命名空间,gps.msg会成为其中的一个结构体。
talker.cpp中编写了一个node节点,而这个node节点是topic通讯中的消息发送者。
接下来逐段分析代码:
//代码段1 主要讲解ros::init
#include //所有的ROS c++程序都要使用这个头文件
#include //gps.h使gps.msg文件编译生成的.h文件
int main(int argc,char** argv){ //char** p相当于char* p[],即字符数组的指针,或者说是字符串的指针
ros::init(argc,argv,"talker");//初始化 ROS 。argc 和 argv ROS使用这两个参数来解析命令行的重映射参数,ROS也会修改他们,使得他们不再包含任何重映射参数,所以如果在处理命令行之前调用ros::init(),你就不用自己处理这些参数了。
//node_name 在被重映射参数覆盖之前,这个名字会用来命名节点名,即如果在运行时,加上了talker:newname,那么node的名字会被改变,节点名字在ROS系统中必须唯一。如果有第二个同名的节点启动,则第一个会自动被关闭。有时你需要运行多个同样的节点,则可以通过下面描述的init_options::AnonymousName来解决。
//options 这个可选的参数可以使你指定一些操作来改变roscpp的行为。这里的名称必须是一个 base name ,也就是说,名称内不能包含 / 等符号。
上面对参数进行了基础的解释,也是ROS给的官方解释,但目前,可能大家还是对里面的参数有些疑惑,解决问题的第一步是分解问题,上面的看不懂的句子可以分解为以下几个问题:1.argc 和 argv到底是什么?2.解析命令行重映射参数又是什么?
1.argc 和 argv到底是什么?(http://www.runoob.com/cprogramming/c-command-line-arguments.html)(https://blog.csdn.net/wu_lai_314/article/details/8454394)
argc是整数类型,表示传给main函数的命令行参数的数量。
命令行参数是什么呢?执行程序时,可以从命令行传值给 c/c++ 程序。这些值被称为命令行参数,它们对程序很重要,特别是当您想从外部控制程序。
argv[ ] 是一个指针数组,指向传递给程序的每个参数。argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针,*argv[n] 是最后一个参数。如果没有提供任何参数,argc 将为 1,否则,如果传递了一个参数,argc 将被设置为 2。
那么在ROS工程中,命令行的参数是如何传递到main函数中的呢?(https://mirsking.com/2015/01/26/ros_init/)
官方说argc, argv是用来解析来自命令行remapping arguments, 而所谓remapping arguments就是在调用rosrun package_name node_name param_name:=param_value
中的param:=param_value
部分. 这些参数是从命令行的remapping arguments中传入的.
2.解析命令行重映射参数是什么?
这个实例中,重映射参数就是argv中存放的参数(用面向对象的思维方式思考,argv中存放的参数是类的对象,而重映射参数是一个类,不要混淆),它到底是什么含义(也就是面向对象中的类)呢?参考http://wiki.ros.org/Remapping Arguments,简单来说,就是在ROS node启动时,可以通过remapping来对节点的参数进行重新设置(name:=new_name语法进行参数的重新设置)。我们将重映射暂且理解为以不同的参数运行同一节点,而在此例中,传递的参数是节点的名称。
#重映射参数的例子
rosrun rospy_tutorials talker chatter:=/wg/chatter#重映射通过name:=new_name语法进行参数传递,即将chatter这个参数设置为/wg/chatter
下面一段代码的主要的c++知识点为模板函数,ROS知识点为topic通讯中定义消息发送者的代码流程:
//代码段2
ros::NodeHandle nh; //定义节点的句柄,实例化节点。
topic_demo::gps msg ;//定义msg变量,类型为topic_demo命名空间下的gps
msg.x=1.0;
msg.y=1.0;
msg.state="working";//定义变量的值
//创建publisher
ros::Publisher pub = nh.advertise("gps_info", 1); //定义ros::Publisher类型的变量 pub为nh.advertise("gps_info", 1),nh.advertise("gps_info", 1);为模板函数,指的是在函数进行定义时,不指定其应该传入的具体参数类型,而是以一个模板传入,使用此函数的时候,再定义函数的参数类型。代码下面有详细讲解
//定义发布的频率
ros::Rate loop_rate(1.0); //循环发送的频率为1,即一秒钟1次
while (ros::ok())
{
//以指数增长,每隔1秒更新一次
msg.x = 1.03 * msg.x ;
msg.y = 1.01 * msg.y;
ROS_INFO("Talker: GPS: x = %f, y = %f ", msg.x ,msg.y);//将当前的信息打印处理,类似于printf。
//以1Hz的频率发布msg
pub.publish(msg);
//根据前面定义的频率, sleep 1s
loop_rate.sleep();//根据前面的定义的loop_rate,设置1s的暂停
}
return 0;
}
模板函数的具体解析可以在https://blog.csdn.net/lezardfu/article/details/56852043中学习。
//ROS c++中, advertise函数的定义,它可以返回一个ros publisher(发送者)的实例。
template
ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);
M [required]
This is a template argument specifying the message type to be published on the topic.此参数定义了topic中传递的message的具体类型
topic [required]
This is the topic to publish on.此参数定义了PUBLISH的TOPIC的名称
queue_size [required]
This is the size of the outgoing message queue. If you are publishing faster than roscpp can send the messages over the wire, roscpp will start dropping old messages. A value of 0 here means an infinite queue, which can be dangerous. See the rospy documentation on choosing a good queue_size for more information.此参数定义了publish是,在缓冲buffer中的msg的个数。若随时发送随时接收,则可以设置为1.
latch [optional]
Enables "latching" on a connection. When a connection is latched, the last message published is saved and automatically sent to any future subscribers that connect. This is useful for slow-changing to static data like a map. Note that if there are multiple publishers on the same topic, instantiated in the same node, then only the last published message from that node will be sent, as opposed to the last published message from each publisher on that single topic.