ROS学习笔记(wiki)

ROS的文件系统


在这里插入图片描述

catkin_workspace


workspace_folder/         -- WORKSPACE
  src/                    -- SOURCE SPACE
    CMakeLists.txt        -- The 'toplevel' CMake file
    package_1/
      CMakeLists.txt
      package.xml
      ...
    package_n/
      CATKIN_IGNORE       -- Optional empty file to exclude package_n from being processed
      CMakeLists.txt
      package.xml
      ...
  build/                  -- BUILD SPACE
    CATKIN_IGNORE         -- Keeps catkin from walking this directory
  devel/                  -- DEVELOPMENT SPACE (set by CATKIN_DEVEL_PREFIX)
    bin/
    etc/
    include/
    lib/
    share/
    .catkin
    env.bash
    setup.bash
    setup.sh
    ...
  install/                -- INSTALL SPACE (set by CMAKE_INSTALL_PREFIX)
    bin/
    etc/
    include/
    lib/
    share/
    .catkin             
    env.bash
    setup.bash
    setup.sh
    ...

catkin_workspace最多有四个space(文件夹),每个space有不同的作用。

  • Source Space:该空间下每个文件夹里有一个或多个catkin packages。该空间根目录下必须有一个CMakeLists.txt文件,可以在该空间根目录使用catkin_init_workspace命令生成该文件。

  • Build Space:该空间是调用cmake和make来编译source space中的catkin packages的路径。cmake和catkin把缓存信息(cache information)和其他中间件(intermediate files)保存在这里。

  • Development (Devel) Space :编译后的目标的存放路径。

  • Install Space:make install命令存放的路径

参考wiki.ros.org中关于catkin工作空间的描述1

ROS下载包


在这里插入图片描述
把PACKAGE替换成想要下载的包名称

ROS中重要的概念


在这里插入图片描述
在这里插入图片描述

Node

一个节点就是在ros package里的一个可执行文件,ros节点通过ROS client library和其他节点互相通信。节点的功能:

  • 订阅或发布topic主题

  • 提供或使用service服务

在这里插入图片描述

编写的node节点在cmake后会生成对应的可执行文件,放在devel/lib/<package>对应的包名称的路径下。

Topic

可以通过rostopic list -v或者图形化工具rosrun rqt_graph rqt_graph查看节点和话题之间的关系
在这里插入图片描述
显示被发布和被订阅的主题,并分别显示了发布者和订阅者的数量、消息类型message type。
在这里插入图片描述
箭头上是主题,箭头从发布者指向订阅者。

使用图形化工具rosrun rqt_plot rqt_plot可以显示话题里发布的数据。

ROS Services

服务是节点和其他节点互相交流的另一个方式,服务允许节点发送一个服务请求request和接收一个应答response。
使用rosservice type [service]可以查看服务的类型,如果类型是std_srvs/Empty,说明当请求该服务时不需要任何参数(请求服务时不会发送数据,接收应答时不会接收数据)。

Parameter Server

参数服务器可以存储integers, floats, boolean, dictionaries, and lists。

rqt_console和rqt_logger_level

rqt_console链接到ros的日志机制(logging framework)来显示节点运行时的输出。
rqt_logger_level可以改变显示的日志等级:(Fatal,Error,Warn,Info,Debug)。

roslaunch

roslaunch从一个启动文件(launch file)里启动节点。

<launch>

  # 使用group可以建立两个相同名称的节点,不会起冲突,因为在两个group里
  <group ns="turtlesim1">
    # 包名称     自定义节点名称  节点名称
    <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
  </group>

  <group ns="turtlesim2">
    <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
  </group>

  # 第3个节点
  <node pkg="turtlesim" name="mimic" type="mimic">
    <remap from="input" to="turtlesim1/turtle1"/>
    <remap from="output" to="turtlesim2/turtle1"/>
  </node>

</launch>

msg

msg存储在包的msg/目录下,msg只是简单的文本文件,每行都有字段类型和字段名称。

字段类型:
在这里插入图片描述

ros里有一种特殊的字段类型:Header。包含时间戳(timestamp)和坐标帧信息(coordinate frame information),经常看到在msg文件的第一行有Header header声明。

srv

msg存储在包的srv/目录下,srv和msg的区别是:srv有request和response两个部分,由虚线分隔开。虚线上面是request,下面是response。

int64 A
int64 B
---
int64 Sum

编写的.msg和.srv文件在cmake的时候都会生成对应的头文件,放在devel/include/路径下

新建messages,services或actions


以下是使用catkin_create_pkg生成的CMakeLists.txt里的注释:
在这里插入图片描述
要在这个包里声明和编译messages,services或actions,跟随以下步骤:

MSG_DEP_SET是你在message/services/actions中使用的消息类型(message types)的包的集合(使用MSG_DEP_SET这个名字只是为了下面的注释方便说明,没有其他目的,不是ros里特殊的宏定义)。

在这个包的package.xml里:

  • <build_depend>里添加message_generation
  • MSG_DEP_SET里的每个包添加<build_depend><exec_depend>
  • 如果 MSG_DEP_SET 不为空,则以下依赖项已被引入,但仍然可以确定地声明:
    <exec_depend>里添加message_runtime

在这个包的CMakeLists.txt里:

  • find_package(catkin REQUIRED COMPONENTS ...)添加message_generationMSG_DEP_SET里的每个包
  • catkin_package(CATKIN_DEPENDS ...)里添加message_runtimeMSG_DEP_SET里的每个包
  • 取消对应的add_*_files注释,并添加.msg/.srv/.action文件
  • 取消generate_messages的注释,并在generate_messages(DEPENDENCIES ...)添加MSG_DEP_SET里的每个包

任何在msg文件夹下的.msg文件都会生成ros所支持的语言的代码,下面是以/home/lyq/catkin_ws/src/beginner_tutorials为例,请将的catkin_ws换成自己的catkin_workspace根路径,beginner_tutorials换成自己的包的根路径。
在这里插入图片描述

参考wiki创建msg和srv教程2

写一个Publisher Node:src/talker.cpp

//'ros/ros.h'是一个方便的头文件,它包含了使用ros系统中最常见的公共部分所必需的所有头文件。
#include "ros/ros.h"
//由于使用到`std_msgs/String`的消息类型,需要包含对应的头文件`std_msgs/String.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)
{
  ros::init(argc, argv, "talker");	//初始化ros并定义节点名称。

  /**
   * NodeHandle 是和 ROS system 通信的主要接口。
   * NodeHandle 是完全初始化节点的地方。
   * 对应的解构的 NodeHandle 会释放node使用的所有资源.
   */
  ros::NodeHandle n;

  /**
   * 使用 advertise() 函数告诉ros你要发布的主题名称和发布的方式。
   * 这会调用 ROS master node,登记(registry)哪个节点在发布/订阅主题。
   * 在 advertise() 函数调用完成后,ROS master node会注意哪些节点尝试订阅这个主题,
   * 这些节点会分别和发布这个主题的节点建立一个点对点的连接。
   * advertise() 函数返回一个 Publisher 对象,通过它可以调用 publish() 函数来发布消息。
   * 当这个 Publisher 对象的所有副本都被 destroyed 时,这个主题会被自动取消广播。
   *
   * advertise() 函数的第二个参数是用来发布消息的消息队列 message queue 的长度
   * 如果消息的发布速度比我们发送它们的速度快,这里的数字指定在丢弃一些消息之前要缓冲多少消息。
   */
  // 发布一个名为chatter的主题,消息类型是std_msgs::String
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

 //定义循环频率,从最后一次调用Rate::sleep()开始计算时间。这里定义为10Hz
  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()); //ROS_INFO

    // publish() 的参数类型必须和前面调用的 advertise<>() 相同 
    chatter_pub.publish(msg);

    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }


  return 0;
}

在主函数开始必须用ros::init()先初始化ros系统,ros::init()有两种方式:

  • ros::init(argc, argv):通过命令行参数映射节点名称
  • ros::init(argc, argv,"<node_name>"):直接通过代码自己定义节点名称

关于消息队列message queue,wiki教程中有说道:

  • 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.
  • The second argument is the size of our publishing queue.In this case if we are publishing too quickly it will buffer up a maximum of 1000 messages before beginning to throw away old ones.

关于ros::ok():
在这里插入图片描述

关于ros::spinOnce():
在这个程序里不重要,因为没有回调函数callbacks。如果要订阅主题,需要调用这个函数,否则不会调用回调函数。

写一个Subscriber Node:src/listener.cpp

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

/**
 * This tutorial demonstrates simple receipt of messages over the ROS system.
 */
// 当一个新的消息到达chatter主题时,会调用这个回调函数,消息通过一个boost shared_ptr指针传递。
// https://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm
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;

  /**
   * subscribe() 函数告诉 ROS 你想要订阅一个特定的主题。
   * 这会调用 ROS master node,登记(registry)哪个节点在发布/订阅主题。
   * 消息被传递到一个回调函数,在这里是 chatterCallback
   * subscribe() 函数返回一个 Subscriber 对象,直到你取消订阅前要保证它的存在。
   * 当这个对象的所有副本被销毁时,回调函数会自动取消订阅
   *
   * subscribe() 函数的第二个参数是消息队列,第三个参数是回调函数名称
   * 如果消息到达的速度大于我们处理的速度,达到最大mq长度时会丢弃旧的消息。
   */
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  /**
   * ros::spin() 会进入一个循环, 产生 callbacks.  
   * 在ros1,所有的回调都会在这个线程(the main one)里调用。
   * 当按下Ctrl-C或节点被master关闭,ros::spin()会退出。
   */
  ros::spin();

  return 0;
}

ros:spin()的作用:
在这里插入图片描述
ros:spin()不会返回,一旦接受到数据就会执行回调函数。注意回调函数是ros:spin()调用的。

编译节点

在这个包的CMakeLists.txt里添加:

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
  • 这会产生2个可执行文件 talker 和 listener,默认会放在你的devel space的对应包目录下路径在~/catkin_ws/devel/lib/<package name>.

  • add_dependencies()用法:
    第一个参数是要编译生成的target(这里是可执行文件),其他是这个target的依赖。告诉编译器先编译其他依赖,再编译target。

写一个Service Node:src/add_two_ints_server.cpp

#include "ros/ros.h"
//`beginner_tutorials/AddTwoInts.h`是从`.srv`文件生成的头文件
#include "beginner_tutorials/AddTwoInts.h"

// 注意这里的参数类型: Request 和 Response,并且是用指针传递
bool add(beginner_tutorials::AddTwoInts::Request  &req,
         beginner_tutorials::AddTwoInts::Response &res)
{
  res.sum = req.a + req.b;
  ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  return true;
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_two_ints_server");	// 初始化ros节点
  ros::NodeHandle n;	// 节点句柄

 // 发布服务,第一个参数是服务的名称,第二个参数是调用的函数
  ros::ServiceServer service = n.advertiseService("add_two_ints", add);
  ROS_INFO("Ready to add two ints.");
  ros::spin();

  return 0;
}

在这里ros::spin()的作用和subcriber中的作用类似,一旦调用不会返回,当接收到数据时自动调用add函数。

写一个Client Node:src/add_two_ints_client.cpp

#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_two_ints_client");
  if (argc != 3)
  {
    ROS_INFO("usage: add_two_ints_client X Y");
    return 1;
  }

  ros::NodeHandle n;

  // <>里是数据类型,参数是服务的名称
  ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
  
  beginner_tutorials::AddTwoInts srv;
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);
  
  // call()函数调用了服务,调用成功会返回true并且改变srv.response。
  // 如果失败,srv.response不会改变。
  if (client.call(srv))	
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_two_ints");
    return 1;
  }

  return 0;
}

编译节点

add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)

add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
  • 生成2个可执行文件,默认放在devel space下对应的包目录里,默认是~/catkin_ws/devel/lib/<package name>
    在这里插入图片描述

参考


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值