ROS简介 本系列文档来自古月居授权转载,官方网址：http://www.guyuehome.com 系列目录 ROS探索总结-1.ROS简介 ROS探索总结-2.ROS总体框架 ROS探索总结-3.ROS新手教程 ROS探索总结-4.简单的机器人仿真 ROS探索总结-5.创建简单的机器人模型smartcar ROS探索总结-6.使用smartcar进行仿真 ROS探索总结-7.smartcar源码上传 ROS探索总结-8.键盘控制 ROS探索总结-9.操作杆控制 ROS探索总结-10.语音控制 ROS探索总结-11.机器视觉 ROS探索总结-12.坐标系统 ROS探索总结-13.导航与定位框架 ROS探索总结-14.move_base（路径规划） ROS探索总结-15.amcl（导航与定位） ROS探索总结-16.HRMRP机器人的设计 ROS探索总结-17.构建完整的机器人应用系统 ROS探索总结-18.重读tf ROS探索总结-19.如何配置机器人的导航功能 ROS探索总结-20.发布导航需要的传感器信息 ROS探索总结-21.如何发布里程计消息 ROS探索总结-22.设置机器人的tf变换 ROS探索总结-23.解读URDF ROS探索总结-24.使用gazebo中的插件 ROS探索总结-25.MoveIt基础 ROS探索总结-26.MoveIt编程 ROS探索总结-27.ROS Industrial 一、历史 随着机器人领域的快速发展和复杂化，代码的复用性和模块化的需求原来越强烈，而已有的开源机器人系统又不能很好的适应需求。2010年Willow Garage公司发布了开源机器人操作系统ROS（robot operating system），很快在机器人研究领域展开了学习和使用ROS的热潮。 ROS系统是起源于2007年斯坦福大学人工智能实验室的项目与机器人技术公司Willow Garage的个人机器人项目（Personal Robots Program）之间的合作，2008年之后就由Willow Garage来进行推动。已经有四年多的时间了 (视频)。随着PR2那些不可思议的表现，譬如叠衣服，插插座，做早饭，ROS也得到越来越多的关注。Willow Garage公司也表示希望借助开源的力量使PR2变成“全能”机器人。 PR2价格高昂，2011年零售价高达40万美元。PR2现主要用于研究。PR2有两条手臂，每条手臂七个关节，手臂末端是一个可以张合的钳子。PR2依靠底部的四个轮子移动。在PR2的头部，胸部，肘部，钳子上安装有高分辨率摄像头，激光测距仪，惯性测量单元，触觉传感器等丰富的传感设备。在PR2的底部有两台8核的电脑作为机器人各硬件的控制和通讯中枢。两台电脑安装有Ubuntu和ROS。 二、设计目标 ROS是开源的，是用于机器人的一种后操作系统，或者说次级操作系统。它提供类似操作系统所提供的功能，包含硬件抽象描述、底层驱动程序管理、共用功能的执行、程序间的消息传递、程序发行包管理，它也提供一些工具程序和库用于获取、建立、编写和运行多机整合的程序。 ROS的首要设计目标是在机器人研发领域提高代码复用率。ROS是一种分布式处理框架（又名Nodes）。这使可执行文件能被单独设计，并且在运行时松散耦合。这些过程可以封装到数据包（Packages）和堆栈（Stacks）中，以便于共享和分发。ROS还支持代码库的联合系统。使得协作亦能被分发。这种从文件系统级别到社区一级的设计让独立地决定发展和实施工作成为可能。上述所有功能都能由ROS的基础工具实现。 三、主要特点 ROS的运行架构是一种使用ROS通信模块实现模块间P2P的松耦合的网络连接的处理架构，它执行若干种类型的通讯，包括基于服务的同步RPC（远程过程调用）通讯、基于Topic的异步数据流通讯，还有参数服务器上的数据存储。但是ROS本身并没有实时性。 ROS的主要特点可以归纳为以下几条： （1）点对点设计 一个使用ROS的系统包括一系列进程，这些进程存在于多个不同的主机并且在运行过程中通过端对端的拓扑结构进行联系。虽然基于中心服务器的那些软件框架也可以实现多进程和多主机的优势，但是在这些框架中，当各电脑通过不同的网络进行连接时，中心数据服务器就会发生问题。 ROS的点对点设计以及服务和节点管理器等机制可以分散由计算机视觉和语音识别等功能带来的实时计算压力，能够适应多机器人遇到的挑战。 （2）多语言支持 在写代码的时候，许多编程者会比较偏向某一些编程语言。这些偏好是个人在每种语言的编程时间、调试效果、语法、执行效率以及各种技术和文化的原因导致的结果。为了解决这些问题，我们将ROS设计成了语言中立性的框架结构。ROS现在支持许多种不同的语言，例如C++、Python、Octave和LISP，也包含其他语言的多种接口实现。 ROS的特殊性主要体现在消息通讯层，而不是更深的层次。端对端的连接和配置利用XML-RPC机制进行实现，XML-RPC也包含了大多数主要语言的合理实现描述。我们希望ROS能够利用各种语言实现的更加自然，更符合各种语言的语法约定，而不是基于C语言给各种其他语言提供实现接口。然而，在某些情况下利用已经存在的库封装后支持更多新的语言是很方便的，比如Octave的客户端就是通过C++的封装库进行实现的。 为了支持交叉语言，ROS利用了简单的、语言无关的接口定义语言去描述模块之间的消息传送。接口定义语言使用了简短的文本去描述每条消息的结构，也允许消息的合成，例如下图就是利用接口定义语言描述的一个点的消息： 每种语言的代码产生器就会产生类似本种语言目标文件，在消息传递和接收的过程中通过ROS自动连续并行的实现。这就节省了重要的编程时间，也避免了错误：之前3行的接口定义文件自动的扩展成137行的C++代码，96行的Python代码，81行的Lisp代码和99行的Octave代码。因为消息是从各种简单的文本文件中自动生成的，所以很容易列举出新的消息类型。在编写的时候，已知的基于ROS的代码库包含超过四百种消息类型，这些消息从传感器传送数据，使得物体检测到了周围的环境。 最后的结果就是一种语言无关的消息处理，让多种语言可以自由的混合和匹配使用。 （3）精简与集成 大多数已经存在的机器人软件工程都包含了可以在工程外重复使用的驱动和算法，不幸的是，由于多方面的原因，大部分代码的中间层都过于混乱，以至于很困难提取出它的功能，也很难把它们从原型中提取出来应用到其他方面。 为了应对这种趋势，我们鼓励将所有的驱动和算法逐渐发展成为和ROS没有依赖性单独的库。ROS建立的系统具有模块化的特点，各模块中的代码可以单独编译，而且编译使用的CMake工具使它很容易的就实现精简的理念。ROS基本将复杂的代码封装在库里，只是创建了一些小的应用程序为ROS显示库的功能，就允许了对简单的代码超越原型进行移植和重新使用。作为一种新加入的有优势，单元测试当代码在库中分散后也变得非常的容易，一个单独的测试程序可以测试库中很多的特点。 ROS利用了很多现在已经存在的开源项目的代码，比如说从Player项目中借鉴了驱动、运动控制和仿真方面的代码，从OpenCV中借鉴了视觉算法方面的代码，从OpenRAVE借鉴了规划算法的内容，还有很多其他的项目。在每一个实例中，ROS都用来显示多种多样的配置选项以及和各软件之间进行数据通信，也同时对它们进行微小的包装和改动。ROS可以不断的从社区维护中进行升级，包括从其他的软件库、应用补丁中升级ROS的源代码。 （4）工具包丰富 为了管理复杂的ROS软件框架，我们利用了大量的小工具去编译和运行多种多样的ROS组建，从而设计成了内核，而不是构建一个庞大的开发和运行环境。 这些工具担任了各种各样的任务，例如，组织源代码的结构，获取和设置配置参数，形象化端对端的拓扑连接，测量频带使用宽度，生动的描绘信息数据，自动生成文档等等。尽管我们已经测试通过像全局时钟和控制器模块的记录器的核心服务，但是我们还是希望能把所有的代码模块化。我们相信在效率上的损失远远是稳定性和管理的复杂性上无法弥补的。 （5）免费并且开源 ROS所有的源代码都是公开发布的。我们相信这将必定促进ROS软件各层次的调试，不断的改正错误。虽然像Microsoft Robotics Studio和Webots这样的非开源软件也有很多值得赞美的属性，但是我们认为一个开源的平台也是无可为替代的。当硬件和各层次的软件同时设计和调试的时候这一点是尤其真实的。 ROS以分布式的关系遵循这BSD许可，也就是说允许各种商业和非商业的工程进行开发。ROS通过内部处理的通讯系统进行数据的传递，不要求各模块在同样的可执行功能上连接在一起。如此，利用ROS构建的系统可以很好的使用他们丰富的组件：个别的模块可以包含被各种协议保护的软件，这些协议从GPL到BSD，但是许可的一些“污染物”将在模块的分解上就完全消灭掉。 参考资料： （1）《开源机器人操作系统——ROS》 张建伟等著 （2）《an open-source Robot Operating System》 paper （3） willowgarage公司网站：http://www.willowgarage.com/ （4） ROS官方wiki：http://www.ros.org
ROS总体框架 一、 总体结构 根据ROS系统代码的维护者和分布来标示，主要有两大部分： （1）main：核心部分，主要由Willow Garage公司和一些开发者设计、提供以及维护。它提供了一些分布式计算的基本工具，以及整个ROS的核心部分的程序编写。 （2）universe：全球范围的代码，有不同国家的ROS社区组织开发和维护。一种是库的代码，如OpenCV、PCL等；库的上一层是从功能角度提供的代码，如人脸识别，他们调用下层的库；最上层的代码是应用级的代码，让机器人完成某一确定的功能。 一般是从另一个角度对ROS分级的，主要分为三个级别：计算图级、文件系统级、社区级。 二、 计算图级 计算图是ROS处理数据的一种点对点的网络形式。程序运行时，所有进程以及他们所进行的数据处理，将会通过一种点对点的网络形式表现出来。这一级主要包括几个重要概念：节点（node）、消息（message）、主题（topic）、服务（service）。 （1） 节点 节点就是一些直行运算任务的进程。ROS利用规模可增长的方式是代码模块化：一个系统就是典型的由很多节点组成的。在这里，节点也可以被称之为“软件模块”。我们使用“节点”使得基于ROS的系统在运行的时候更加形象化：当许多节点同时运行时，可以很方便的将端对端的通讯绘制成一个图表，在这个图表中，进程就是图中的节点，而端对端的连接关系就是其中弧线连接。 （2） 消息 节点之间是通过传送消息进行通讯的。每一个消息都是一个严格的数据结构。原来标准的数据类型（整型，浮点型，布尔型等等）都是支持的，同时也支持原始数组类型。消息可以包含任意的嵌套结构和数组（很类似于C语言的结构structs）。 （3） 主题 消息以一种发布/订阅的方式传递。一个节点可以在一个给定的主题中发布消息。一个节点针对某个主题关注与订阅特定类型的数据。可能同时有多个节点发布或者订阅同一个主题的消息。总体上，发布者和订阅者不了解彼此的存在。 （4） 服务 虽然基于话题的发布/订阅模型是很灵活的通讯模式，但是它广播式的路径规划对于可以简化节点设计的同步传输模式并不适合。在ROS中，我们称之为一个服务，用一个字符串和一对严格规范的消息定义：一个用于请求，一个用于回应。这类似于web服务器，web服务器是由URIs定义的，同时带有完整定义类型的请求和回复文档。需要注意的是，不像话题，只有一个节点可以以任意独有的名字广播一个服务：只有一个服务可以称之为“分类象征”，比如说，任意一个给出的URI地址只能有一个web服务器。 在上面概念的基础上，需要有一个控制器可以使所有节点有条不紊的执行，这就是一个ROS的控制器（ROS Master）。 ROS Master 通过RPC（Remote Procedure Call Protocol，远程过程调用）提供了登记列表和对其他计算图表的查找。没有控制器，节点将无法找到其他节点，交换消息或调用服务。 比如控制节点订阅和发布消息的模型如下： ROS的控制器给ROS的节点存储了主题和服务的注册信息。节点与控制器通信从而报告它们的注册信息。当这些节点与控制器通信的时候，它们可以接收关于其他以注册及节点的信息并且建立与其它以注册节点之间的联系。当这些注册信息改变时控制器也会回馈这些节点，同时允许节点动态创建与新节点之间的连接。 节点与节点之间的连接是直接的，控制器仅仅提供了查询信息，就像一个DNS服务器。节点订阅一个主题将会要求建立一个与出版该主题的节点的连接，并且将会在同意连接协议的基础上建立该连接。 另：ROS控制器控制服务： 三、 文件系统级 ROS文件系统级指的是在硬盘上面查看的ROS源代码的组织形式。 ROS中有无数的节点、消息、服务、工具和库文件，需要有效的结构去管理这些代码。在ROS的文件系统级，有以下几个重要概念：包（package）、堆（stack）、 （1） 包 ROS的软件以包的方式组织起来。包包含节点、ROS依赖库、数据套、配置文件、第三方软件、或者任何其他逻辑构成。包 （2） 堆 堆是包的集合，它提供一个完整的功能，像“navigation stack”。Stack与版本号关联，同时也是如何发行ROS软件方式的关键。 ROS是一种分布式处理框架。这使可执行文件能被单独设计，并且在运行时松散耦合。这些过程可以封装到包（Packages）和堆（Stacks）中，以便于共享和分发。下图是在包和堆在文件中的具体结构： Manifests (manifest.xml)：提供关于Package元数据，包括它的许可信息和Package之间依赖关系，以及语言特性信息像编译旗帜（编译优化参数）。 Stack manifests (stack.xml)：提供关于Stack元数据，包括它的许可信息和Stack之间依赖关系。 四、 社区级 ROS的社区级概念是ROS网络上进行代码发布的一种表现形式。结构如下图所示： 代码库的联合系统。使得协作亦能被分发。这种从文件系统级别到社区一级的设计让独立地发展和实施工作成为可能。正是因为这种分布式的结构，似的ROS迅速发展，软件仓库中包的数量指数级增加。 参考资料： （1）《开源机器人操作系统——ROS》 张建伟等著 （2）《an open-source Robot Operating System》 paper （3） willowgarage公司网站：http://www.willowgarage.com/ （4） ROS官方wiki：http://www.ros.org
ROS新手教程 前面我们介绍了ROS的特点和结构，接下来就要开始准备动手感受一下ROS的强大了。ROS官网的wiki上针对新手的教程很详细，最好把所有的新手教程都搞清楚，这是后面开发最基础的东西。尽管如此，ROS对于新手来说还是很难上手，这里，我就来总结一下我当时学习的历程，也为其他新手作为一个参考。 一、ROS的安装 ROS的安装当然是我们开始动手的第一步了，这里我们使用的操作系统是ubuntu，因为ROS在ubuntu上的支持是最好的。 如果是新手，我建议使用”apt-get“的方法进行安装，不走很简单，按照wiki上说的，大概半个小时就可以安装完毕完全版的ROS：（现在最新版的ROS是groovy，但是我还是习惯使用fuerte） http://ros.org/wiki/fuerte/Installation/Ubuntu 如果想挑战源码编译，当然也没有问题： http://ros.org/wiki/fuerte/Installation/Ubuntu/Source 安装完毕之后运行一下“roscore”，如果没有问题，安装就成功了！ 注：groovy版本的安装： “apt-get”：http://www.ros.org/wiki/groovy/Installation/Ubuntu 源码编译：http://www.ros.org/wiki/groovy/Installation/Source 转载注： 安装部分的介绍已经比较旧, 参考稳定版本indigo http://wiki.ros.org/indigo/Installation 二、ROS的新手教程 wiki上的新手教程还是很详细的，对代码都有解释，新手一定要把这些例子和代码搞明白： http://www.ros.org/wiki/ROS/Tutorials 上面的教程都是英文的，如果感觉略有压力（本人就是），可以参考下面这两个博客中的部分翻译： http://blog.sina.com.cn/s/articlelist_1712413141_0_1.html 不过往后面的学习都是英文的资料了，还是要努力适应看英文的文档。 ROS使用的编程语言主要是C++和python，所以也有针对这两种语言的功能包roscpp和rospy，这两个包的教程与上面的教程基本相似，看完上面的教程也可以看看这两个包的教程： http://www.ros.org/wiki/rospy_tutorials http://www.ros.org/wiki/roscpp/Tutorials 努力学习完上面的这些教程，你至少应该明白ROS里面的节点和消息是干什么用的了吧，如果还没理解，那就再多看几遍吧！ 三、ROS中的常用功能 ROS中提供了很多强大的功能，我们学习完上面的基本知识之后要继续进行深入。 1、rviz rviz是ROS中一款强大的3D可视化工具，这个玩意在后面可是要频繁用到的，是必须要弄明白的，详细的教程可以参考wiki： http://www.ros.org/wiki/rviz 我们可以在里面创建自己的机器人，并且让机器人动起来。还可以创建地图，显示3D点云等等，总之，想在ROS中显示的东东都可以在这里显示出来。当然这些显示都是通过消息的订阅来完成的，机器人通过ROS发布数据，rviz订阅消息接收数据，然后显示，这些数据也是有一定的数据格式的，可以参考下面的链接： http://www.ros.org/wiki/rviz/DisplayTypes 看到上面的机器人了吧，是不是很酷，在rviz中，这样的机器人模型是通过urdf文件描述的，具体urdf文件怎么写，参考wiki： http://www.ros.org/wiki/urdf 2、tf tf是ROS中的坐标变换系统，在机器人的建模仿真中经常用到。 ROS中主要有两种坐标系： （1）固定坐标系：用于表示世界的参考坐标系; （2）目标坐标系：相对于摄像机视角的参考坐标系。 教程见：http://www.ros.org/wiki/tf 3、gazebo 这个工具是ROS中的物理仿真环境，gazebo本身就是一款机器人的仿真软件，基于ODE的物理引擎，可以模拟机器人以及环境中的很多物理特性，这个软件可以稍作了解，并不是后面开发所必须要的。 教程见：http://www.ros.org/wiki/simulator_gazebo/Tutorials 四、ROS常用机器人 1、PR2 看ROS的应用时，最常见到的机器人就是PR2。这个机器人是ROS的主要维护者（Willow Garage）针对ROS量身定做的机器人，有两个运行ubuntu和ROS的电脑，和两个机器臂以及很多牛逼的传感器，功能是非常强大的，但是售价那是相当的昂贵，国内很少见到，基本都是在国外的研究所里。这款机器人的ROS包比较多，从仿真到导航，所以代码具有比较高的参考价值，当然新手还是先看看其他机器人的代码再来挑战PR2吧，代码比较庞杂。 wiki：http://www.ros.org/wiki/Robots/PR2 2、TurtleBot 这个机器人应该算是应用ROS小型移动机器人的典型代表了，资料、文档和代码比较多，主要在建立模型和导航定位方面，代码比较 wiki：http://www.ros.org/wiki/Robots/TurtleBot 3、Husky、Erratic 这两款机器人和TurtleBot机器人差不多，都是小型的轮式移动机器人，同样可以作为新手学习的参考： http://www.ros.org/wiki/Robots/Husky http://www.ros.org/wiki/Robots/Erratic 这些教程和机器人基本都是我当时学习的时候研究和了解过的，总结在此也让那些比较迷茫的初学者作为参考，可以尽快了解和掌握ROS。再次强调一下，wiki上的新手教程是一定要先熟悉的，后面的内容在后续学习过程中可以一边做一边学习。
简单的机器人仿真 修改版：2016-10-13 by ncnynl 前边我们已经介绍了ROS的基本情况，以及新手入门ROS的初级教程，现在就要真正的使用ROS进入机器人世界了。接下来我们涉及到的很多例程都是《ROS by Example》这本书的内容，我是和群里的几个人一起从国外的亚马逊上买到的，还是很有参考价值的，不过前提是你已经熟悉之前的新手教程了。 《ROS by Example》目前是第二版《ROS by Example v1 indigo》 可点击加入Q群:477659623，进入群文件下载。 本章部分内容来自《ROS by Example v1 indigo》，请按书中的说明安装例子。 一、ROS by Example 这本书是关于国外关于ROS出版的第一本书，主要针对Electric和Fuerte版本，使用机器人主要是TurtleBot。书中详细讲解了关于机器人的基本仿真、导航、路径规划、图像处理、语音识别等等，而且在google的svn上发布了所有代码，可以通过以下命令下载、编译： svn checkout http://ros-by-example.googlecode.com/svn/trunk/rbx_vol_1 rosmake rbx_vol_1 rospack profile //加入ROS package路径 注意：原来的内容已经无效，更改为针对indigo版本： 检查环境并安装依赖 $ cd ~ $ wget https://raw.githubusercontent.com/pirobot/rbx1/indigo-devel/rbx1-prereq.sh $ sh rbx1-prereq.sh 安装书中例子 $ cd ~/catkin_ws/src/ $ git clone https://github.com/pirobot/rbx1.git $ cd rbx1 $ git checkout indigo-devel $ cd ~/catkin_ws $ catkin_make $ source ~/catkin_ws/devel/setup.bash $ rospack profile 二、rviz简单机器人模拟 1、安装机器人模拟器 rviz是一个显示机器人实体的工具，本身不具有模拟的功能，需要安装一个模拟器arbotix。 svn checkout http://vanadium-ros-pkg.googlecode.com/svn/trunk/arbotix rosmake arbotix 注意：原来内容已经无效，更改为indigo版本： $sudo apt-get install ros-indigo-arbotix-* $rospack profile 2、TurtleBot机器人的模拟 在书中的rbx_vol_1包里已经为我们写好了模拟的代码，我们先进行实验，完成后再仔细研究代码。 机器人模拟运行： 在新终端打开： $ roscore 新终端打开： $ roslaunch rbx1_bringup fake_pi_robot.launch 然后在终端中可以看到，机器人已经开始运行了，打开rviz界面，才能看到机器人实体。 rosrun rviz rviz -d `rospack find rbx1_nav`/sim.rviz 注意：适合indigo版本原来为sim_fuerte.vcg，更改为sim.rviz 后面的参数是加载了rviz的配置文件sim.rviz。效果如下： 此时的机器人是静止的，需要发布一个消息才能让它动起来。 rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.2, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.5}}' 如果要让机器人停下来，需要在中断中按下“Ctrl+c”，然后输入： rostopic pub -1 /cmd_vel geometry_msgs/Twist '{}' 也可以改变发送的topic信息，使机器人走出不同的轨迹。 三、实现分析 按照上面的仿真过程，我们详细分析每一步的代码实现。 1、TurtleBot机器人运行 机器人运行使用的是launch文件，首先打开fake_turtlebot.launch文件。 注意：请比较书中的indigo版本，此launch已经旧了 <launch> <arg name="urdf_file" default="$(find xacro)/xacro.py '$(find turtlebot_description)/urdf/turtlebot.urdf.xacro'" /> <node name="arbotix" pkg="arbotix_python" type="driver.py" output="screen"> <rosparam file="$(find rbx1_bringup)/config/fake_turtlebot_arbotix.yaml" command="load" /> </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"> </node> <node pkg="tf" type="static_transform_publisher" name="odom_left_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /left_wheel_link 100" /> <node pkg="tf" type="static_transform_publisher" name="odom_right_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /right_wheel_link 100" /> </launch> 上面版本已经旧新的indigo版本为： <launch> <arg name="urdf_file" default="$(find xacro)/xacro.py '$(find rbx1_description)/urdf/turtlebot.urdf.xacro'" /> <node name="arbotix" pkg="arbotix_python" type="arbotix_driver" output="screen" clear_params="true"> <rosparam file="$(find rbx1_bringup)/config/fake_turtlebot_arbotix.yaml" command="load" /> </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"> </node> </launch> 文件可以大概分为四个部分： （1） 从指定的包中加载urdf文件 （2） 启动arbotix模拟器 （3） 启动状态发布节点 （4） tf坐标系配置 2、rviz配置文件 在打开rviz的时候需要加载一个.vcg的配置文件，主要对rviz中的插件选项进行默认的配置。这里打开的是sim_fuerte.vcg文件，由于文件比较长，这里只列举重点的部分。 Background\ ColorB=0.12549 Background\ ColorG=0.12549 Background\ ColorR=0.12549 Camera\ Config=158.108 0.814789 0.619682 -1.57034 Camera\ Type=rviz::FixedOrientationOrthoViewController Fixed\ Frame=/odom Grid.Alpha=0.5 Grid.Cell\ Size=0.5 Grid.ColorB=0.941176 Grid.ColorG=0.941176 Grid.ColorR=0.941176 Grid.Enabled=1 Grid.Line\ Style=0 Grid.Line\ Width=0.03 Grid.Normal\ Cell\ Count=0 Grid.OffsetX=0 Grid.OffsetY=0 Grid.OffsetZ=0 Grid.Plane=0 注意：sim_fuerte.vcg为旧版本，新版本为sim.rviz, 打开路径为：rbx1_nav/sim.rviz 上面的代码是配置背景颜色和网格属性的，对应rviz中的选项如下图所示。 其中比较重要的一个选项是Camera的type，这个选项是控制开发者的观察角度的，书中用的是FixedOrientationOrthoViewController的方式，就是上面图中的俯视角度，无法看到机器人的三维全景，所以可以改为OrbitViewController方式，如下图所示： 3、发布topic 要让机器人动起来，还需要给他一些运动需要的信息，这些信息都是通过topic的方式发布的。 这里的topic就是速度命令，针对这个topic，我们需要发布速度的信息，在ROS中已经为我们写好了一些可用的数据结构，这里用的是Twist信息的数据结构。在终端中可以看到Twist的结构如下： 用下面的命令进行消息的发布，其中主要包括力的大小和方向。 [plain] view plain copy Background\ ColorB=0.12549 Background\ ColorG=0.12549 Background\ ColorR=0.12549 Camera\ Config=158.108 0.814789 0.619682 -1.57034 Camera\ Type=rviz::FixedOrientationOrthoViewController Fixed\ Frame=/odom Grid.Alpha=0.5 Grid.Cell\ Size=0.5 Grid.ColorB=0.941176 Grid.ColorG=0.941176 Grid.ColorR=0.941176 Grid.Enabled=1 Grid.Line\ Style=0 Grid.Line\ Width=0.03 Grid.Normal\ Cell\ Count=0 Grid.OffsetX=0 Grid.OffsetY=0 Grid.OffsetZ=0 Grid.Plane=0 4、节点关系图
创建简单的机器人模型smartcar 修改版：2016-10-13 by ncnynl 前面我们使用的是已有的机器人模型进行仿真，这一节我们将建立一个简单的智能车机器人smartcar，为后面建立复杂机器人打下基础。 urdf相关工具安装（补充） 安装urdf_tutorial，可以使用其中的例子 $ sudo apt-get install ros-indigo-urdf-tutorial 安装liburdfdom-tools，提供对urdf文件的检查 $ sudo apt-get install liburdfdom-tools 一、创建硬件描述包 $ roscreate-pkg smartcar_description 注意：上面方法旧了，indigo版本使用catkin_create_pkg创建包： $ catkin_create_pkg smartcar_description std_msgs rospy roscpp urdf 二、智能车尺寸数据 因为建立的是一个非常简单的机器人，所以我们尽量使用简单的元素：使用长方体代替车模，使用圆柱代替车轮，具体尺寸如下： 三、建立urdf文件 在smartcar_description文件夹下建立urdf文件夹 $mkdir -p urdf ，创建智能车的描述文件smartcar.urdf，描述代码如下： <?xml version="1.0"?> <robot name="smartcar"> <visual> <geometry> <box size="0.25 .16 .05"/> </geometry> <origin rpy="0 0 1.57075" xyz="0 0 0"/> <material name="blue"> <color rgba="0 0 .8 1"/> </material> </visual> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <joint name="right_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_front_wheel"/> <origin rpy="0 1.57075 0" xyz="0.08 0.1 -0.03"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <joint name="right_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_back_wheel"/> <origin rpy="0 1.57075 0" xyz="0.08 -0.1 -0.03"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <joint name="left_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_front_wheel"/> <origin rpy="0 1.57075 0" xyz="-0.08 0.1 -0.03"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <joint name="left_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_back_wheel"/> <origin rpy="0 1.57075 0" xyz="-0.08 -0.1 -0.03"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <visual> <geometry> <box size=".02 .03 .03"/> </geometry> <material name="white"> <color rgba="1 1 1 1"/> </material> </visual> <joint name="tobox" type="fixed"> <parent link="base_link"/> <child link="head"/> <origin xyz="0 0.08 0.025"/> </joint> </robot> 四、建立launch命令文件 在smartcar_description文件夹下建立launch文件夹mkdir -p launch，创建智能车的描述文件 base.urdf.rviz.launch，描述代码如下： <launch> <arg name="model" /> <arg name="gui" default="False" /> <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" > </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" /> <node name="rviz" pkg="rviz" type="rviz" args="-d $(find urdf_tutorial)/urdf.vcg" /> </launch> 注意：上面的launch已经旧了，indigo版本launch如下： <launch> <arg name="model" /> <arg name="gui" default="False" /> <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" > </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" /> <node name="rviz" pkg="rviz" type="rviz" args="-d $(find urdf_tutorial)/urdf.rviz" /> </launch> 五、效果演示 在终端中输入显示命令： roslaunch smartcar_description base.urdf.rviz.launch gui:=true 显示效果如下图所示，使用gui中的控制bar可以控制四个轮子单独旋转。
使用smartcar进行仿真 之前的博客中，我们使用rviz进行了TurtleBot的仿真，而且使用urdf文件建立了自己的机器人smartcar，本篇博客是将两者进行结合，使用smartcar机器人在rviz中进行仿真。 urdf相关工具安装（补充） 安装urdf_tutorial，可以使用其中的例子 $ sudo apt-get install ros-indigo-urdf-tutorial 安装liburdfdom-tools，提供对urdf文件的检查 $ sudo apt-get install liburdfdom-tools 一、模型完善 之前我们使用的都是urdf文件格式的模型，在很多情况下，ROS对urdf文件的支持并不是很好，使用宏定义的.xacro文件兼容性更好，扩展性也更好。所以我们把之前的urdf文件重新整理编写成.xacro文件。 .xacro文件主要分为三部分： 1、机器人主体smartcar_body.urdf.xacro <?xml version="1.0"?> <robot name="smartcar" xmlns:xacro="http://ros.org/wiki/xacro"> <property name="M_PI" value="3.14159"/> <include filename="$(find smartcar_description)/urdf/gazebo.urdf.xacro"/> <property name="base_x" value="0.33" /> <property name="base_y" value="0.33" /> <xacro:macro name="smartcar_body"> <inertial> <origin xyz="0 0 0.055"/> <mass value="1.0" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <box size="0.25 .16 .05"/> </geometry> <origin rpy="0 0 0" xyz="0 0 0.055"/> <material name="blue"> <color rgba="0 0 .8 1"/> </material> </visual> <collision> <origin rpy="0 0 0" xyz="0 0 0.055"/> <geometry> <box size="0.25 .16 .05" /> </geometry> </collision> <inertial> <origin xyz="0.08 0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="0.08 0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="left_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_front_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="0.08 0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="0.08 -0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="0.08 -0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="right_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_front_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="0.08 -0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="-0.08 0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="-0.08 0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="left_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_back_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="-0.08 0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="-0.08 -0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="-0.08 -0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="right_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_back_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="-0.08 -0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="0.08 0 0.08"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <box size=".02 .03 .03"/> </geometry> <material name="white"> <color rgba="1 1 1 1"/> </material> </visual> <collision> <origin xyz="0.08 0 0.08"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="tobox" type="fixed"> <parent link="base_link"/> <child link="head"/> <origin xyz="0.08 0 0.08"/> </joint> </xacro:macro> </robot> 注意：原版本在indigo可能运行有问题，修改的indigo版本如下： <?xml version="1.0"?> <robot name="smartcar" xmlns:xacro="http://ros.org/wiki/xacro"> <xacro:property name="M_PI" value="3.14159"/> <xacro:include filename="$(find smartcar_description)/urdf/gazebo.urdf.xacro"/> <xacro:property name="base_x" value="0.33" /> <xacro:property name="base_y" value="0.33" /> <xacro:macro name="smartcar_body"> <inertial> <origin xyz="0 0 0.055"/> <mass value="1.0" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <box size="0.25 .16 .05"/> </geometry> <origin rpy="0 0 0" xyz="0 0 0.055"/> <material name="blue"> <color rgba="0 0 .8 1"/> </material> </visual> <collision> <origin rpy="0 0 0" xyz="0 0 0.055"/> <geometry> <box size="0.25 .16 .05" /> </geometry> </collision> <inertial> <origin xyz="0.08 0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="0.08 0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="left_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_front_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="0.08 0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="0.08 -0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="0.08 -0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="right_front_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_front_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="0.08 -0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="-0.08 0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="-0.08 0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="left_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="left_back_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="-0.08 0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="-0.08 -0.08 0.025"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> <material name="black"> <color rgba="0 0 0 1"/> </material> </visual> <collision> <origin rpy="0 1.57075 1.57075" xyz="-0.08 -0.08 0.025"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="right_back_wheel_joint" type="continuous"> <axis xyz="0 0 1"/> <parent link="base_link"/> <child link="right_back_wheel"/> <origin rpy="0 1.57075 1.57075" xyz="-0.08 -0.08 0.025"/> <limit effort="100" velocity="100"/> <joint_properties damping="0.0" friction="0.0"/> </joint> <inertial> <origin xyz="0.08 0 0.08"/> <mass value="0.1" /> <inertia ixx="1.0" ixy="0.0" ixz="0.0" iyy="1.0" iyz="0.0" izz="1.0"/> </inertial> <visual> <geometry> <box size=".02 .03 .03"/> </geometry> <material name="white"> <color rgba="1 1 1 1"/> </material> </visual> <collision> <origin xyz="0.08 0 0.08"/> <geometry> <cylinder length=".02" radius="0.025"/> </geometry> </collision> <joint name="tobox" type="fixed"> <parent link="base_link"/> <child link="head"/> <origin xyz="0.08 0 0.08"/> </joint> </xacro:macro> </robot> 修改property，为xacro:property 修改include， 为xacro:include 2、gazebo属性部分 gazebo.urdf.xacro <?xml version="1.0"?> <robot xmlns:controller="http://playerstage.sourceforge.net/gazebo/xmlschema/#controller" xmlns:interface="http://playerstage.sourceforge.net/gazebo/xmlschema/#interface" xmlns:sensor="http://playerstage.sourceforge.net/gazebo/xmlschema/#sensor" xmlns:xacro="http://ros.org/wiki/xacro" name="smartcar_gazebo"> <xacro:macro name="smartcar_sim"> <gazebo reference="base_link"> <material>Gazebo/Blue</material> </gazebo> <gazebo reference="right_front_wheel"> <material>Gazebo/FlatBlack</material> </gazebo> <gazebo reference="right_back_wheel"> <material>Gazebo/FlatBlack</material> </gazebo> <gazebo reference="left_front_wheel"> <material>Gazebo/FlatBlack</material> </gazebo> <gazebo reference="left_back_wheel"> <material>Gazebo/FlatBlack</material> </gazebo> <gazebo reference="head"> <material>Gazebo/White</material> </gazebo> </xacro:macro> </robot> 3、主文件 smartcar.urdf.xacro <?xml version="1.0"?> <robot name="smartcar" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:gazebo="http://playerstage.sourceforge.net/gazebo/xmlschema/#gz" xmlns:model="http://playerstage.sourceforge.net/gazebo/xmlschema/#model" xmlns:sensor="http://playerstage.sourceforge.net/gazebo/xmlschema/#sensor" xmlns:body="http://playerstage.sourceforge.net/gazebo/xmlschema/#body" xmlns:geom="http://playerstage.sourceforge.net/gazebo/xmlschema/#geom" xmlns:joint="http://playerstage.sourceforge.net/gazebo/xmlschema/#joint" xmlns:controller="http://playerstage.sourceforge.net/gazebo/xmlschema/#controller" xmlns:interface="http://playerstage.sourceforge.net/gazebo/xmlschema/#interface" xmlns:rendering="http://playerstage.sourceforge.net/gazebo/xmlschema/#rendering" xmlns:renderable="http://playerstage.sourceforge.net/gazebo/xmlschema/#renderable" xmlns:physics="http://playerstage.sourceforge.net/gazebo/xmlschema/#physics" xmlns:xacro="http://ros.org/wiki/xacro"> <include filename="$(find smartcar_description)/urdf/smartcar_body.urdf.xacro" /> <smartcar_body/> <smartcar_sim/> </robot> 注意：原来的在indigo下运行有问题，修改的indigo版本如下： <?xml version="1.0"?> <robot name="smartcar" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:gazebo="http://playerstage.sourceforge.net/gazebo/xmlschema/#gz" xmlns:model="http://playerstage.sourceforge.net/gazebo/xmlschema/#model" xmlns:sensor="http://playerstage.sourceforge.net/gazebo/xmlschema/#sensor" xmlns:body="http://playerstage.sourceforge.net/gazebo/xmlschema/#body" xmlns:geom="http://playerstage.sourceforge.net/gazebo/xmlschema/#geom" xmlns:joint="http://playerstage.sourceforge.net/gazebo/xmlschema/#joint" xmlns:controller="http://playerstage.sourceforge.net/gazebo/xmlschema/#controller" xmlns:interface="http://playerstage.sourceforge.net/gazebo/xmlschema/#interface" xmlns:rendering="http://playerstage.sourceforge.net/gazebo/xmlschema/#rendering" xmlns:renderable="http://playerstage.sourceforge.net/gazebo/xmlschema/#renderable" xmlns:physics="http://playerstage.sourceforge.net/gazebo/xmlschema/#physics" xmlns:xacro="http://ros.org/wiki/xacro"> <xacro:include filename="$(find smartcar_description)/urdf/smartcar_body.urdf.xacro" /> <smartcar_body/> <smartcar_sim/> </robot> 修改include， 为xacro:include 二、lanuch文件smartcar_display.rviz.launch 在launch文件中要启动节点和模拟器。 <launch> <arg name="urdf_file" default="$(find xacro)/xacro.py '$(find smartcar_description)/urdf/smartcar.urdf.xacro'" /> <arg name="gui" default="false" /> <node name="arbotix" pkg="arbotix_python" type="driver.py" output="screen"> <rosparam file="$(find smartcar_description)/config/smartcar_arbotix.yaml" command="load" /> </node> <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" > </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"> </node> <node pkg="tf" type="static_transform_publisher" name="odom_left_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /left_front_link 100" /> <node pkg="tf" type="static_transform_publisher" name="odom_right_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /right_front_link 100" /> <node name="rviz" pkg="rviz" type="rviz" args="-d $(find smartcar_description)/urdf.vcg" /> </launch> 注意：原来的版本在indigo运行有问题，修改的indigo版本如下： <launch> <arg name="urdf_file" default="$(find xacro)/xacro.py '$(find smartcar_description)/urdf/smartcar.urdf.xacro'" /> <arg name="gui" default="false" /> <node name="arbotix" pkg="arbotix_python" type="arbotix_driver" output="screen"> <rosparam file="$(find smartcar_description)/config/smartcar_arbotix.yaml" command="load" /> </node> <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher" > </node> <node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher"> </node> <node pkg="tf" type="static_transform_publisher" name="odom_left_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /left_front_link 100" /> <node pkg="tf" type="static_transform_publisher" name="odom_right_wheel_broadcaster" args="0 0 0 0 0 0 /base_link /right_front_link 100" /> <node name="rviz" pkg="rviz" type="rviz" args="-d $(find smartcar_description)/urdf.rviz" /> </launch> 修改driver.py为arbotix_driver 修改urdf.vcg 为urdf.rviz,确保在smartcar_description目录下有urdf.rviz。从urdf_tutorial目录提取 $ cd ~/catkin_ws/src/smartcar_description $ cp /opt/ros/indigo/share/urdf_tutorial/urdf.rviz . 在indigo版本里，新增smartcar_description/config/smartcar_arbotix.yaml文件，内容为： port: /dev/ttyUSB0 baud: 115200 rate: 20 sync_write: True sync_read: True read_rate: 20 write_rate: 20 controllers: { # Pololu motors: 1856 cpr = 0.3888105m travel = 4773 ticks per meter (empirical: 4100) base_controller: {type: diff_controller, base_frame_id: base_link, base_width: 0.26, ticks_meter: 4100, Kp: 12, Kd: 12, Ki: 0, Ko: 50, accel_limit: 1.0 } } 三、仿真测试 首先运行lanuch，既可以看到rviz中的机器人： roslaunch smartcar_description smartcar_display.rviz.launch 效果图： 发布一条动作的消息： rostopic pub -r 10 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.5, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.5}}' 效果图： 四、节点关系 运行rqt_graph： $ rqt_graph 效果图： 参考： http://wiki.ros.org/urdf/Tutorials http://wiki.ros.org/xacro http://blog.csdn.net/sunbibei/article/details/52297524 http://blog.csdn.net/sujun3304/article/details/18962719
smartcar源码上传 修改版：2016-10-13 by ncnynl 看到前面写的博客还是帮助了很多ROS的学习者，我感到非常荣幸。其实我也是一名ROS的新手，ROS的相关资料少，上手难度大，我现在也在摸索着学习，还希望大家都将自己的学习成果在网上或者ROS群里分享。 我看到有些人在运行我前面写的smartcar程序，为了方便大家的学习，我这两天整理了一下代码，已经上传到csdn上，下载请见： http://download.csdn.net/detail/hcx25909/5487985 或者 http://file.ncnynl.com/ros/smartcar.zip 下载 注意：修改的indigo版本： http://file.ncnynl.com/ros/smartcar_description_indigo_ncnynl.com.tar.gz 下载 注意事项 代码的内容主要是前边用到的机器人smartcar的urdf文件，已经运行使用的launch文件，大家下载后可以直接编译使用，由于代码在我的计算机上编译时带有路径信息，所以大家编译的时候请使用下面的命令先清除再编译： rosmake smartcar_description --pre-clean 注意：indigo版本用catkin编译： $ cd ~/catkin_ws/ $ catkin_make 代码中的urdf文件里室机器人的urdf描述，和上一篇博客中讲的是一样的，launch文件夹中主要有以下文件： 第一个实在gazebo仿真中用到的，需要先运行gazebo，然后再运行。但是貌似有问题，我每次加载到gazebo中的时候似乎物理参数不对，机器人在gazebo里飞来飞去的，至今还没有解决。如果哪位仁兄发现问题了，还请多多指教。 第二个文件是我在绘制模型的时候用到的，主要功能是在rviz中显示机器人模型，文件中的以下代码： <arg name="gui" default="false" /> 如果把false改为true，则会打开一个gui的调试界面，可以通过滑动条旋转车轮，如下图所示。默认是false。 第三个文件是在方针的时候用到的，就是前面博客中使用的，会打开arbotix仿真器。 根目录中还有rviz的配置文件urdf.vcg，只要大家在rviz中修改界面或者参数，推出的时候保存，就会自动修改这个文件了。 config文件夹下的是仿真用到的配置文件。 PS：代码中还有很多问题，请大家多多指教，共同进步。
操作杆控制 对于移动机器人，键盘的控制往往满足不了我们的需求，以前看好多电影里边都是用一个摇杆来控制机器人的，简直帅爆了，正好我这里有一个操作杆，那就来尝试感受一下。 操作杆（joystick）控制会更加有操作感，ROS中的很多机器人也带有操作杆的相关代码，只需要简单的移植即可。我们使用的是赛钛客（saitek）的一款操作杆，如下图所示： 使用的移植代码是clearpath_husky机器人中的python代码。 参考链接： http://www.ros.org/wiki/joy 一、测试操作杆驱动 首先将操作杆的接口插入电脑，然后在终端中输入： ls /dev/input/ 显示如下： 其中的js0就代表我们的操作杆。然后测试操作杆的操作是否有效，输入: sudo jstest /dev/input/js0 然后会在终端中显示操作杆的各个控制值的即时值，操作操作杆，如果每个按键和操作都有效，说明操作杆是正常的。最后在ROS中的节点里进行测试。打开joy节点： rosrun joy joy_node 再打开一个窗口，输入下面命令，查看数据: rostopic echo joy 操作操作杆，窗口下面的数据就开始刷新。 二、控制代码 从上面的测试中，我们发现在前后左右摇动操作杆时，相应改变的数据是Axes中0号和1号位的数据，也是我们最常用的数据，其他按键对应的位置也可以找到，编程的时候就是利用的这些数据位置，所以一定要找到每个按键的对应编号。 ROS中已经为我们建立了操作杆的数据结构： 我们主要用到的就是axes和buttons数据。最终的代码如下： import roslib; roslib.load_manifest('smartcar_teleop') import rospy from sensor_msgs.msg import Joy from geometry_msgs.msg import Twist from std_msgs.msg import String class Teleop: def __init__(self): rospy.init_node('smartcar_teleop_joy') self.turn_scale = rospy.get_param('~turn_scale') self.drive_scale = rospy.get_param('~drive_scale') self.deadman_button = rospy.get_param('~deadman_button', 0) self.cmd = None cmd_pub = rospy.Publisher('cmd_vel', Twist) announce_pub = rospy.Publisher('/smartcar/announce/teleops', String, latch=True) announce_pub.publish(rospy.get_namespace()); rospy.Subscriber("joy", Joy, self.callback) rate = rospy.Rate(rospy.get_param('~hz', 20)) while not rospy.is_shutdown(): rate.sleep() if self.cmd: cmd_pub.publish(self.cmd) def callback(self, data): """ Receive joystick data, formulate Twist message. """ cmd = Twist() cmd.linear.x = data.axes[1] * self.drive_scale cmd.angular.z = data.axes[0] * self.turn_scale if data.buttons[self.deadman_button] == 1: self.cmd = cmd else: self.cmd = None if __name__ == "__main__": Teleop() 三、机器人控制 首先来创建一个launch文件（teleop_joy.launch）: <launch> <arg name="drive_speed" default="1.0" /> <arg name="turn_speed" default="1.0" /> <arg name="joy_dev" default="/dev/input/js0" /> <arg name="cmd_topic" default="cmd_vel" /> <node pkg="joy" type="joy_node" name="joy_node"> </node> <node pkg="smartcar_teleop" type="teleop_joy.py" name="smartcar_teleop"> <remap from="cmd_vel" to="$(arg cmd_topic)" /> </node> </launch> 在rviz中打开我们的机器人模型，然后打开操作杆的控制节点： roslaunch smartcar_display.rviz.launch roslaunch smartcar_teleop teleop_joy.launch 然后按住刹车键进行操作，机器人就可以开始移动了： 在新终端中输入： rostopic echo joy 可以查看到实时的操作杆控制数据： 四、节点关系图
语音控制 如今语音识别在PC机和智能手机上炒的火热，ROS走在技术的最前沿当然也不会错过这么帅的技术。ROS中使用了CMU Sphinx和Festival开源项目中的代码，发布了独立的语音识别包，而且可以将识别出来的语音转换成文字，然后让机器人智能处理后说话。 一、语音识别包 1、安装 安装很简单，直接使用ubuntu命令即可，首先安装依赖库： $ sudo apt-get install gstreamer0.10-pocketsphinx $ sudo apt-get install ros-fuerte-audio-common $ sudo apt-get install libasound2 然后来安装ROS包： $ svn checkout http://albany-ros-pkg.googlecode.com/svn/trunk/rharmony $ rosmake pocketsphinx 其中的核心文件就是nodes文件夹下的recognizer.py文件了。这个文件通过麦克风收集语音信息，然后调用语音识别库进行识别生成文本信息，通过/recognizer/output消息发布，其他节点就可以订阅该消息然后进行相应的处理了。 2、测试 安装完成后我们就可以运行测试了。 首先，插入你的麦克风设备，然后在系统设置里测试麦克风是否有语音输入。 然后，运行包中的测试程序： $ roslaunch pocketsphinx robocup.launch 此时，在终端中会看到一大段的信息。尝试说一些简单的语句，当然，必须是英语，例如：bring me the glass，come with me，看看能不能识别出来。 《ros by example》这本书中写得测试还是很准确的，但是我在测试中感觉识别相当不准确，可能是我的英语太差了吧。 我们也可以直接看ROS最后发布的结果消息： $ rostopic echo /recognizer/output 二、语音库 1、查看语音库 这个语音识别时一种离线识别的方法，将一些常用的词汇放到一个文件中，作为识别的文本库，然后分段识别语音信号，最后在库中搜索对应的文本信息。如果想看语音识别库中有哪些文本信息，可以通过下面的指令进行查询： $ roscd pocketsphinx/demo $ more robocup.corpus 2、添加语音库 我们可以自己向语音库中添加其他的文本识别信息，《ros by example》自带的例程中是带有语音识别的例程的，而且有添加语音库的例子。 首先看看例子中要添加的文本信息： $ roscd rbx1_speech/config $ more nav_commands.txt 这就是需要添加的文本，我们也可以修改其中的某些文本，改成自己需要的。 然后我们要把这个文件在线生成语音信息和库文件，这一步需要登陆网站http://www.speech.cs.cmu.edu/tools/lmtool-new.html，根据网站的提示上传文件，然后在线编译生成库文件。 把下载的文件都解压放在rbx1_speech包的config文件夹下。我们可以给这些文件改个名字： $ roscd rbx1_speech/config $ rename -f 's/3026/nav_commands/' * 在rbx1_speech/launch文件夹下看看voice_nav_commands.launch这个文件： <launch> <node name="recognizer" pkg="pocketsphinx" type="recognizer.py" output="screen"> </node> </launch> 可以看到，这个launch文件在运行recognizer.py节点的时候使用了我们生成的语音识别库和文件参数，这样就可以实用我们自己的语音库来进行语音识别了。 通过之前的命令来测试一下效果如何吧： $ roslaunch rbx1_speech voice_nav_commands.launch $ rostopic echo /recognizer/output 三、语音控制 有了语音识别，我们就可以来做很多犀利的应用了，首先先来尝试一下用语音来控制机器人动作。 1、机器人控制节点 前面说到的recognizer.py会将最后识别的文本信息通过消息发布，那么我们来编写一个机器人控制节点接收这个消息，进行相应的控制即可。 在pocketsphinx包中本身有一个语音控制发布Twist消息的例程voice_cmd_vel.py，rbx1_speech包对其进行了一些简化修改，在nodes文件夹里可以查看voice_nav.py文件： #!/usr/bin/env python """ voice_nav.py Allows controlling a mobile base using simple speech commands. Based on the voice_cmd_vel.py script by Michael Ferguson in the pocketsphinx ROS package. See http://www.ros.org/wiki/pocketsphinx """ import roslib; roslib.load_manifest('rbx1_speech') import rospy from geometry_msgs.msg import Twist from std_msgs.msg import String from math import copysign class VoiceNav: def __init__(self): rospy.init_node('voice_nav') rospy.on_shutdown(self.cleanup) # Set a number of parameters affecting the robot's speed self.max_speed = rospy.get_param("~max_speed", 0.4) self.max_angular_speed = rospy.get_param("~max_angular_speed", 1.5) self.speed = rospy.get_param("~start_speed", 0.1) self.angular_speed = rospy.get_param("~start_angular_speed", 0.5) self.linear_increment = rospy.get_param("~linear_increment", 0.05) self.angular_increment = rospy.get_param("~angular_increment", 0.4) # We don't have to run the script very fast self.rate = rospy.get_param("~rate", 5) r = rospy.Rate(self.rate) # A flag to determine whether or not voice control is paused self.paused = False # Initialize the Twist message we will publish. self.cmd_vel = Twist() # Publish the Twist message to the cmd_vel topic self.cmd_vel_pub = rospy.Publisher('cmd_vel', Twist) # Subscribe to the /recognizer/output topic to receive voice commands. rospy.Subscriber('/recognizer/output', String, self.speech_callback) # A mapping from keywords or phrases to commands self.keywords_to_command = {'stop': ['stop', 'halt', 'abort', 'kill', 'panic', 'off', 'freeze', 'shut down', 'turn off', 'help', 'help me'], 'slower': ['slow down', 'slower'], 'faster': ['speed up', 'faster'], 'forward': ['forward', 'ahead', 'straight'], 'backward': ['back', 'backward', 'back up'], 'rotate left': ['rotate left'], 'rotate right': ['rotate right'], 'turn left': ['turn left'], 'turn right': ['turn right'], 'quarter': ['quarter speed'], 'half': ['half speed'], 'full': ['full speed'], 'pause': ['pause speech'], 'continue': ['continue speech']} rospy.loginfo("Ready to receive voice commands") # We have to keep publishing the cmd_vel message if we want the robot to keep moving. while not rospy.is_shutdown(): self.cmd_vel_pub.publish(self.cmd_vel) r.sleep() def get_command(self, data): # Attempt to match the recognized word or phrase to the # keywords_to_command dictionary and return the appropriate # command for (command, keywords) in self.keywords_to_command.iteritems(): for word in keywords: if data.find(word) > -1: return command def speech_callback(self, msg): # Get the motion command from the recognized phrase command = self.get_command(msg.data) # Log the command to the screen rospy.loginfo("Command: " + str(command)) # If the user has asked to pause/continue voice control, # set the flag accordingly if command == 'pause': self.paused = True elif command == 'continue': self.paused = False # If voice control is paused, simply return without # performing any action if self.paused: return # The list of if-then statements should be fairly # self-explanatory if command == 'forward': self.cmd_vel.linear.x = self.speed self.cmd_vel.angular.z = 0 elif command == 'rotate left': self.cmd_vel.linear.x = 0 self.cmd_vel.angular.z = self.angular_speed elif command == 'rotate right': self.cmd_vel.linear.x = 0 self.cmd_vel.angular.z = -self.angular_speed elif command == 'turn left': if self.cmd_vel.linear.x != 0: self.cmd_vel.angular.z += self.angular_increment else: self.cmd_vel.angular.z = self.angular_speed elif command == 'turn right': if self.cmd_vel.linear.x != 0: self.cmd_vel.angular.z -= self.angular_increment else: self.cmd_vel.angular.z = -self.angular_speed elif command == 'backward': self.cmd_vel.linear.x = -self.speed self.cmd_vel.angular.z = 0 elif command == 'stop': # Stop the robot! Publish a Twist message consisting of all zeros. self.cmd_vel = Twist() elif command == 'faster': self.speed += self.linear_increment self.angular_speed += self.angular_increment if self.cmd_vel.linear.x != 0: self.cmd_vel.linear.x += copysign(self.linear_increment, self.cmd_vel.linear.x) if self.cmd_vel.angular.z != 0: self.cmd_vel.angular.z += copysign(self.angular_increment, self.cmd_vel.angular.z) elif command == 'slower': self.speed -= self.linear_increment self.angular_speed -= self.angular_increment if self.cmd_vel.linear.x != 0: self.cmd_vel.linear.x -= copysign(self.linear_increment, self.cmd_vel.linear.x) if self.cmd_vel.angular.z != 0: self.cmd_vel.angular.z -= copysign(self.angular_increment, self.cmd_vel.angular.z) elif command in ['quarter', 'half', 'full']: if command == 'quarter': self.speed = copysign(self.max_speed / 4, self.speed) elif command == 'half': self.speed = copysign(self.max_speed / 2, self.speed) elif command == 'full': self.speed = copysign(self.max_speed, self.speed) if self.cmd_vel.linear.x != 0: self.cmd_vel.linear.x = copysign(self.speed, self.cmd_vel.linear.x) if self.cmd_vel.angular.z != 0: self.cmd_vel.angular.z = copysign(self.angular_speed, self.cmd_vel.angular.z) else: return self.cmd_vel.linear.x = min(self.max_speed, max(-self.max_speed, self.cmd_vel.linear.x)) self.cmd_vel.angular.z = min(self.max_angular_speed, max(-self.max_angular_speed, self.cmd_vel.angular.z)) def cleanup(self): # When shutting down be sure to stop the robot! twist = Twist() self.cmd_vel_pub.publish(twist) rospy.sleep(1) if __name__=="__main__": try: VoiceNav() rospy.spin() except rospy.ROSInterruptException: rospy.loginfo("Voice navigation terminated.") 可以看到，代码中定义了接收到各种命令时的控制策略。 2、仿真测试 和前面一样，我们在rviz中进行仿真测试。 首先是运行一个机器人模型： $ roslaunch rbx1_bringup fake_turtlebot.launch 然后打开rviz： $ rosrun rviz rviz -d `rospack find rbx1_nav`/sim_fuerte.vcg 如果不喜欢在终端里看输出，可以打开gui界面： $ rxconsole 再打开语音识别的节点： $ roslaunch rbx1_speech voice_nav_commands.launch 最后就是机器人的控制节点了： $ roslaunch rbx1_speech turtlebot_voice_nav.launch OK，打开上面这一堆的节点之后，就可以开始了。可以使用的命令如下： 下图是我的测试结果，不过感觉准确度还是欠佳： 四、播放语音 现在机器人已经可以按照我们说的话行动了，要是机器人可以和我们对话就更好了。ROS中已经集成了这样的包，下面就来尝试一下。 运行下面的命令： $ rosrun sound_play soundplay_node.py $ rosrun sound_play say.py "Greetings Humans. Take me to your leader." 有没有听见声音！ROS通过识别我们输入的文本，让机器人读了出来。发出这个声音的人叫做kal_diphone，如果不喜欢，我们也可以换一个人来读： $ sudo apt-get install festvox-don $ rosrun sound_play say.py "Welcome to the future" voice_don_diphone 哈哈，这回换了一个人吧，好吧，这不也是我们的重点。 在rbx1_speech/nodes文件夹中有一个让机器人说话的节点talkback.py: #!/usr/bin/env python """ talkback.py - Version 0.1 2012-01-10 Use the sound_play client to say back what is heard by the pocketsphinx recognizer. Created for the Pi Robot Project: http://www.pirobot.org Copyright (c) 2012 Patrick Goebel. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.5 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details at: http://www.gnu.org/licenses/gpl.htmlPoint """ import roslib; roslib.load_manifest('rbx1_speech') import rospy from std_msgs.msg import String from sound_play.libsoundplay import SoundClient import sys class TalkBack: def __init__(self, script_path): rospy.init_node('talkback') rospy.on_shutdown(self.cleanup) # Set the default TTS voice to use self.voice = rospy.get_param("~voice", "voice_don_diphone") # Set the wave file path if used self.wavepath = rospy.get_param("~wavepath", script_path + "/../sounds") # Create the sound client object self.soundhandle = SoundClient() # Wait a moment to let the client connect to the # sound_play server rospy.sleep(1) # Make sure any lingering sound_play processes are stopped. self.soundhandle.stopAll() # Announce that we are ready for input self.soundhandle.playWave(self.wavepath + "/R2D2a.wav") rospy.sleep(1) self.soundhandle.say("Ready", self.voice) rospy.loginfo("Say one of the navigation commands...") # Subscribe to the recognizer output and set the callback function rospy.Subscriber('/recognizer/output', String, self.talkback) def talkback(self, msg): # Print the recognized words on the screen rospy.loginfo(msg.data) # Speak the recognized words in the selected voice self.soundhandle.say(msg.data, self.voice) # Uncomment to play one of the built-in sounds #rospy.sleep(2) #self.soundhandle.play(5) # Uncomment to play a wave file #rospy.sleep(2) #self.soundhandle.playWave(self.wavepath + "/R2D2a.wav") def cleanup(self): self.soundhandle.stopAll() rospy.loginfo("Shutting down talkback node...") if __name__=="__main__": try: TalkBack(sys.path[0]) rospy.spin() except rospy.ROSInterruptException: rospy.loginfo("Talkback node terminated.") 我们来运行看一下效果： $ roslaunch rbx1_speech talkback.launch 然后再说话，ROS不仅将文本信息识别出来了，而且还读了出来，厉害吧。当然了，现在还没有加入什么人工智能的算法，不能让机器人和我们聪明的说话，不过这算是基础了，以后有时间再研究一下人工智能就更犀利了。
机器视觉 机器视觉在计算机时代已经越来越流行，摄像头价格越来越低廉，部分集成深度传感器的混合型传感器也逐渐在研究领域普及，例如微软推出的Kinect，而且与之配套的软件功能十分强大，为开发带来了极大的便利。ROS集成了Kinect的的驱动包OpenNI，而且使用OpenCV库可以进行多种多样的图像处理。 注：本章内容及代码均参考《ROS by Example》书中的第十章。 一、图像显示 我们从最基础的开始，想办法显示Kinect的图像数据。 1、安装驱动包 安装步骤很简单： $sudo apt-get install ros-fuerte-openni-kinect 2、测试 首先运行kinect节点： $roslaunch rbx1_vision openni_node_fuerte.launch 然后我们调用ROS的image_view包来直接显示摄像头的数据库。 image_view包的介绍可以参考：http://www.ros.org/wiki/image_view。 $rosrun image_view image_view image:=/camera/rgb/image_color 我们可以看到弹出了一个独立的图像显示框： 3、分析数据 下图是我们上面测试中的节点图。 我们可以使用如下的命令来查看节点之间发送的图像消息是什么样的： rostopic echo /camera/rgb/image_color 然后就会看到数据刷刷的在显示，有没有感觉看不清楚，我们使用终端的重定向功能将数据全部存储到文件中： rostopic echo /camera/rgb/image_color > test 好了，现在看看文件中是不是已经有数据了，我们来看一下其中的一帧数据： header: seq: 19285 stamp: secs: 1370867560 nsecs: 538447820 frame_id: camera_rgb_optical_frame height: 240 width: 320 encoding: rgb8 is_bigendian: 0 step: 960 data: [223, 225, 225, 220, 225, 225…………….. 从数据中我们可以的出来几个很重要的参数，首先是图像的分辨率：240320，编码的格式是rgb8，也就是说图像应该有240320=76800个像素，而每个像素由八位的R、G、B三个数据组成，因此我们可以预计下面的data中应该有76800*3=230400个八位的数据了。 我们可以验证一下data中到底有多少个数据，这个很简单了，数一下就行了，不过好像有点多，我使用的是linux的“wc”命令。首先我一帧数据复制到单独的文件中，每个数据都是用“，”号隔开的，只要计算“，”的数量就知道数据的数量了。 结果和我们预想的是一样的。知道这个数据格式以后，我们以后就可以直接把其他摄像头的数据装换成这种格式的数据就可以直接在ROS中使用了。 4、rviz显示图像 我们再来点高级的。rviz是我们经常使用的工具，把图像显示在rviz中才更有应用价值。rviz已经为我们提供好了显示图像的接口，使用非常简单。 首先按照上一节的方法运行kinect节点，然后打开rviz： rosrun rviz rviz 然后修改“Fixed Frame”为/camera_rgb_color，修改“Target Frame”为，接着点击add，选择camera类型。添加成功后选择camera菜单下的Iamge Topic选项，选择/camera/rgb/image_color，确定后下面的显示框内就显示图像了。 二、深度显示 使用kinect的一大特色就是可以获得传感器的深度数据，也就是物体距离传感器的距离，传说kinect的可识别范围在60cm到10m之间。 1、显示深度图像 首先也需要运行Kinect的节点： roslaunch openni_launch openni.launch 这一步我使用的是ROS安装openni包中的节点，使用与前面相同的命令运行的节点在后面无法产生深度数据。 然后同样适用iamge_view包就可以简单的显示深度图像了： rosrun image_view disparity_view image:=/camera/depth/disparity 2、rviz中显示深度 首先运行rviz： rosrun rviz rviz 然后修改“Fixed Frame”和“Target Frame”，在add中添加PointCloud2类型，修改“topic”，具体参数如下图所示：