ROS:从入门到放弃(三) 发布接收不同类型消息1

僤鯓⒐⒋嵵緔 提交于 2020-01-21 06:32:45

发布接收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文件夹去打开文件,有的可能喜欢用其他编译器打开文件,这个都不重要了,总之打开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

如果你在第二个和第三个terminal中看到很类似于上个文章跑的程序的结果,就证明程序已经跑成功了.那么下面我们就来看看发布接收int8类型消息的程序和string有哪些不同.

代码对比

在上面的代码中,我把所有不同的地方都注释了,注释符号//后面的内容是原发布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).
其他的都一样了.然后是接收程序.

  1. #include "std_msgs/Int8.h"代替了#include "std_msgs/String.h" 不在赘述.
  2. chatterCallback函数的参数由std_msgs::String::ConstPtr& msg变成了std_msgs::Int8::ConstPtr& msg
  3. 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 发现下图中的内容.
float64Def.png

图片中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

创建了一个pub_arrary_test.cpp并把代码复制进去.保存并退出.同样写完代码后我们需要在CMakeLists.txt里添加编译它的内容.在terminal中输入下面内容打开pub_sub_test package的CMakeLists.txt.

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大概是下面的样子.
error1.png

这个错误表明,我的那个数组,并不能直接赋值给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链接,发现下图内容.
float64MultiDef.png

data旁边写的是float64[],这他喵的看起来不就像数组吗?
说实话,我之前打开ROS wiki查看数据类型时,是崩溃的.我既不知道如float64[]是什么数据类型,也不知道右边是data是成员函数,那个什么MultiArrayLayout是什么鬼我就更不知道了.要怎么用就更更是不知道了.这都什么辅助资料啊,当时的表情是这样的
mengBi.jpg

那么其实float64[] skr啥在另一个网站说了的.
http://wiki.ros.org/msg
也就是我们之前找到float64是什么的网站,找到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 ,但是"std_msgs/Float64MultiArray.h",已经包含了,就不用再重复包含了.打开CMakeLists,删除掉前面添加的编译pub_sub_test.cpp那两行文字,不然编译肯定要出错啦,添加编译pub_sub.cpp文件的内容.(这儿不再说怎么做了,咱们之前已经做了三次了,一模一样的).但是这儿要多做一件事,std::vector 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;
}

添加好内容后保存退出,回到catkin_ws目录下使用catkin_make编译.怎么跑这接收器和发布器和string, in8一样,不再赘述.那么话又说回来,那个MultiArrayLayout到底是什么?其实用过vector的同学应该知道,std::vector就是一个一维的向量,不是二维三维的.二维向量应该是这么定义的std::vector<std::vector >,既然float64[]的定义就是std::vector,那么我们就不能用它来储存二维向量.那么这个消息的名字想象就挺奇特的,Float64MultiArray,我本以为可以直接通过msg.data接收高维的向量,结果这是不可能的.…MultiArrary并不能直接作为多维数组的数据,数据只能是一维的.哈哈此处又想用表情包了.你这个MultiArray第一不是理想中的那个array,第二它还不multi不能直接发布多维vector.你如果想通过MultiArray发布多维的数据,你首先得把他们整合到一维的向量中,那么可以想象你需要在MultiArrayLayout中储存你多维数据的结构信息,这样你可以通过layout中的内容,在接收到数据后把数据恢复出来.哈哈是有点鸡肋.不行了,还是得吐槽一下.送MultiArray一个表情包.
skrWhat.jpeg

毕竟ROS不是专门用来发布数组什么的,这个做地没那么理想可以理解.同志们是不是想放弃ROS了哈哈.不要着急.它的强项message是关于Robotics的比如位置姿态什么的,那些消息还是整合地很好的.怎么通过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这个资料有更清晰的认识.

作者:陈瓜瓜_ARPG
链接:https://www.jianshu.com/p/57a8fa8bd608
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!