ROS入门学习

ROS学习笔记

ROS功能包/软件包(Packages)

  • ROS软件包是一组用于实现特定功能的相关文件的集合,包括可执行文件和其余支持文件。
  • 全部的 ROS 软件都是一个软件包或其余软件包的一部分。
  • 每一个程序包由一个清单文件(文件名为 package.xml)定义。
    • 该文件定义关于包的一些细节,包括其名称、版本、维护者和依赖关系。
    • 包含 package.xml 文件的目录被称为软件包目录
    • 使用catkin编译构建系统的功能包, 编译产生的可执行文件存放在一个单独的标准化目录层次结构中。
  • 功能包集(stack): 功能包集是紧密相关的功能包的集合,从groovy开始慢慢地被淘汰, 取而代之的是元功能包(metapackages)。

节点管理器(The Master)

  • 接单(node)是几乎相对独立的小程序,这些节点必须可以通讯, 通讯的关键部分是ROS节点管理器
    • 启动节点管理器的命令 -- roscore。
    • 大多数 ROS 节点在启动时链接到节点管理器上,若是运行中链接中断,则不会尝试从新链接。
    • 所以,若是 roscore被终止,当前运行的其余节点将没法创建新的链接,即便稍后重启 roscore 也无济于事。
    • roslaunch 的工具,其目的是一次性启动多个节点。

节点(Nodes)

  • 一旦启动roscore后,即可以运行ROS程序了, ROS程序的运行实例被称为节点(node)
  • “运行实例”(running instance)很重要。
    • 若是咱们同时执行相同程序的多个副本——注意确保每一个副本使用不一样的节点名——则每一个副本都被当作一个单独的节点。
  • 启动节点:
    • 利用命令rosrun: rosrun package-name executable-name;
    • rosrun 没有什么“神奇”的:它只不过是一个 shell 脚本,可以理解 ROS 的文件组织结构,知道到哪里能找到与给定包名称对应的可执行文件。
    • 经过节点管理器注册成为 ROS 节点发生在程序的内部,而不是经过 rosrun 命令。
  • 查看节点列表:
    • 利用命令: rosnode list;
    • rosout 节点是一个特殊的节点,经过 roscore 自动启动,其做用相似于标准输出(即std::cout)。
    • 节点名不必定与对应可执行文件名称相同。
    • 得到特定节点的信息: rosmode info node-name;
    • 终止节点: rosnode kill node-name;
      • 终止和重启节点一般不会对其余节点有较大影响。
      • 即便节点间正在相互交换消息(message),这些链接也会在节点终止时断开,在节点重启时从新链接。
    • 将节点从列表中删除: rosnode cleanup。

话题和消息

  • ROS节点之间进行通讯所利用的最重要的机制就是消息传递。
  • 在ROS中,消息有组织地存放在话题里。
  • 消息传递的理念:
    • 当一个节点想要分享信息时,它就会发布(publish)消息到对应的一个或者多个话题;
    • 当一个节点想要接收信息时, 它就会订阅(subscribe)它所须要的一个或多个话题;
    • ROS节点管理器负责确保发布节点和订阅节点能找到对方;
    • 消息是直接地从发布节点传递到订阅节点,中间并不通过节点管理器转交;
  • 查看节点构成的计算图:
    • 查看节点之间的发布-订阅关系的最简单方式就是在终端输入rqt_graph命令。
      • r表明ROS, qt指的是Qt图形界面(GUI)工具包;
    • 在默认状况下,rqt_graph 隐藏了其认为只在调试过程当中使用的节点。
    • 的名称/rosout 既指节点又指话题。但 ROS 并不会因这种重复的名字而混淆,由于 ROS 会根据上下文来推测咱们讨论的是/rosout 节点仍是/rosout 话题。
  • ROS 节点一般设计成了只管发布它们有用的信息,而不须要担忧是否有其余节点来订阅这些消息。这样有助于减小各个节点之间的耦合度。
  • ,/teleop_turtle 节点会以消息的形式将这些运动控制命令发布到 话 题 /turtle1/cmd_vel; 与此同时,由于turtlesim_node 订阅了该话题,所以它会接收到这个些消息,控制海龟按照该预约的速度移动。

话题列表

  • 经过rostopic list获取当前活跃的话题;
  • 打印消息内容rostopic echo topic-name;
  • 测试发布频率rostopic hz topic-name(每秒发布消息的数量) 和 rostopic bw topic-name(每秒发布消息所占的带宽);
  • Type在文本输出中表示数据类型;
  • 查看消息类型: rosmsg show message-type-name。
  • 一个复合域是由简单的一个或者多个子域组合而成,其中的每个子域多是另外一个复合域或者独立的域,并且它们通常也都由基本数据类型组成。

用命令行发布消息

  • 利用rostopic命令工具: rostopic pub -r rate-in-hz topic-name message-type message-content(按照指定的频率给指定的话题发布指定的消息);
    • 该命令最后的参数 message-content 应该按顺序提供消息类型中全部域的参数值。
    • rostopic pub -r 1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 3]'命令中利用了-r来指定话题以频率模式发布消息;
      • 锁存模式只是发布一条消息,但锁存模式是默认的模式。
      • -f是从文件中读取,输入应该 符合rostopic echo的输出格式。
      • 编写一种脚本将 rostopic echo 和 rostopic pub 结合起来做为“记录”和“回放”消息的方式;rosbag工具。

理解消息类型的命名

  • 每条消息类型都属于一个特定的包,消息类型名总会包含一个斜杆,斜杆钱面的名字是包含它的包: package-name/type-name;
    • turtlesim/Color中的turtlesim是功能包名, Color是类型名称;
  • ROS 节点管理器不容许多个节点拥有相同的名称。

话题通讯的多对多机制

  • 基于话题和消息的通讯机制是多对多的,即多个发布者和多个订阅者能够共享同一个话题。
  • 节点之间的松耦合关系:
    • 每一个节点都不须要显式知道其余节点的存在与否;它们的惟一交互方式是间接地发生在基于话题和消息的通讯层。
    • 节点之间的独立性,以及其支持的任务可分解特性(即复杂任务分解成可重用的小模块),是 ROS 最关键的设计特性之一。
    • “生产”消息的程序(例如 turtle_teleop_key)只管发布该消息,而不用关心该消息是如何被“消费”的。
    • “消费”消息的程序(例如 turtlesim_node)只管订阅该话题或者它所须要消息的全部话题,而不用关心这些消息数据是如何“生产”的。
  • ROS为更加直接的一对一通讯提供了一种称为服务(services)的机制。
  • ROS 利用roswtf进行问题的查找。
    • roswtf 将会检测在安装过程当中 rosdep 初始化是否完成,任何节点是否出现了意外的挂起或者终止,以及活跃的节点是否正确地和其余节点相链接等。
    • roswtf 检测的完整列表只能在 Python 源码中才能找到。

编写ROS程序

  • 学习编写可以发布和订阅消息的 ROS 程序。

建立工做区和功能包

  • 建立工做区,咱们建立的包,应该所有放到一个叫作工做区的目录中。
    • 工做区就是包含本身全部包的一个工做区目录。
  • ROS的catkin编译系统试图一次性编译同一个工做区中的全部功能包,若是设计到几个相互独立的项目,则应该维护数个独立的工做区。
  • 每一个工做区必须建立一个src的子目录,这个子目录讲用于存放功能包的源码。
  • catkin_create_pkg package-name是建立一个功能包。
    • 一个配置文件 -- package.xml这是一个清单文件。
    • CMakeLists.txt是一个Cmake的脚本文件, 其实catkin在内部使用了Cmake。
  • ROS 包的命名遵循一个命名规范,只容许使用小写字母、数字和下划线,并且首字符必须是一个小写字母。一些 ROS工具,包括 catkin,不支持那些不遵循此命名规范的包。
  • 本着保持文档与实际功能同步的精神,至少在package.xml中填写description 和 maintainer 两部分多是比较合理的
  • 包含消息的声明: 每个话题都与一个消息类型相关联,每个消息类型都有一个相对应C++头文件。
    • #include <package_name/type_name.h>头文件, 像#include <geometry_msgs/Twist.h>表示名为 geometry_msgs 的包所拥有的类型为 Twist 的消息。

编写回调函数

  • 发布和订阅消息的一个重要的区别是订阅者节点没法知道消息何时才能到达, 为了对应这一事实, 咱们必须把响应收到消息事件的代码放到回调函数里, ROS每接收到一个新的消息将调用一次这个函数。

日志消息

  • 学习如何生成和查看日志消息。
  • ROS中分为5个不一样的严重级别,按严重性程度递增为: DEBUG、INFO、WARN、ERROR、FATAL。
    • DEBUG: reading header from buffer.
    • INFO: Waiting for all connections to establish.
    • WARN: Less than 5GB of space free on disk.
    • ERROR: Publisher header did not have required element: type.
    • FATAL: You must call ros::init() before creating the first NodeHandle.
  • 为日志系统自己就是面向行的, 没有必要使用std::endl或者其余的行终止符。
  • 检查和清除日志文件:
    • 这些日志文件将随着时间积累;
    • rosrun和roslaunch运行时会检查和检测已经存在的日志的大小,并会在日志文件大小超过1GB时提醒,可是不会采起任何措施来减少日志文件的大小。
    • 查看当前用户使用的日志文件的当前大小: rosclean check。
    • 删除全部已经存在的日志: rosclean purge。
    • 尝试生成DEBUG级别的消息将会被忽略。
    • 设置日志级别相似于 rqt_consolt 中的日志级别过滤选项。
    • ROS_INFO_STREAM 等构建方式是宏而不是函数调用。这些宏对应的展开代码会检查消息是否被启用,而且只会评估那些被启用消息的表达式。
  • 经过命令行设置日志级别:
    • rosservice call /node-name/set_logger_level ros.package-name level;
    • 这条命令调用 set_logger_level 服务,该服务由各个节点自动提供。
    • roservice 的参数 ros.package-name 是必需的,用来指明咱们指望配置的日志记录器(logger)的名称。
  • rqt_logger_level经过图形界面设置日志级别;
  • 经过C++代码设置日志级别,最直接的方式是调用ROS来实现日志功能的log4cxx提供的代码接口
#include <log4cxx/logger.h>
. . .
log4cxx::Logger::getLogger(ROSCONSOLE_DEFAULT_NAME)->setLevel(
ros::console::g_level_lookup[ros::console::levels::Debug]
);
ros::console::notifyLoggerLevelsChanged();
  • 调用 ros::console::notifyLoggerLevelsChanged()是有必要的,由于每一个日志的启用或者禁用是缓存了的。

计算图源命名

  • ROS的计算图资源(节点、话题、参数、和服务等)的命名和解析。

全局名称

  • 节点、话题、服务和参数统称为计算图源,而每一个计算图源由一个叫计算图源名称(graph resource name)的短字符串标识。
  • 前斜杆"/"代表这个名称为全局名称, 由斜杆分开的一系列命名空间(namespace), 每一个斜杆表明一级命名空间。
  • 描述资源自己的基本名称(base name)。

相对名称

  • 一个主要替代方案是让 ROS为计算图源提供一个默认的命名空间,具备此特征的名称叫作相对计算图源名称(ralative graph resource name),或简称为相对名称(relative name)。
  • 设置默认命名空间:
    • 默认的命名空间是单独地为每一个节点设置的,而不是在系统范围进行。
    • 大部分 ROS 程序,包括调用 ros::init 的全部 C++程序,接受叫作_ns 的命令行参数,此参数将为程序指定一个默认命名空间。 _ns:=default-namespace;
    • 还能够利用环境变量为在 shell 内执行的 ROS 程序设置默认命名空间。Export ROS_NAMESPACE=default-namespace

私有名称

  • 私有名称,以一个波浪字符(~)开始,是第三类也是最后一类计算图源名称。
  • 私有名称并不能彻底肯定它们自身所在的命名空间,而是须要 ROS 客户端库将这个名称解析为一个全局名称。
  • 与相对名称的主要差异在于,私有名称不是用当前默认命名空间,而是用的它们节点名称做为命名空间。

匿名名称(Anonymous names)

  • 匿名名称的目的是使节点的命名更容易遵照惟一性的规则。其思路是,当节点调用 ros::init 方法时能够请求一个自动分配的惟一名称。
  • 为了请求一个匿名名称,节点须要将ros::init_options::Anonymous-Name 做为第四个参数传递给ros::init 方法:ros::init(argc, argv, base_name, ros::init_options::AnonymousName);

启动文件

  • 利用启动文件一次性配置和运行多个节点,ROS提供了一个同时启动节点管理器(master)和多个节点的途径,即启动文件(launch file)
  • 启动文件须要roslaunch工具来进行启动。

使用启动文件

  • roslaunch的基本思想是在一个XML格式的文件内将须要同时启动的一组节点罗列出来。

在命名空间内启动节点

  • 对一个节点设置默认命名空间,这个过程一般叫作压人(pushing down)命名空间--的一般方法是使用一个启动文件,并对其节点元素配置命名空间(ns,ns = namesapce)属性。
  • 事实上 roslaunch 要求启动文件中的节点名称是基名称,即不涉及任何命名空间的相对名称。若是节点元素的名称属性中出现了全局名称,roslaunch 将会报错。

名称重映射(Remapping names)

  • 除了相对名称和私有名称, ROS节点还支持重映射(remapping), 它能够从更精细的层面控制对所用节点名称的修改。
  • 重映射是基于替换的思想: 每一个重映射包含一个原始名称和一个新名称。
  • 每当节点使用重映射中的原始名称时, ROS客户端库就会将它默默地替换成其对应的名称。

启动参数(launch arguments)

  • 为了使启动文件便于配置, roslaunch还支持启动参数,其功能有点像可执行程序中的局部变量;
  • 这样的有点是经过设置参数来描述节点在不一样ROS会话中运行时可能须要改变.
  • 向包括的启动文件中发送参数值,参数仅定义在对其进行声明的启动文件中,而不能被包含的启动文件继承;
  • 将arg元素做为一个包含元素的子元素。
<incluce file=”path–to-launch-file”>
    <arg name=”arg-name” value=”arg-value”/>
    …
</include>

建立组(Creating groups)

  • 组元素(group)是启动文件的最后一个特征,它提供了一种在大型启动文件内管理节点的便捷方式。
  • 组能够把若干个节点放入同一个命名空间内:
<group ns=”namespace”/>
…
</group>
  • 组内的每一个节点都从给定的默认命名空间启动。
  • 若是一个组元素内的某个节点有它本身的命名空间属性,而且其名称是(原则上也应该是)相对名称,那么该节点将会在一个默认命名空间内启动,这个默认的命名空间是将此节点命名空间嵌入到组元素命名空间的结果。这个规则和前面讲到的名称解析是相符的,而且这个规则也适用于组元素的嵌套。
  • 组能够有条件地使能或禁止一个节点:
<group if=”0 or 1”/>
</group>
  • 只有 0 和 1 才是 if 和 unless 属性的合法取值。特别须要提醒的是,读者熟悉的布尔型运算符 AND 和 OR 在这里不能直接使用。
  • 使用组能够减小代码重复——命名空间和条件设置仅出现一次——而且使启动文件的结构更加清晰。

参数

  • ROS还提供另外一种参 数(parameters)机制用于获取节点的信息。
  • 使用集中参数服务器(parameter server)维护一个变量集的值, 包括整数,浮点数,字符串以及其余数据类型,每一个变量用一个较短的字符串标识。

经过命令行获取参数

  • 查看参数列表: rosparam list。
  • 参数服务器是节点管理器的一部分,所以,它老是经过 roscore 或者 roslaunch 自动启动。
  • 须要铭记的是,全部的参数都属于参数服务器而不是任何特定的节点。
  • 查询参数:
    • rosparam get parameter_name, 查询某个参数的值。
    • rosparam get namespace, 经过查询全局命名空间,咱们能够一次性看到全部参数的值。
  • 设置参数:
    • rosparam set parameter_name parameter_value, 修改一个已有参数或者建立一个新的参。
    • 以YAML字典的形式表示参数和对应值的映射关系。
    • 冒号后的空格是很是重要的,以确保 rosparam 将其做为一个/duck_colors 命名空间内的参数集,而不是全局命名空间中的单个字符串参数 duck_colors。
  • 建立和加载参数文件:
    • rosparam dump filename namespace, 了以 YAML 文件的形式存储命名空间中的全部参数。
    • rosparam load filename namespace, 从一个文件中读取参数,并将它们添加到参数服务器。
  • 一个好的策略是演示真正的ROS节点如何工做得更好案例,是turtle首先测试这些参数是否存在,当且仅当这些参数不存在时, 才至指定默认的蓝色。
  • rosparam get 命令是获取背景参数的值。
  • 若是节点关心它的一些或者全部参数是否改变,必须明确向参数服务器请求这些参数的值。

使用C++获取参数

  • ROS参数的C++接口是至关简单的: void ros::param::set(parameter_name, input_value);和bool ros::param::get(parameter_name, output_value);

在启动文件中设置参数

  • 设置参数:
    • 可使用param元素请求roslaunch设置参数值,
  • 设置私有参数:
    • 做为节点元素的子集时,param元素中给出的参数名老是被当作私有名称解析,不管它们是否以~或者/开始。
    <node . . . >
    <param name="param-name" value="param-value" />
     . . .
    </node>
    // 启动文件中设置
    <launch>
      <node
          pkg="turtlesim "
          type="turtlesim_node"
          name="turtlesim "
      />
      <node
          pkg="agitr "
          type="pubvel_with_max"
          name="publish_velocity "
      >
      <param name="max_vel" value="3" />
      </node>
      <node
          pkg="agitr "
          type="set_bg_color"
          name="set_bg_color"
      />
    </launch>
  • 在文件中读取参数: 启动文件也支持与rosparam load等价的命令, 能够一次性从文件中加载多个参数: <rosparam command="load" file="path-to-param-file" />
    • 这里的参数文件一般是经过rosparam dump命令建立的。
    <rosparam
        command="load"
        file="$(find package-name)/param-file"
    />
  • 参数的思想虽然简单, 可是能够大大提升ROS节点的灵活性和可配置性。

服务

  • 消息传递是ROS中节点通讯的主要方法, 但确实受到了必定的限制,服务调用(service calls)是另外一种通讯的方法。
  • 服务调用和消息的区别在两个方面:
    • 服务调用是双向的, 一个节点给另外一个节点发送信息并等待响应,所以信息流是双向的。消息发布后并无响应的概念,甚至不能保证系统内有节点订阅了这些消息。
    • 服务调用实现的是一对一通讯, 每个服务有一个节点发起,这对这个服务的响应返回同一个节点。另外一方面,每个消息都和一个话题相关,这个话题可能有不少的发布者和订阅者。
    • 消息和服务十分类似。

服务的专用术语

  • 一个客户端(client)节点发送一些称为请求(request)的数据到一个服务器(server)节点,并等待回应。
  • 服务器节点接收到请求后, 采起一些行动(计算、配置软件和硬件、改变自身行为等),而后发送一些称为响应(response)的数据给客户端节点。
  • 请求和响应数据携带的特定内容由服务数据类型(service data type)来决定,它与决定消息内容的消息类型是相似的,服务数据类型也是由一系列域构成;
    • 惟一区别是,服务数据类型分为两部分,分别表示请求(客户端节点提供服务器节点)和响应(服务其节点反馈给客户端节点)。

从命令行查看和调用服务

  • 服务一般由节点内部的代码调用;
  • 列出全部服务: ros service list; 服务名是计算图源名称,同其余资源名称同样,能够划分为全局的、相对的或者私有的名称。rosservice list命令的输出是全部服务的全局名称。
  • 服务一般将节点名用做命名空间来防止命名冲突,而且容许节点经过私有名称来提供服务。
  • 查看某个节点的服务类型,查看一个特定节点提供的服务,使用rosnode info命令。
  • 查找提供服务的节点: rosservice node service-name;
  • 查看服务数据类型: 当服务的数据类型已知时, 咱们可使用rossrv指令来得到此服务数据类型的详情 -- rossrv show service-data-type-name。
  • 服务数据类型中的请求或响应字段能够为空,甚至两个字段能够同时为空。
  • 从命令行调用服务: rosservice call service-name request-content

客户端程序

  • 声明请求和响应的类型,必须包含相关的头文件: #include <package_name/type_name.h>
  • 建立一个客户端对象, ros::ServiceClient client = node_handle.serviceClient<service_type>(service_name);
    • service_name是一个字符串,应当是一个相对名称。
    • 与建立相似的 ros::Publisher 对象相比,建立 ros::ServiceClient 对象不须要队列大小。
  • 调用服务: 一旦拥有了一个ServiceClient, 一个完整的Request以及Response, 咱们就能够调用服务了: bool success = service_client.call(request, reponse);
    • 这个方法实际上完成了定位服务器节点、传输请求数据、等待响应和存储响应数据等一系列工做。
  • 声明依赖: 须要编辑CMakeLists.txt和清单文件packag.xml。
    • 必须保证CMakeLists.txt中的find_package行涉及了turtlesim功能包: find_package(catkin REQUIRED COMPONENTS roscpp turtlesim)。
    • 在package.xml中, 咱们应当确保build_depend和run_depend元素中存在相同名称的包, turtlesim turtlesim turtlesim

服务器程序

  • 必须建立一个ros::ServiceServer来代替ros::Subscriber, 惟一的区别在于服务端能够经过一个响应对象和一个代表成功与否的不二比昂两给客户端回传数据。
  • 节点每次接收到一个服务请求,ROS就执行一次回调函数,参数Request中包含了来自客户端的数据,回调函数的工做是给Response对象的数据成员赋值。
  • 建立服务器对象: ros::ServiceServer server = node_handle.advertiseService(service_name,pointer_to_callback_function);
    • 私有名称以~开始。
  • 可使用两个分开的线程:一个发布消息,一个处理服务回调。 尽管 ROS没有明确指出程序要使用多个线程,但若是能够的话,这是很是便于合做的。
  • 能够用ros::spin来代替sleep/ros::spinOnce循环,而且利用计数器回调函数(timer callback)来发布消息。

消息录制与回放

  • ROS 系统的一个重要特征即是系统中信息的消费者不该该关心信息的生产者。
    • 这种体系架构最明显的体现是ROS主要使用的消息发布-订阅模型。
    • 不论什么时刻,只要有消息被发布,其订阅节点就应该正常工做,而无论是哪一个或是那些节点正在发布这些消息。
  • rosbag工具,可以发布在一个或这多个话题上的消息录制到一个包文件中,而后能够回放这些消息,重现像是的运行过程。

录制与回放包文件

  • 术语包文件(bag files)是指用于存储带时间戳的ROS消息的特殊格式文件,rosbag命令行工具能够用来录制和回放包文件。
  • 录制包文件: rosbag record -O filename.bag topic-names。
    • 若是不指定文件名,rosbag 将基于当前的日期和时间自动生成一个。
    • 用rosbag record -a 记录当前发布的全部话题的消息。
    • 用 rosbag record -j 启用包文件的压缩。
  • 回放包文件: rosbag info filename.bag。
  • 持续时间、消息计数以及话题列表三个字段彷佛颇有趣。
  • 录制正方形轨迹的包文件,rosbag record -O square.bag /turtle1/cmd_vel /turtle1/pose

启动文件里面的包文件

  • 经过这两个可执行文件能够很容易地将包文件做为启动文件的一部分,方法是包含适当的节点元素。
// 录制节点
<node
    pkg="rosbag"
    name="record"
    type="record"
    args="-O filename.bag topic-names"
/>
// 回放节点
<node
    pkg="rosbag"
    name="play"
    type="play"
    args="filename.bag"
/>
  • 在网络环境中运行ROS, ROS的一大优点是支持分布式机器人控制模式,即诸多程序运行在不一样的计算机上,经过互相交互来完成指定任务。
    • 须要在网络层和ROS层进行配置,网络层确保计算机之间可以互相通讯, 而ROS层的配置是确保全部节点都能与节点管理器通讯。
  • 编写更规范的程序, 程序必定要注重可扩展性和可维护性;
    • 用ros::Timer的回调函数来替代ros::Rate对象。
  • 使用rviz使数据可视化,机器人传感器得到的数据不只复杂并且一般带有噪声, ROS提供了一个叫作rviz的很是强大的图形界面工具,它能够经过订阅用户选择的话题来显示机器人内部的各类信息,便于机器人的开发和调试。
  • 建立消息和服务类型
  • 使用tf工具来管理多个坐标系,用不一样的坐标系来描述机器人不一样部件的位置,包括机器人要避开或交互的目标。
  • 须要知道将某个坐标从一个坐标系到另外一个坐标系的变换矩阵(Transforation),ROS提供了一个标准功能包,来帮助节点来完成坐标转换。
    • tf具备很强的鲁棒性,既可以处理来自不一样节点的数据,也能应对坐标系的实时变换。
  • 使用Gazebo仿真, ROS系统最大的优点之一就是可以实现软件的模块化设计,基于这个框架能够轻易地替换系统中的各类软件模块,从而节约系统的开发时间,也使得测试变得简单方便。
    • Gazebo是一个高保真的机器人仿真器。
    • 在Gazebo中,咱们能够创建机器人和相应场景的仿真模型,而后为仿真机器人定义与实体机器人相同的通讯接口。