既对ros tutorial 上的例子有了一定的了解之后,今天对发布器和订阅器代码(http://wiki.ros.org/cn/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29)进行了研究,同时稍作改动进行了验证
发布器-------------------------------------------------------------------------------------------------------------
<pre name="code" class="cpp">#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Int16.h"//因为后面我要用到整形的msg,此处要加上Int的头文件,不加后面会报错
#include <sstream>
int main (int argc,char **argv)
{
ros::init(argc,argv,"input");//初始化ROS, 是确切说明节点名字的地方, 节点的名字必须唯一,
//它允许ROS通过命令行重新命名
ros::NodeHandlen;//创建了该节点的一个句柄
ros::Publisherinput_pub = n.advertise<std_msgs::String>("chatter1",1000);
ros::Publisherinput_pub = n.advertise<std_msgs::String>("chatter2",1000);
/*尖括号里为发布的数据类型(调试的时候第一个就忘了改), 即消息Message ,我再这里想要在一个节点(node)上发布两个话题(topic)
圆括号中”chatter1”&”chatter2”为发布的话题的名称, 即Topic也可以写成ros::Publisher chatter_pub; chatter_pub= n.advertise<...>... (引自ROS 代码解析)*/
ros::Rate loop_rate(10);
/*一个ros::Rate对象允许user制定循环的频率它将会记录从上次调用Rate::sleep()到现在为止的时间, 并且休眠正确的时
intcount = 0; 我的代码里没有用到计数,值得注意的是源代码的count 变量一定程度上反应了代码的执行过程(通过观察输出结果可以明白)*/
int a b,c;
while (ros::ok())//默认情况下,roscpp将会安装一个SIGINT监听,
//它使当Ctrl-C按下时,ros::ok()将会返回 0
{
std_msgs::Int16msg1;
std_msgs::Stringmsg2; //此处定义msg1 和msg2,注意类型不一致
msg2.data="hello!I am Baymax!";
std::cin>>a>>b; //获取键盘的输入值,经过我的验证,如果不输入数据,程序会在这里等待
c=a+b;
msg1.data=c;
// ROS_INFO("%s", msg.data.c_str()); 此处可以百度c_str的含义做更深入了解
ROS_INFO("%s", msg2.data.c_str()); //输出 msg2的信息
input_pub.publish(msg1);
input_pub.publish(msg2); //发布信息
ros::spinOnce(); // 可以查询spinOnce做进一步了解,此处为调用一次回调函数,关于回调函数可以从订阅节点处做直观了解
loop_rate.sleep(); //休眠一下, 使程序满足前面所设置的 10Hz的要求
}
return 0;
}
订阅器--------------------------------------------------------------------------
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Int16.h"//因为后面我要用到整形的msg,此处要加上Int的头文件,不加后面会报错
void chatterCb1(const std_msgs::Int16::ConstPtr& msg1) // 此处我设定了两个回调函数
{ ROS_INFO("I heard you said answer is : [%s]", msg1->data); }
void chatterCb2(const std_msgs::String::ConstPtr& msg2)
{ ROS_INFO("I heard: [%s]", msg->data.c_str()); }
int main(int argc, char **argv)
{
ros::init(argc, argv, "output");// 初始化节点(node)
ros::NodeHandle n;
ros::Subscriber sub1 = n.subscribe("chatter1", 1000, chatterCb1);
ros::Subscriber sub2 = n.subscribe("chatter2", 1000, chatterCb2); //订阅节点,触发回调
ros::spin();
return 0;
}
1.ros::spinOnce() 和ros::spin()的区别
ROS 监视器和你订阅的topic链接,消息到达时,他会把消息加入到队列中,但不会立即处理消息。在读到ros::spin()之后ros才开始执行回调函数。而spinOnce 是指调用一次回调函数,spin则不停的调用回调函数,直到node被关闭的时候才会停止。
2.定义特定的时间间隔来处理回调函数
问题提出:如果你的信息的到达速度是100HZ,你每5HZ调用一次spinonce()函数,如果你的订阅器缓冲区大小为1,那么你丢掉了95/100的信息。因此协调好订阅发布的频率是非常重要的。
使用定时器是一种很好的办法,在这里介绍一种经常使用的Ros::rate工具。
ros::Rate r(100);
while (ros::ok())
{
libusb_handle_events_timeout(...); // Handle USB events
ros::spinOnce(); // Handle ROS events
r.sleep();
}
这里保证了while()大循环的频率是100HZ,这是通过ros::Rate 和 r.sleep函数配对实现的
在其他的工程中,可能main函数被包括在了其他GUI界面的程序里面,如果仍然希望做到上面的设定频率,只需要按照他的格式,找到一个地方写ros::spinOnce()函数就可以了。
void timerCb(int value) { ros::spinOnce(); }
glutTimerFunc(10, timerCb, 0);
glutMainLoop(); // Never returns
(出处:http://answers.ros.org/question/11887/significance-of-rosspinonce/)
-------------------------------------------------------------------------------------------
(如有不妥之处,恳请批评指正)