机器人操作系统(ROS)浅析(肖军浩 博士 译) 学习笔记一(第一章到第五章)

机器人操作系统(ROS)浅析(肖军浩 博士 译) 学习笔记

第一章:
第 1 章 绪论
1.1 选择 ROS 的理由 
分布式计算 现代机器人系统往往需要多个计算机同时运行多个 进程,例如: 
 (1)一些机器人搭载多台计算机,每台计算机用于控制机器人的 部分驱动器或传感器; 
 (2)即使只有一台计算机,通常仍将程序划分为独立运行且相互 协作的小的模块来完成复杂的控制任务,这 也是常见的做法;
 (3)当多个机器人需要协同完成一个任务时,往往需要互相通信 来支撑任务的完成; 
 (4)用户通常通过台式机、笔记本或者移动设备发送指令控制机 器人,这种人机交互接口可以认为是机器人软件的一部分。 
软件复用:
    (1) ROS 标准包(Standard Packages)提供稳定、可调式的各类重要机器人算法实现。 
    (2)ROS通信接口
快速测试 :
    (1)精心设计的 ROS 系统框架将底层硬件控制模块和顶层数据处 理与决策模块分离;
    (2)ROS 另外提供了一种简单的方法可以在调试过程中记录传感 器数据及其他类型的消息数据,并在试验后按时间戳回放,这类记录的数据叫作包(bag), 一个被称为 rosbag 的工具可以用于记录和回放包数据。
总结:ROS的主要代码由C++语言 3 编写,本书后续章节也会介绍如何在ROS中使用C++进行编程。 ROS 不仅是一个函数库,除包含客户端库(Client Libraries) 外,还包含一个中心服务器(Central Server)、 一系列命令行 工具、图形化界面工具以及编译环境。
 
1.2 内容概述 
在groovy及之前的版本中,ROS采用rosbuild系 统来完成软件的编译,而在新的版本中,则改用catkin编译系统。 


第 2 章 入门概述
2.1 安装 ROS
(1)创建文件/etc/apt/sources.list.d/ros-latest.list
(2)ros-latest.li文件中添加信息:deb http://packages.ros.org/ros/ubuntu trusty main 
(3)wget https://raw.githubusercontent.com/ros/rosdistro/master/ros.key 
(4)sudo apt-key add ros.key 
(5)sudo apt-get update 
(6)sudo apt-get install ros-indigo-desktop-full 
(7)安装 turtlesim 功能包:sudo apt-get install ros-indigo-turtlesim 
(8)为系统设置 rosdep:sudo rosdep init 
2.2 配置账户 
(1)rosdep update 
(2)source /opt/ros/indigo/setup.bash 
   (3)编辑账户根目 录中的文件.bashrc,在最后一行输入:source /opt/ros/indigo/setup.bash

2.3 使用 TURTLESIM的小例子
    
2.4 功能包/软件包(PACKAGES)
    (1)在 ROS 中,所有软件都被组织为软件包的形式,称为 ROS软件包或功能包,有时也简称为包。ROS 软件包是一组用于实现特 定功能的相关文件的集合,包括可执行文件和其他支持文件。
    (2)使用下行命令,可以获取所有已安装的ROS软件包列表清单:rospack list
        每个程序包由一个清单文件(文件名为 package.xml)定义。 该文件定义关于包的一些细节,包括其名称、版本、维护者和依赖关系。
        使用新的 catkin 编译构建系统的功能包 ——编译产生的可执行文件并未存放在功能包目录下,而 是存放在一个单独的标准化目录层次结构中。
        对于使用 apt-get 安装的功能包,其所在根目录为/opt/ros/indigo。可 执行文件存储在这个根目录下的 lib 子目录里。
        要找到一个软件包的目录,使用 rospack find 命令: rospack find package-name  

        (turtlesim是一个软件包)一些命令: $ rosls turtlesim , $ rosls turtlesim /images ,
                                         $ roscd turtlesim /images/ , $ roscd turtlesim /images/ 
        图像查看命令: eog box−turtle . png

2.5 节点管理器(THE MASTER) ——》谈如何实际执行 ROS 软件?
    ROS 的一个基本目标是使机器人专家设计的很多称为节点 (node)的几乎相对独立的小程序能够同时运行。为此,这些节点必须能够彼此通信。ROS中实现通信的关键部分就是 ROS 节点管理器。要启动节点管理器,使用如下命令: roscore
    大多数 ROS 节点在启动时连接到节点管理器上,如果运行 中连接中断,则不会尝试重新连接。因此,如果 roscore 被终止,当前运行的其他节点将无法建立新的连接,即使稍后重启 roscore 也无济于事。 
     roslaunch 的工具,其目的是一次性启动多个节点。这是一个自适应工具,如果启动多节点时没有节 点管理器运行,它会自动启动节点管理器;如果已经有一个节点 管理器在运行,则会使用已有的。

2.6 节点(NODES)
要启动节点管理器,使用如下命令: roscore     。当结束时,可以通过在 roscore 终端键入 Ctrl-C 停止节点管理器。 
启动节点(也称运行ROS程序)的基本命令是rosrun:rosrun package-name executable-name 
如果 roscore 被终止,当前运行的其他节点将无法建立新的连接,即使 稍后重启 roscore 也无济于事。 

roscore 
rosrun turtlesim turtlesim_node ——》这个节点是 可执行文件turtlesim_node的实例化。
rosrun turtlesim turtle_teleop_key ——》是可执行文件 turtle_teleop_key 的实例化。
 启动节点:rosrun package-name executable-name 
查看节点列表:rosnode list ,输出如下:
                
这里有三个要点:
    (1)rosout 节点是一个特殊的节点,通过 roscore 自动启动。/rosout前面的反斜杠“/”表明该节点名称属于全局命名空间。
    (2)节点名并不一定与对应可执行文件名称相同。
    (3)查看节点 要获得特定节点的信息,使用如下命令: rosnode info node-name 
                        要终止节点,使用如下命令: rosnode kill node-name 
        终止和重启节点通常不会 对其他节点有较大影响;即使节点间正在相互交换消息(message),这些连接也会在节点终止时断开,在节点重启时重新连接。 
        还可以用 Ctrl-C 命令终止节点。但使用这种方法时可能不会在节点管理器中注销该节点,因此会导致已终止的节点 仍然在 rosnode 列表中。这虽然没有什么坏处,但可能会 让用户对当前系统的行为感到困扰。此时可以使用下面的 命令将节点从列表中删除: rosnode cleanup 

2.7 话题和消息 
turtlesim 例子中,遥控节点和仿真节点必须以某种方式进行对话。    
遥控节点:上下左右按键
仿真节点:移动的海归

ROS节点之间进行通信所利用的最重要的机制就是消息传递。在ROS中,消息有组织地存放在话题里。且消息是直接地从发布节点传递到订阅节点,中间并不经过节点管理器转交。
ROS节点管理器负责确 保发布节点和订阅节点能找到对方。


查看节点构成的计算图 :在ROS系统中查看节点之间的发布-订阅关系的最简单方式就是在终端输入如下命令: rqt_graph ,r 代表 ROS,qt 指的是用来实现这个可视化程序的 Qt 图形界面(GUI)工具包。

/teleop_turtle节点向话题/turtle1/cmd_vel发布消息(上下左右移动的按键), 而/turtlesim节点订阅了这些消息(“cmd_vel”是“command velocity”的缩写)

当按下一个键时,/teleop_turtle 节点会以消息的形式将这些运动控制命令发布到话题/turtle1/cmd_vel;与此同时,因为 turtlesim_node 订阅了该话题,因此它会接收到这个些消息,控制 海龟按照该预定的速度移动.
    节点:/teleop_turtle
    消息:运动控制命令
    话题:/turtle1/cmd_vel
    
 请注意: rqt_graph 本身就是一个节点。所有的节点发布都向话题/rosout 发布消息,该话题由同名的 /rosout 节点订阅。/rosout这个话题的作用是用来生成各个节点的文本日志消息。此处的名称/rosout 既指节点又指话题。但 ROS 并不会因这 种重复的名字而混淆,因为 ROS 会根据上下文来推测我们 讨论的是/rosout 节点还是/rosout 话题。 

为了获取当前活跃的话题,使用如下命令: rostopic list 
打印消息内容:rostopic echo topic-name 
有两个命令可以用来测量消息发布的频率以及这 些消息所占用的带宽: rostopic hz topic-name ; rostopic bw topic-name 
查看话题:rostopic info topic-name 
查看消息类型: rosmsg show message-type-name 
和 ROS 里其他的程序一样,每条消息类型都属于一个特定的包。消息类型名总会包含一个斜杠,斜杠前面的名字是包含它的包: package-name/type-name 
2.7.2 消息和消息类型 
消息里到底包含了什么信息?
——》话题的消息类型能告诉你该话题中每个消息携带了哪些信息,以及这些信息是如何组织的。
命令:rostopic list 
查看话题命令:rostopic info /turtle1/cmd_vel 
查看消息类型:rosmsg show geometry_msgs/Twist
理解消息类型的命名:消息类型名总会包含一个斜杠,斜杠前面的名字是包含它的包:package-name/type-name 
                            


输入命令:rostopic echo /turtle1/cmd_vel ,并按下按键时,有一下输出:

用命令行发布消息:rostopic pub –r rate-in-hz topic-name message-type message-content ,这条命令重复地按照指定的频率给指定的话题发布指定的消息。 
            例子:rostopic pub –r 3 /turtle1/cmd_vel geometry_msgs/Twist ’[0,0,0]’ ’[0,0,1]’ 
                              频率    话题              类型                 消息

2.8 一个更复杂的例子 
    于绝大多数设计精巧的 ROS 节点是松耦合的。每个节点都不需要显式知道其他节点的存在与否;它们的唯一交互方式是间接地发生在基于话题和消息的通信层。
    ROS 为更加直接的一对一通信提供了一种称为服务 (services)的机制。


    当ROS没有按你的预期运行时,一种可能有帮助的工具,也是本章要学习的最后一个命令行工具:roswtf

第 3 章 编写 ROS 程序 
3.1 创建工作区和功能包  
    (1)现在,终于是时候开始创建你自己的 ROS 程序了。本章将介绍如何建立一个开发工作区,并且 在此工作区编写三个短程序。
    (2)在我们写任何程序之前,第一步是创建一个容纳我们的功能包的工作区,然后再创建功能包本身。
    (3)创建工作区 :mkdir /home/jokane-/ros
        创建功能包 :创建一个新ROS功能包的命令应该在你工作区中的 src目录下运行:catkin_create_pkg  package-name ,执行完后会在包中自动生成两个文件:CMakeLists.txt, package.xml。
        第一个配置文件,叫做 package.xml,是我们在 2.4 节讨论过 的清单文件。
        第二个文件,叫做CMakeLists.txt,是一个Cmake的脚本文件。Cmake 是一个符合工业标准的跨平台编译系统。这个文件包 含了一系列的编译指令,包括应该生成哪种可执行文件,需要哪些源文件,以及在哪里可以找到所需的头文件和链接库。 当然,这个文件表明 catkin 在内部使用了 Cmake。
    注意:(1)ROS 包的命名遵循一个命名规范,只允许使用小写字母、 数字和下划线。
       (2)这个三层的目录结构(一个工作区目录,包含了一个src目录,src中又包含了一个功能包目录)对于简单项目和小工作区似乎有些大材小用,但是catkin编译系统需要它。 

3.2 你好,ROS! 
清单文件:package.xml,该文件定义关于包的一些细节,包括其名称、版本、维护者和依赖关系。
头文件 ros/ros.h 包含了标准 ROS 类的声明,将会在每一个 你写的 ROS 程序中包含它。 
创建功能包 :catkin_create_pkg agitr 
在agitr文件包中会生成两个配置文件:package.xml和CMakeLists.txt

编译 Hello 程序 ,一共有四个步骤 :
(1)声明依赖库:在CMakeLists.txt文件中
       <build_depend>roscpp</build_depend>
        <run_depend>roscpp</run_depend>  ——》 format="2"时已经不需要这条了。

(2)声明可执行文,在CMakeLists.txt文件中
        其一般形式是:
                find_package(catkin REQUIRED COMPONENTS roscpp)

                add_executable(hello hello.cpp) 
                target_link_libraries(hello ${catkin_LIBRARIES}) 
(3)编译工作区 
        在工作区目录运行:catkin_make

(4)Sourcing setup.bash 
执行 hello 程序 :
        A:在终端执行:roscore
        B:执行:source devel/setup.bash 
        C:执行:rosrun agitr hello
 
程序代码解析:
(1)ros::init函数初始化ROS客户端库。请在你程序的起始处调用一次该函数。函数最后的参数是一个包含节点默认名的字符串:ros::init( argc , argv , " hello_ros" ) ; 
(2)ros::NodeHandle(节点句柄)对象是你的程序用于和ROS系统交互的主要机制。创建此对象会将你的程序注册成一个节点。仅仅在第一个 NodeHandle 对象创建时才会在节点管理器注册新的节点。 同样,只有当所有的 NodeHandle 对象都销毁后。

3.3 发布者程序 
    源文件称为pubvel.cpp:发送随机生成的速度指令到一个turtlesim海龟,使它漫无目的地巡游。
一、发布消息 :
(1)包含消息类型声明 
    每一个 ROS 话题都与一个消息类型相关联。每一个消息类型都有一个相对应 C++头文件。你需要在你的程序中为每一个用到 的消息类型包含这个头文件,代码如下所示: 
    #include <package_name/type_name.h> 
当引用 C++代码中的消息类时,你将会使 用双分号(::)来区分开包名和类型名,双分号也称为范围解析运算符。
#include <geometry_msgs/Twist.h> ,头文件定义了一个名为 geometry_msgs::Twist 的类。

(2)创建发布者对象 
    发布消息的实际工作是由类名为ros::Publisher 的一个对象来完成的。
        ros::Publisher pub = node_handle.advertise<message_type>( topic_name, queue_size); 
    如果想从同一个节点发布关于多个话题的消息,你需要为每个话题创建一个独立的 ros::Publisher 对象。建议为每一个话题创建一个发布者,并且在你程序执行的全 过程中一直使用那个发布者。因为创建一个发布者是一个很耗时的操作。

(4)发布消息 :使用 ros::Publisher 对象的 publish 方法可以很简单地发布消息:
                    pub.publish(msg); 
    这个方法将所给的消息添加到发布者的输出消息队列中,从这里, 它会尽快被发送到相同话题的订阅者那里。

(5)定义输出格式  
二、消息发布循环 
 (1)节点是否停止工作的检查:pubvel 的 while 循环的条件是: ros::ok()  
        这个函数检查我们的程序作为 ROS 节点是否仍处于运行良好的状态。

 (2)控制消息发布频率     
        ros::Rate rate(2); 
        邻近每次循环迭代的结尾,我们调 用此对象的 sleep 方法:
                         rate.sleep();
        每次调用此方法时就会在程序中产生延迟。延迟的持续时间被用来阻止循环的迭代速率超过指定的速率。没有这种控制,程序会以计算机允许的最快速度发布消息,这样会占满发布和订阅的序列,并且浪费计算和网络资源。
 (3)编译 pubvel 
声明消息类型依赖库:  因为pubvel使用了来自geometry_msgs包 的消息类型,
            find_package(catkin REQUIRED COMPONENTS roscpp geometry_msgs)
    在 package.xml 文件中,我们添加新的依赖项: 
        <build_depend>geometry_msgs</build_depend> 
 (4)执行 pubvel :
        rosrun agitr pubvel

3.4 订阅者程序
订阅 turtlesim_node发布的/turtle1/pose 话题。这一话题的消息描述了海龟的位姿(位置和朝向)。
这里有三个新的知识点:
    (1)编写回调函数 
        回调函数类似于: 
            void function_name(const package_name::type_name &msg){ . . . } 
    其中参数 package_name 和 type_name 和发布消息时的相同,它们指明了我们想订阅的话题的消息类。

    (2)创建订阅者对象 
        创建一个 ros::Subscriber对象: 
            ros::Subscriber sub = node_handle.subscribe(topic_name,queue_size, pointer_to_callback_function); 
        e.g:ros::Subscriber sub = nh.subscribe ( " turtle1/pose " ,1000,&poseMessageReceived );
    可以通过如下两个方法减少订阅者队列溢出的可能性:
        (1)通过调用 ros::spin 或者 ros:spinOnce 确 保允许回调发生;
        (2)减少每个回调函数的计算时间。  
    (3)给ROS控制权 
        最后的复杂之处在于只有当明确给ROS许可时,它才会执行的回调函数。
        实际上有两个略微不同的方式来做到这一点:
            (1)ros::spinOnce(); ——》这个代码要求 ROS 去执行所有挂起的回调函数,然后将控制权限返回给我们。
            (2)ros::spin(); ——》这个方法要求 ROS 等待并且执行回调函数,直到这个节点关机。 
                ros::spin()大体等于这样一个循环: 
            while(ros::ok( ))          
            {
                 ros::spinOnce(); 
            }
    使用 ros::spinOnce()还是使用 ros::spin()的建议如下:你的程 序除了响应回调函数,还有其他重复性工作要做吗?如果答案是 “否”,那么使用 ros::spin();否则,合理的选择是写一个循环,做 其他需要做的事情,并且周期性地调用 ros::spinOnce()来处理回调。 
注意:忽略了调用 ros::spinOnce 或 ros::spin。在这种情况下,ROS 永远没有机 会去执行你的回调函数。
3.4.1 编译并运行 subpose 
    运行:rosrun turtlesim turtlesim_node    ——》 rosrun agitr pubvel (启动发布消息) ——》 rosrun sub subpose(启动订阅消息)

第 4 章 日志消息
4.1 严重级别 
    按照严重性程度递增,这些级别有: 
        DEBUG INFO WARN ERROR FATAL
4.2 示例程序
        ROS_DEBUG_STREAM
         ROS_INFO_STREAM
        ROS_WARN_STREAM
        ROS_ERROR_STREAM
         ROS_FATAL_STREAM
    默认的最小级别是INFO.
4.3 生成日志消息
        ROS_DEBUG_STREAM(message);
         ROS_INFO_STREAM(message); 
        ROS_WARN_STREAM(message); 
        ROS_ERROR_STREAM(message); 
        ROS_FATAL_STREAM(message); 
        ——》其中各个宏的参数message可以是C++中标准输出流(ostream)中的各种表达式。
    (1)生成一次性日志消息 :
        ROS 提供了可以仅仅生成一次日志消息的简单的宏。 
        ROS_DEBUG_STREAM_ONCE(message); 
        ROS_INFO_STREAM_ONCE (message); 
        ROS_WARN_STREAM_ONCE (message); 
        ROS_ERROR_STREAM_ONCE (message); 
        ROS_FATAL_STREAM_ONCE (message); 
    (2)生成频率受控的日志消息 :
        ROS_DEBUG_STREAM_THROTTLE(interval, message); 
        ROS_INFO_STREAM_THROTTLE(interval, message); 
        ROS_WARN_STREAM_THROTTLE(interval, message); 
        ROS_ERROR_STREAM_THROTTLE(interval, messge); 
        ROS_FATAL_STREAM_THROTTLE(interval, message); 
    ——》参数 interval 是 double类型的,它表示以秒为单位的时间量,这是相邻日志消息出现的最小时间间隔。
4.4 查看日志消息
(1)最明显的是,日志消息可以被送至控制台。
        DEBUG和INFO消息被打印至标准输出(standard output),而 WARN、 ERROR和FATAL消息将被送至标准错误(standard error)。
        格式化控制台消息 :[${severity}] [${time}]: ${message} 
(2)除了在控制台上显示,每一个日志消息都被发布到话题 /rosout 上。
        话题的消息类型是 rosgraph_msgs/Log。
        /rosout 话题的主要作用是它在一个流中包含了系统中所有节点的日志消息。
        由于/rosout 只是一个普通的话题,你当然可以通过 :
            rostopic echo /rosout  这条命令直接查看消息内容。
        查看/rosout消息最简单的方式是使用下面这条命令: 
                    rqt_console  ——》rqt_console 订阅的是/rosout_agg,而不是/rosout。后缀_agg 表示消息实际上是被 rosout 节点聚合到一起的。 /rosout 话题发布        的消息都通过 rosout 节点输出到 /rosout_agg 话题上。 
(3)日志消息的第三个,也是最后一个目的地,是由 rosout 节点生成的日志文件。
        由 rosout 节点生成的日志文件。作为/rosout 话题回调函数的一部分,该节点可以将日志消息作为一行写入到一个日志文件,文件名类似于: 
            ~/.ros/log/run_id/rosout.log 这里的 rosout.log 日志文件是纯文本文件。
    使用下面这条命令来查看当前账户中被ROS日志消 耗的硬盘空间: 
         rosclean check 
    如果日志正在消耗过多的硬盘空间,可以通过下面的命令删除所有已经存在的日志: 
         rosclean purge 

4.5 启用和禁用日志消息 
rqt_console 会接收任何输入的日志消息,其过滤选项只是选择性地显示其中的一部分。
设置日志的方法有两种:
(1)为了通过命令行设置一个节点的日志级别,可以使用与以下类似的命令: 
rosservice call  /node-name/set_logger_level   ros.package-name  level 
——》这条命令调用 set_logger_level 服务,该服务由各个节点自动提供。 
 node-name 是你期望设置日志级别的节点名称 
 package-name 正如你猜测的一样,是拥有这个节点的功能包 的名称 
 level 参数是 DEBUG、INFO、WARN、ERROR、FATAL 中的一个字符串,即为节点设置的日志级别。
(2)通过图形界面设置日志级别 :
        以尝试以下命令: rqt_logger_level 
  (3)通过 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()是有必要的,因为每个日志的启用或者禁用是缓存了的。

第 5 章 计算图源命名 
5.1 全局名称 
(1)节点、话题、服务和参数统称为计算图,而每个计算图源由一个叫计算图源名称(graph resource name)的短字符串标识。
一个全局名称的几个组成部分: e.g:/turtle1/cmd_vel 
 前斜杠“/”表明这个名称为全局名称。 
 由斜杠分开的一系列命名空间(namespace),每个斜杠代表 一级命名空间。e.g:turtle1
     描述资源本身的基本名称(base name)。e.g:cmd_vel

5.2 相对名称 
     相对名称的典型特征是它缺少全局名称带 有的前斜杠“/”。
     默认命名空间的名称 + 相对名称 = 全局名称,e.g:/turtle1  +  cmd_vel =》/turtle1/cmd_vel 
                   默认命名空间   相对名称      全局名称 
     为节点选择一个不同的默认命 名空间的最好也是最常用的方法是在启动文件中使用命名空间 (ns)属性。 ROS 还提供了一些其他机制支持这种操作:
    (1)大部分 ROS 程序接 受叫做__ns 的命令行参数,此参数将为程序指定一个默认命 名空间。 
            __ns:=default-namespace
     (2)还可以利用环境变量为在 shell 内执行的 ROS 程序设置默认命名空间。 
            Export ROS_NAMESPACE=default-namespace 

5.3 私有名称
     与相对名称的主要差别在于,私有名称不是用当前默认命名空间,而是用的它们节点名称作为命名空间。每个节点内部都有这样一些资源,这些资源只与本节点有关,而不会与其他节点打交道,这些资源 就可以使用私有名称。
其私有名称∼max_vel 转换至如下全局名称:
 /sim1/pubvel  + ~max_vel => /sim1/pubvel/max_vel    
             私有名称 全局名称 
5.4 匿名名称(ANONYMOUS NAMES)
    为了请求一个匿名名称,节点需要将 ros::init_options::Anonymous-Name 作为第四个参数传递给 ros::init 方法: 
ros::init(argc, argv, base_name, ros::init_options::AnonymousName); 
 


 

猜你喜欢

转载自blog.csdn.net/qq_29230261/article/details/81020357
今日推荐