ROS 语法入门学习(一)

机器人操作系统ROS:从入门到放弃(一) 发布接收消息 - 简书

一、发布消息

主要是 ROS的publisher和subcribe

ROS 常用的概念

1. message: 消息,机器人需要传感器,传感器采集到的信息,也就是这里所说的message。

2. topic: 话题,假设我们有两个传感器,GPS和温度计.在ROS中我们需要给采集到的消息取个名字用来区分不同的message,这就是topic了。

3. node: 节点,

4. package: 一个ROS package包含了你要完成的一个项目的所有文件.

5. workSpace: Ros 工作空间,用来存放很多不同的package, 完成很多不同的项目。

6. publish, subscribe:  ROS很大的一个作用就是传递message.为什么要传递消息呢? 打个比方你写了一个程序,用来获得GPS的讯息,写了另外一个程序,用来处理GPS的信息.这时你就是需要把采集到的信息传输到处理用的程序中.信息的传输在ROS中称为publish. 信息的接收(貌似中文ros wiki把这个叫做"订阅",我更喜欢翻译成"接收"了)在ROS中称为subscribe。【接收消息subscribe,传输消息 publish】

第一个ROS程序

1. 首先创建一个工作空间 workSpace

参考之前的博客:https://blog.csdn.net/chrishuimin/article/details/122731469

2. 创建一个package

cd ~/catkin_ws/src     //进入workspace的src文件夹,所有package都要在这里面创建. 编译的时候ROS才能找得到.
catkin_create_pkg pub_sub_test std_msgs rospy roscpp   //catkin_create_pkg是ROS自带的命令,表示要创建一个package了. pub_sub_test是这个package的名称,当然你也可以取任何名字. ROS的package命名习惯是不出现大写字母.后面的内容表示你这个ROS package的依赖项.

cd ..  //回退到workspace文件夹
catkin_make   //编译

完成上述命令后进入到catkin_ws/src文件夹中你会发现多了一个pub_sub_test文件夹,这就是你的新package了.

3. 写一个ROS 程序

(1) 写发布消息的程序(发布器)

打开一个terminal键入下面命令

cd ~/catkin_ws/src/pub_sub_test/src
touch pub_string.cpp   //创建一个叫pub_string的c++文件

第一个例子我们还是会采用官网文档的例子: cn/ROS/Tutorials/WritingPublisherSubscriber(c++) - ROS Wiki

#include "ros/ros.h"
#include "std_msgs/String.h"

#include <sstream>

/**
 * This tutorial demonstrates simple sending of messages over the ROS system.
 */
int main(int argc, char **argv)
{
  /**
   * The ros::init() function needs to see argc and argv so that it can perform
   * any ROS arguments and name remapping that were provided at the command line.
   * For programmatic remappings you can use a different version of init() which takes
   * remappings directly, but for most command-line programs, passing argc and argv is
   * the easiest way to do it.  The third argument to init() is the name of the node.
   *
   * You must call one of the versions of ros::init() before using any other
   * part of the ROS system.
   */
  // init() 需要用到argc 和 argv ,第三个参数是 节点的名称
  ros::init(argc, argv, "talker");

  /**
   * NodeHandle is the main access point to communications with the ROS system.
   * The first NodeHandle constructed will fully initialize this node, and the last
   * NodeHandle destructed will close down the node.
   */
  // NodeHandle是与ROS系统通信的主要接入点。构造的第一个NodeHandle将完全初始化该节点,而销毁的最后一个NodeHandle将关闭该节点。
  ros::NodeHandle n;

  /**
   * The advertise() function is how you tell ROS that you want to
   * publish on a given topic name. This invokes a call to the ROS
   * master node, which keeps a registry of who is publishing and who
   * is subscribing. After this advertise() call is made, the master
   * node will notify anyone who is trying to subscribe to this topic name,
   * and they will in turn negotiate a peer-to-peer connection with this
   * node.  advertise() returns a Publisher object which allows you to
   * publish messages on that topic through a call to publish().  Once
   * all copies of the returned Publisher object are destroyed, the topic
   * will be automatically unadvertised.
   *
   * The second parameter to advertise() is the size of the message queue
   * used for publishing messages.  If messages are published more quickly
   * than we can send them, the number here specifies how many messages to
   * buffer up before throwing some away.
   */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

  ros::Rate loop_rate(10);

  /**
   * A count of how many messages we have sent. This is used to create
   * a unique string for each message.
   */
  int count = 0;
  while (ros::ok())
  {
    /**
     * This is a message object. You stuff it with data, and then publish it.
     */
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();

    ROS_INFO("%s", msg.data.c_str());

    /**
     * The publish() function is how you send messages. The parameter
     * is the message object. The type of this object must agree with the type
     * given as a template parameter to the advertise<>() call, as was done
     * in the constructor above.
     */
    chatter_pub.publish(msg);

    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }


  return 0;
}

代码解读:

1: #include "ros/ros.h" ,你的节点(包含main函数的那个程序)如果要使用ROS都得包含这个头文件

2: #include "std_msgs/String.h",我们这个程序是用来发布一个String消息的

3:#include<sstream>,sstream是c++自带的头文件,可以实现利用输入输出流的方式往string里写东西,并且还可以拼接string和其他类型的变量.代码中的

std::stringstream ss;
ss<<"hello world"<<count;

实现了string hello world和int变量 count的拼接,形成一个新的string.即如果count是1,那么helloworld1会作为string被存放在ss当中。

4: ros::init(argc, argv, "talker"),在程序初始化ros,这行代码也是你几乎所有节点都需要的talker就是node(节点)的名字.即你这段程序在ROS当中的名字叫talker

5:ros::NodeHandle n,官方的解释是nodeHandle是和ROS系统通讯的重要工具。NodeHandle的对象名按照习惯一般取为n,nh(推荐)什么的

【4、5是初始化ROS必须的】

6:ros::Publisher 这一行定义你要publish的信息和信息的名字了。

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

(1)n.advertise通过NodeHandle的对象n告诉ROS系统我要创建一个可以发布信息的对象了 。

(2)发布的信息的类型 <std_msgs::String>告诉ROS我要发布的是标准信息中的String类型。

(3)信息的名字:chatter,即之前说的topic,

(4) 数字1000表示 要缓冲的信息数量。

7: ros::Rate loop_rate(10) 表示发布信息的速度为10Hz,用于控制发布的信息的快慢。

这个函数要和loop_rate.sleep()配合使用才能达到控制速度目的。

8: while(ros::ok()) 要是ros不OK,程序就退出了。

9: std_msgs::String msg定义了std_msgs::String的对象msg. 这是我们要发布的信息。

10:msg.data = ss.str()这一行的前面两行我们在3中已经讲了。这一行把ss.str()赋值给msg.datassstringstream的对象,它现在储存了hello world 0这个string类型的变量(假设此时count = 0),提取stringstreamstring的方法就是ss.str()

11: ROS_INFO()这一行就可以理解为ROS里的printf()就可以了。

12: chatter_pub.publish(msg) ,前面我们定义了一个chatter_pub用来发布信息。在我们完成了std::string放到std_msgs::Stringmsg之后,我们就可以发布这个信息了。方法就是这个直观的名字pusblish().

13: ros::spinOnce()这个函数是用于接收器的,必须要有spinOnce或者spin,ROS才会检测是不是接收到信息。

(2) 写接收消息的程序(接收器)

既然发布器写完,我们就要写接收器了。

打开一个terminal,输入下面命令,新建一个c++文件。

cd ~/catkin_ws/src/pub_sub_test/src
touch sub_string.cpp

代码:带注释的参考上面的链接

#include "ros/ros.h"
#include "std_msgs/String.h"

/**
 * This tutorial demonstrates simple receipt of messages over the ROS system.
 */
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", 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;
}

1: 先讲main函数,ros节点的名字为 listener。

ros::init(argc, argv, "listener");

2: ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); 这一行是定义接收器的方法。Publisher使用'n.advertise',这儿使用n.subscriber表示定义接收器, chatter即前面publisher的topic的名字。【注意node的名字得独一无二但是topic的名字得和你想接收的信息的topic一样!

ROS怎么知道你想接收什么信息呢?如果你有两个发布器,都发布std_msgs::String类型的消息,接收器通过找谁的topic和自己一样就接收谁的信息。

我们发现这儿没有publisher中类似于<std_msgs::String>的东西来定义要接收的数据类型。其实是有的,它藏在了第三个参数chatterCallback里。我们注意到chatterCallback和main函数之前定义那个返回值为空的函数名字一样。chatterCallback称为回调函数接收器每一次接收到消息,就会调用名字为它的函数chatterCallback这个名字也是可以随意定义的,只要和前面那个返回值为空的函数有一样的名字就可以了,但一般命名为...Callback这样一看就知道这是ROS使用得回调函数。ROS的回调函数返回值只能为空。如果你想在接收到消息处理后返回什么有用的值怎么办呢?自然是写到类中了(当然也可以用全局变量)。

3:我们看回调函数。(const std_msgs::String::ConstPtr& msg) 这个模式是比较固定的。如果你要接受的是Int8类型的消息,那么参数会变成(const std_msgs::Int8::ConstPtr& msg)

  • ConstPtr代表一个指针
  • msg这个参数是你接收到的消息的指针

ROS_INFO("I heard: [%s]", msg->data.c_str()); msg->data就是一个std::string类型的量

对象和指针的调用区别:在Publisher中我们定义了std_msgs::String对象msg,类包含数据成员data调用方式msg.data。如果类的指针叫msg,那么调用该成员的方式msg->data】

4:ros::spin()会使程序在循环中,一直检测有没有接收到新的消息。其终止方式和使ros::ok()返回false方式一样。

(3) 编译ROS程序

ROS中的程序要先写在CMakeLists中,再使用catkin_make编译。

打开pub_sub_test里面的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})

第一行表示我们要编译add_executable表示我们要添加一个可执行文件pub_string是这个可执行文件的名字(并不是非得和pub_string.cpp中的pub_string一样,不过建议用一样的),src/pub_string.cpp指定要编译的源文件的位置.
第二行target_link_libraries表示我们要将可执行文件链接到一个库,我们要使用ROS当然是要链接到ROS的库了,括号里pub_string指定要链接可执行文件的名字,后面是指定要链接的库的名字.三四行类似,作用是编译sub_string.cpp文件.

接下来编译:

cd ~/catkin_ws/
catkin_make

编译通过:

 (4) 执行ROS程序

打开三个终端,

第一个输入: roscore;

第二个输入:

cd ~/catkin_ws/
source devel/setup.bash
rosrun pub_sub_test sub_string

第三个输入:

cd ~/catkin_ws/
source devel/setup.bash
rosrun pub_sub_test pub_string

显示的结果:

打印出 hello world 0等

图片左边是接收信息,右边是发布信息。

两个程序都是无限循环的,需要在terminal中按ctrl+c退出.但在这之前你可以在打开一个terminal,输入下面的东西

source ~/catkin_ws/devel/setup.bash
rqt_graph

 可以看到下图显示的内容,

talker就是你发布信息的程序中取的节点的名字,listener就是你接收信息的程序中取的节点的名字,chatter就是你在程序中取的topic的名字.这副图传递的信息是
节点 talker 通过 topic chatter 向节点 listener 发布消息


rqt_graph命令简洁地表现除了node和node之间的关系,当你以后有很多节点很多topic时,可以看看这个图像理清思维.


 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值