发布接收int类型消息
第一篇文章我们发布接收了string类型的消息.我们提到在ROS里发布的消息必须是在ROS中定义了的.就是如果你要发布一个string类型的消息,你不能直接发布一个std::string,你得发布一个std_msgs::String类型的消息.后者在ROS中才有定义.那么发布其他类型的消息我该怎么办呢?比如现在如果我想发布一个int8类型的消息,Int8是8位整型的消息,范围从-128到127.可以想象,代码在很大程度上应该和发布string类型的代码相似.咱们先直接贴上代码,然后来找不同.首先打开一个terminal.输入下面内容
cd ~/catkin_ws/src/pub_sub_test/src
touch pub_int8.cpp
touch sub_int8.cpp
咱们创建了用来发布8位整型message的发布程序和接收程序.接着,打开pub_int8.cpp,把下面的代码粘贴进去,保存再退出.
#include "ros/ros.h"
#include "std_msgs/Int8.h" //#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::Int8>("chatter", 1000); //ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
std_msgs::Int8 msg; //std_msgs::String msg;
// std::stringstream ss;
// ss << "hello world " << count;
msg.data = count;// msg.data = ss.str();
ROS_INFO("%d", msg.data); //ROS_INFO("%f", msg.data.c_str())
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
接着,把下面的接收程序粘贴到sub_int8.cpp里,保存并退出
#include "ros/ros.h"
#include "std_msgs/Int8.h" //#include "std_msgs/String.h"
void chatterCallback(const std_msgs::Int8::ConstPtr& msg) //void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%d]", msg->data); //ROS_INFO("I heard: [%f]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
ros::spin();
return 0;
}
程序写好后,和上一章一样,你需要添加到CMakeLists里编译,在terminal中输入下面的内容
cd ~/catkin_ws/src/pub_sub_test
gedit CMakeLists.txt
pub_sub_test
这个文件夹里的CMakeLists.txt文件就行了. 在上一章里为了编译发布和接收string消息的程序你在CMakeLists.txt里添加了下面的内容
add_executable(pub_string src/pub_string.cpp)
target_link_libraries(pub_string ${catkin_LIBRARIES})
add_executable(sub_string src/sub_string.cpp)
target_link_libraries(sub_string ${catkin_LIBRARIES})
现在,为了编译发布和接收int8的消息,根据你写好的程序,你在他们下面添加(添加哈,不是替换,不然就不能编译之前的pub和sub string的程序了)下面的内容
add_executable(pub_int8 src/pub_int8.cpp)
target_link_libraries(pub_int8 ${catkin_LIBRARIES})
add_executable(sub_int8 src/sub_int8.cpp)
target_link_libraries(sub_int8 ${catkin_LIBRARIES})
这几行找不同的任务就由你自己来完成了哈.写完之后保存并退出.接着在terminal中输入下面的内容
cd ~/catkin_ws
catkin_make
你就编译完成啦.完美.接下来咱们跑一下程序看看有没有问题. 打开三个terminal. 第一个terminal中输入
roscore
在第二个terminal中输入下面内容
cd ~/catkin_ws/
source devel/setup.bash
rosrun pub_sub_test sub_int8
在第三个terminal中输入下面内容
cd ~/catkin_ws/
source devel/setup.bash
rosrun pub_sub_test pub_int8
代码对比
在上面的代码中,我把所有不同的地方都注释了,注释符号//
后面的内容是原发布String
类型的变量的代码.简单来说,就是把涉及到消息类型的地方全部从String
改到Int8
了.下面我们一行行看不同的地方. 首先是发布器程序 1: #include "std_msgs/Int8.h"
代替了#include "std_msgs/String.h"
.这表明了,每一种不同的消息都有自己的头文件,如果我们要使用不同的消息,就首先要包含它所在的头文件.像Int8, String
这类都属于C++的标准数据类型,所以这些消息在ROS中也被划分到了std_msgs
这个名字下.消息当然还有其他大类,比如我们以后要使用类似于pose的消息,它包含在geometry_msgs这个大类里.需要包含的头文件是 #include "geometry_msgs/Pose.h"
. 2:ros::Publisher chatter_pub = n.advertise<std_msgs::Int8>("chatter", 1000);
中,std_msgs::Int8
代替了std_msgs::String
,定义publisher的时候,它需要发布什么消息是需要指定明确的,之前是ros中的string那么现在就自然换成ros中的int8了 3:std_msgs::Int8 msg
替换了 std_msgs::String msg
. 同样你要发布的消息的类型替换掉. 4: 发布string类型的消息的代码中连续的三行
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
msg.data = count
.我们知道原来的三行中,被注释的前两行是用来形成一个字符串的.原来的msg是std_msgs::String, msg.data就是string类型,现在msg是std::msgs_Int8,那么大概可以猜到msg.data如今就是int8类型了,考虑到我们的count本来就是一个int型变量,所以这儿直接把count赋值给msg.data了.注意这儿的类型变换是int到int8,问题不大,只是整型的范围缩小到-128到127了而已. 5: ROS_INFO
中msg.data本来就是int8型的变量,可以直接print出来,对应需要在ROSINFO中表明数据类型是%d
(字符串类型是%f
). 其他的都一样了.然后是接收程序.
#include "std_msgs/Int8.h"
代替了#include "std_msgs/String.h"
不在赘述.chatterCallback
函数的参数由std_msgs::String::ConstPtr& msg
变成了std_msgs::Int8::ConstPtr& msg
ROS_INFO
中,之前msg->data是stiring类型现在是int8类型,可以直接print出来. 主函数中没有设计到变量定义的地方,所以不需要更改. 总结一下就是把涉及到表明数据类型的地方全部换成std_msgs::Int8就可(注意大小写).
使用ROS wiki了解你需要的数据类型怎么使用
可能经过前面两个例子你会想,我知道了两种类型变量怎么发布,可以以后会遇到N多不同的类型,我该在哪儿找我想要发布的数据类型,包含什么成员之类的呢?这就需要ROS wiki的帮助了. 如果现在我问你,我想发布一个double类型的消息,你该怎么做?可能有了前两个例子,你会考虑比如说把#include “std_msgs/String.h”换成#include “std_mags/Double.h”.你可以把之前任一pub…cpp文件中添加#include “std_mags/Double.h”的程序,然后编译,系统会告诉你找不到这个东西.那该咋办?怎么发布?首先想到百度一下(当然能翻墙最好是google一下).首先你得想到我们之前说了,c++自身包含的一些标准数据类型比如vector, double, int等,在ros中都被存在std_msgs中,所以你直接搜索`ROS std_msgs`之类的就可以了.之后你会看到第一个出现的网站应该是下面这个. http://wiki.ros.org/std_msgs/ 点进去你会发现里面有std_msgs所包含的所有信息种类.在ROS Message Types
下面.然后翻来覆去找一找有没有带有double字样的,发现没有hhhhhhh.怎么会没有呢.其实double也是浮点类型的数据只是精度更高,所以你看精度最高的Float64就是啦.(我怎么知道??至少你会从Float相关的下手吧…).话又说回来,为什么ROS不直接取名叫Double呢?要知道ROS也有很多人用python写的,python里可没有double这种东西,其实你进入ros wiki下面的这个网站 http://wiki.ros.org/msg 找到build_in types
你会发现ros中的数据类型在c++和pyton中分别对应什么类型.float64那儿就写了对应C++的double类型变量. 好了回到网页http://wiki.ros.org/std_msgs/ ,你找到了所需要的数据类型是Float64
怎么使用呢?聪明的同学有这个消息其实就够了.你会猜到把第一个程序中的如#include "std_msgs/String.h"
换成#include "std_msgs/Float64.h
,其他的也像我们发布Int8类型的消息那样照着换就可以了.那么恭喜你答对了,就是这样.那么我不够聪明呢???博主就属于这类人hhhhh,这时候我们发现网页中Float64是可以点进去的诶,点进去看看.进入网页http://docs.ros.org/api/std_msgs/html/msg/Float64.html 发现下图中的内容.
File
这一行就表示出你如果要使用该信息时所需要包含的头文件了,把msg换成h就可以了(python 用户根据的使用方式就是 from std_msgs.msg import Float64
这个后面再说).然后下面的Raw Message Definition和Compact message Definition.两者功能其实差不多,前者有时候会对该类型的消息所包含的内容加以解释(这儿没有= =),后者会显示你的数据的具体类型.这儿都是一样的,也没什么解释,因为ROS里这是最简单的数据类型的,它觉得不需要注释= = . float64
就是表示这个message的数据类型了.data
表示成员变量.之前的代码我们怎么使用std_msgs::Int8 msg
的?我们在程序中这么写的msg.data = count
.一切都是根据这个页面来的.我们通过std_msgs::Float64 msg
定义了类Float64的对象msg,通过查看这个页面得知类中包含数据成员data,数据类型是float64(即c++中的double),所以我们可以给msg.data赋值double类型的变量.这么一看好简单,我不看这个页面根据前面的代码也能猜到嘛.那么现在我问你,如果我想发布的数据类型为一数组(array)呢? 很明显数组也是属于std_msgs所以我们又回到关于std_msgs信息的ROS wiki http://wiki.ros.org/std_msgs 上来. 发现有很多涉及到Arrary的消息类型.比如Float64MultiArray
,一维数组是多维数组的特殊形式,所以既然没有类似Float64Array
这个选项,那么Float64MultiArray
就应该可以用来发布double类型的一维到多维的数组了.怎么使用呢?同样根据之前的经验,先做个大胆猜测,我首先需要#include "std_msgs/Float64MultiArrary.h"
,然后如果我用std_msgs::Float64MultiArray msg
定义了一个double类型的多维数组的对象,我再有一个double类型的数组arrayTest
,应该可以通过msg.data = arraryTest
把C++的数组赋值给ROS的数组.代码类似于下面这样
#include "ros/ros.h"
#include "std_msgs/Float64MultiArray.h" //#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::Float64MultiArray>("chatter", 1000); //ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
//int count = 0;
double testArray[5] = {1,2,3,4,5}; //create an array to publish
while (ros::ok())
{
std_msgs::Float64MultiArray msg; //std_msgs::String msg;
// std::stringstream ss;
// ss << "hello world " << count;
msg.data = testArray;// msg.data = ss.str();
ROS_INFO("I have published array data"); //ROS_INFO("%f", msg.data.c_str())
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
// ++count;
}
这个代码呢,看起来就是一直发布一个内容相同{1,2,3,4,5}的数组.打开一个terminal,输入下面内容
cd ~/catkin_ws/src/pub_sub_test/src
touch pub_array_test.cpp
cd ~/catkin_ws/src/pub_sub_test
gedit CMakeLists.txt
在CMakeLists.txt中你之前添加编译文件的下方添加
add_executable(pub_array_test src/pub_array_test.cpp)
target_link_libraries(pub_array_test ${catkin_LIBRARIES})
保存并退出,在terminal中输入
cd ~/catkin_ws
catkin_make
进行编译.这已经是第三次写从写文件到改CMakeLists到编译的步骤了哦,希望大家牢记于心.虽然用多了也就记住了.编译结果呢??出错了!!! error大概是下面的样子.
msg.data
,也就是说msg.data
并不是一个double类型的数组,那么它skr啥?其实上面error的内容已经指出来了,aka std::vector<double>
,这是一个double的vector!! 虽然C++里vector就是用来操作数组的,但是你这个消息的名字直接叫...Array
,鬼会想到直接用vector啊.好吧好吧,可能因为python里没有vector而是tuple, list什么的.但这样真的让人困惑.那我们去看看ROS wiki里有没有指出这是个vector呢?回到网站 http://wiki.ros.org/std_msgs
点击 Float64MultiArray链接,发现下图内容.
float64[]
,这他喵的看起来不就像数组吗? 说实话,我之前打开ROS wiki查看数据类型时,是崩溃的.我既不知道如float64[]
是什么数据类型,也不知道右边是data
是成员函数,那个什么MultiArrayLayout
是什么鬼我就更不知道了.要怎么用就更更是不知道了.这都什么辅助资料啊,当时的表情是这样的
Array handling
部分,我们可以看到左边ROS的arrary type
对应的C++数据类型就是std::vector.暂且先不管MultiArrayLayout
的事儿.我们创建一个新的文件,把上面pub_string_test.cpp的代码复制进去.打开一个terminal
cd ~catkin_ws/src/pub_sub_test/src
touch pub_string.cpp
double testArray[5] = {1,2,3,4,5}
改成std::vector<double> testArray = {1,2,3,4,5}
. 另外既然要使用vector,那么一般要添加其头文件,添加头文件#include <vector>
,但是"std_msgs/Float64MultiArray.h"
,已经包含了<vector>,就不用再重复包含了.打开CMakeLists,删除掉前面添加的编译pub_sub_test.cpp
那两行文字,不然编译肯定要出错啦,添加编译pub_sub.cpp
文件的内容.(这儿不再说怎么做了,咱们之前已经做了三次了,一模一样的).但是这儿要多做一件事,std::vector<double> testArray = {1,2,3,4,5}
这种给向量的赋值方式是C++11之后才有的,所以要添加C++11编译.ROS的CMakeLists.txt的最上面几行有一行是#add_compile_options(-std=c++11)
,把注释符号#去掉就可以了.保存退出. 那么下面是接收器的文件,在同样的位置创建一个叫sub_array.cpp的文件,内容大同小异.
#include "ros/ros.h"
#include "std_msgs/Float64MultiArray.h" //#include "std_msgs/String.h"
void chatterCallback(const std_msgs::Float64MultiArray::ConstPtr& msg) //void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%f], [%f]", msg->data[0], msg->data[1]); //ROS_INFO("I heard: [%f]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
ros::spin();
return 0;
}
MultiArrayLayout
到底是什么? 其实用过vector的同学应该知道,std::vector<double>
就是一个一维的向量,不是二维三维的.二维向量应该是这么定义的std::vector<std::vector<double> >
,既然float64[]的定义就是std::vector<double>,那么我们就不能用它来储存二维向量.那么这个消息的名字想象就挺奇特的,Float64MultiArray
,我本以为可以直接通过msg.data接收高维的向量,结果这是不可能的....MultiArrary
并不能直接作为多维数组的数据,数据只能是一维的.哈哈此处又想用表情包了.你这个MultiArray第一不是理想中的那个array,第二它还不multi不能直接发布多维vector.你如果想通过MultiArray发布多维的数据,你首先得把他们整合到一维的向量中,那么可以想象你需要在MultiArrayLayout中储存你多维数据的结构信息,这样你可以通过layout中的内容,在接收到数据后把数据恢复出来.哈哈是有点鸡肋.不行了,还是得吐槽一下.送MultiArray一个表情包.
总之到目前为止,一些比较常见的信息,string, int/double, array/vector你应该知道该怎么发布接收了.通过ros wiki std_msgs中的内容查看你发布的标准信息包含什么数据成员,数据是什么类型.结合http://wiki.ros.org/msg 中的内容了解他们在C++或者python中代表什么数据类型. http://docs.ros.org/api/std_msgs/html/msg/Float64MultiArray.html Float64MultiArray的定义中std_msgs/MultiArrayLayout 并不是你理想中的整型或者浮点型或者字符串类型的信息,但是那个地下蓝色字体的链接你一直点下去直到没有链接为止,你会发现所有的这些不明所以的消息都是由那些基本消息构成的. 下一讲我们讲怎么发布接收一个机器人的位置,方向.到时候你会对如何使用ROS wiki这个资料有更清晰的认识.