美文网首页
ROS 2 基本命令总结

ROS 2 基本命令总结

作者: OurNote | 来源:发表于2019-08-04 16:08 被阅读0次

    引言

    截止到 2019年5月,ROS 2 已经正式发布了 4 个版本(以及更早期的几个 alpha, beta 测试版本)。ROS 2 版本的命名方式依然延续了 ROS 的规则(实际上也是 Ubuntu 版本的命名规则),按照字母顺序依次命令,而且都是 ×... ×... 格式的:

    • Ardent Apalone
    • Bouncy Bolson
    • Crystal Clemmys
    • Dashing Diademata

    我们在学习和做项目过程中,逐渐感觉到 ROS 2 趋于成熟,参考的很多项目已经改用 ROS 2 实现。就像 python 2 向 python 3 的转换,随着开发者贡献的 package 越来越多,新的平台越来越成熟,功能逐渐完善,最后彻底的转换就成了大势所趋。

    ROS 2 不是 ROS 的简单扩展,而是全新的架构,如下所示:

    architecture.png

    关于 ROS 2 新框架有如下描述 (来源):

    For ROS 2 the decision has been made to build it on top of an existing middleware solution (namely DDS). The major advantage of this approach is that ROS 2 can leverage an existing and well developed implementation of that standard.

    ROS 2 几乎所有的优点 (无 master节点、实时性好、安全性高等) 都得益于站在了 DDS (Data Distribution Service) 这个巨人的肩膀上。

    DDS 只是一个协议标准,它有多种具体的实现 (implementation),例如:

    在 ROS 2 的安装过程中可以设定使用哪个 DDS 实现。一般情况下,使用默认的 Fast-RTPS 即可。

    如果只是将 ROS 2 作为工具来用,在 ROS 2 client library 的基础上搭建自己的项目,可以暂时忽略掉底层的 DDS 等新机制,掌握 ROS 2 的常用命令应该就够了。回想一下,在之前使用 ROS 的时候,我们也并不需要了解底层的 TCPROS 和 UDPROS 通讯协议。

    相比于 ROS 整洁条理的 wiki,ROS 2 的文档稍显粗糙。

    本文的目的是尝试整理总结 ROS 2 的基本操作命令,方便以后使用时查阅。

    如果用 Debian package 方式安装,即 sudo apt install 方式, Ubuntu 16.04 只能安装 Ardent 版本,Ubuntu 18.04 可以安装 Bouncy,Crystal 和 Dashing。
    如果用源码编译的方式安装,Ubuntu 16.04 可以安装 Ardent,Bouncy 和 Crystal 。

    我们选择的平台:

    • Ubuntu 18.04
    • ROS 2 (Dashing 版本)

    这两个都是 LTS (Long Term Support) 版本,在未来较长的一段时间内应该是主流选择。

    ROS 2 安装

    ROS 2 官网上有详细的安装步骤。为了方便查阅,抄录到这里。

    1. 设置环境变量 LC_ALL 和 LANG,这两个变量描述了用户所在地区、使用的语言、日期、货币格式等。网站上并没有说为什么必须设置这两个变量。猜测可能是 ROS 2 中的某些显示结果 (例如报错信息、日期、货币格式等) 会受到这两个环境变量的影响。这里按照网站上的例子设置就可以了。
      sudo locale-gen en_US en_US.UTF-8
      
      sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
      
      export LANG=en_US.UTF-8
      
    2. 添加 ROS 2 的 repo 以及对应的 key
      sudo apt update
      
      sudo apt install curl gnupg2 lsb-release
      
      curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
      
      sudo sh -c 'echo "deb [arch=amd64,arm64] http://packages.ros.org/ros2/ubuntu `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-latest.list'
      
    3. 安装 ROS 2
      sudo apt update
      
      sudo apt install ros-dashing-desktop
      
    4. 添加命令自动补全功能,即在命令行中用 TAB 键可以自动补全 ROS 2 的命令。
      sudo apt install python3-argcomplete
      
    5. 添加环境变量。这样每次打开 Terminal 就可以自动加载 ROS 2 相关的环境变量,进而使用 ROS 2 相关的命令
      echo "source /opt/ros/dashing/setup.bash" >> ~/.bashrc
      
    6. 如果涉及到 ROS 节点与 ROS 2 节点通讯,还要安装 ros1_bridge
      sudo apt install ros-dashing-ros1-bridge
      

    colcon 编译工具

    colcon 的设计理念可以在这里查到。概括来说,colcon 的目标就是成为一个通用的编译工具,现在主要用来编译 ROS,ROS 2 以及 Gazebo,未来可能使用更广泛。所以尽管 colcon 最早的开发动力来自 ROS 2,但它的定位并不是 ROS 2 的附属品。 colcon 有非常详细的文档,可以在这里查阅。

    在 ROS 2 Ardent 版本中编译工具是 ament_tools,从 ROS 2 Bouncy 版本开始,colcon 就成了默认的编译工具。

    安装 colcon

    colcon 包含在了 ROS 2 的 repo 中,前边已经添加过了这个 repo,所以这里直接安装即可:

    sudo apt install python3-colcon-common-extensions
    

    由于 colcon 本质上独立于 ROS 2,我们并一定先添加 ROS 2 的 repo 再安装 colcon,也可以直接用 pip 方式安装,要求 python 3.5 及以上版本

    sudo pip3 install -U colcon-common-extensions
    

    创建工作空间

    跟 ROS 相同,ROS 2 也是建议创建一个工作空间 workspace,方便管理同一个项目的 packages,而且也是将 package 源文件都放在 src 文件夹中。这里我们用 ROS 2 tutorial 中的例子,创建工作空间 ros2_example_ws 并进入 src

    mkdir -p ~/ros2_example_ws/src   
    
    cd ~/ros2_example_ws/src
    

    现在我们先关注 colcon 的编译过程,所以 package 源文件就先借用官网的。

    git clone https://github.com/ros2/examples 
    
    git checkout $ROS_DISTRO     # 切换到与本机版本对应的 branch 上
    

    目前文件路径结构如下:

    $ tree -L 3
    .
    └── src
        └── examples
            ├── CONTRIBUTING.md
            ├── LICENSE
            ├── rclcpp
            ├── rclpy
            └── README.md
    
    4 directories, 3 files
    

    可以用 colcon list 命令列出 src 文件夹中的所有 packages,即所有包含 package.xmlsetup.py/CMakeLists.txt 的文件夹。

    $ colcon list 
    
    examples_rclcpp_minimal_action_client   /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_action_client (ros.ament_cmake)
    examples_rclcpp_minimal_action_server   /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_action_server (ros.ament_cmake)
    examples_rclcpp_minimal_client  /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_client    (ros.ament_cmake)
    examples_rclcpp_minimal_composition /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_composition   (ros.ament_cmake)
    examples_rclcpp_minimal_publisher   /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_publisher (ros.ament_cmake)
    examples_rclcpp_minimal_service /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_service   (ros.ament_cmake)
    examples_rclcpp_minimal_subscriber  /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_subscriber    (ros.ament_cmake)
    examples_rclcpp_minimal_timer   /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_timer (ros.ament_cmake)
    examples_rclpy_executors    /home/automan/ros2_example_ws/src/examples/rclpy/executors  (ros.ament_python)
    examples_rclpy_minimal_action_client    /home/automan/ros2_example_ws/src/examples/rclpy/actions/minimal_action_client  (ros.ament_python)
    examples_rclpy_minimal_action_server    /home/automan/ros2_example_ws/src/examples/rclpy/actions/minimal_action_server  (ros.ament_python)
    examples_rclpy_minimal_client   /home/automan/ros2_example_ws/src/examples/rclpy/services/minimal_client    (ros.ament_python)
    examples_rclpy_minimal_publisher    /home/automan/ros2_example_ws/src/examples/rclpy/topics/minimal_publisher   (ros.ament_python)
    examples_rclpy_minimal_service  /home/automan/ros2_example_ws/src/examples/rclpy/services/minimal_service   (ros.ament_python)
    examples_rclpy_minimal_subscriber   /home/automan/ros2_example_ws/src/examples/rclpy/topics/minimal_subscriber  (ros.ament_python)
    
    

    用 colcon 编译下载的 package

    colcon build --symlink-install
    

    上边命令中 --symlink-install 表示编译时如果 install 中文件已经存在于 src 或者 build 文件夹中,就用超链接指向该文件,避免浪费空间,也可以实现同步更新。
    例如,在 install 文件夹的 examples_rclcpp_minimal_publisher 中有如下超链接:

    .
    ├── lib
    │   └── examples_rclcpp_minimal_publisher
    │       ├── publisher_lambda -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/publisher_lambda
    │       ├── publisher_member_function -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/publisher_member_function
    │       └── publisher_not_composable -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/publisher_not_composable
    └── share
        ├── ament_index
        │   └── resource_index
        ├── colcon-core
        │   └── packages
        └── examples_rclcpp_minimal_publisher
            ├── cmake
            ├── environment
            ├── hook
            ├── local_setup.bash -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/ament_cmake_environment_hooks/local_setup.bash
            ├── local_setup.sh -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/ament_cmake_environment_hooks/local_setup.sh
            ├── local_setup.zsh -> /home/automan/ros2_example_ws/build/examples_rclcpp_minimal_publisher/ament_cmake_environment_hooks/local_setup.zsh
            ├── package.bash
            ├── package.ps1
            ├── package.sh
            ├── package.xml -> /home/automan/ros2_example_ws/src/examples/rclcpp/minimal_publisher/package.xml
            └── package.zsh
    

    如果去掉 --symlink-install 参数,仅用命令 colcon build 来编译,则上述超链接文件全都变成实体拷贝的文件,得到如下结果:

    .
    ├── lib
    │   └── examples_rclcpp_minimal_publisher
    │       ├── publisher_lambda
    │       ├── publisher_member_function
    │       └── publisher_not_composable
    └── share
        ├── ament_index
        │   └── resource_index
        ├── colcon-core
        │   └── packages
        └── examples_rclcpp_minimal_publisher
            ├── cmake
            ├── environment
            ├── hook
            ├── local_setup.bash
            ├── local_setup.sh
            ├── local_setup.zsh
            ├── package.bash
            ├── package.ps1
            ├── package.sh
            ├── package.xml
            └── package.zsh
    

    编译之后,得到的文档结构如下:

    .
    ├── build
    ├── install
    ├── log
    └── src
    

    即 colcon 编译产生了 build, install, log 三个新文件夹。

    编译之后还可以测试一下 packages

    $ colcon test-result --all
    
    build/examples_rclpy_executors/pytest.xml: 3 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_action_client/pytest.xml: 0 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_action_server/pytest.xml: 0 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_client/pytest.xml: 3 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_publisher/pytest.xml: 3 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_service/pytest.xml: 3 tests, 0 errors, 0 failures, 0 skipped
    build/examples_rclpy_minimal_subscriber/pytest.xml: 3 tests, 0 errors, 0 failures, 0 skipped
    
    Summary: 15 tests, 0 errors, 0 failures, 0 skipped
    

    如果要单独编译某一个 package,可以用如下命令:

    colcon build --packages-select  PACKAGE_NAME
    

    如果不希望编译某一个 package,可以在该 package中创建名为 COLCON_IGNORE 的空文件,colcon 就会忽略掉该 package,不但不编译,连 colcon list 都不显示,这个 package 对 colcon 就是透明的。

    完成编译之后,要 source 一下 setup.bash 文件,确保系统能够找到当前编译生成的可执行文件和库

    source install/setup.bash
    

    或者将 source 命令放入 .bashrc 文件,这样每次打开 terminal 就可以自动加载路径信息

    echo "source ~/ros2_example_ws/install/setup.bash" >> ~/.bashrc
    

    实例测试:
    首先启动一个 publisher

    ros2 run examples_rclcpp_minimal_publisher publisher_member_function
    

    再启动一个 subscriber

     ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function 
    

    如果一切顺利,应该会有如下的界面:

    sub_pub.png

    这里与 ROS 的最大区别是不需要启动 ROS master 节点,即不需要类似 roscore 的命令。ROS 2 是真正的分布式系统,不需要中心节点,这样系统的鲁棒性更强,不会因为中心节点失效而影响整个系统。

    terminal 命令

    分门别类

    ROS 2 根据命令的作用对象划分成多个类别,其中常用的几个类别:

    Commands:
      launch     Run a launch file
      node       Various node related sub-commands
      param      Various param related sub-commands
      pkg        Various package related sub-commands
      run        Run a package specific executable
      service    Various service related sub-commands
      srv        Various srv related sub-commands
      topic      Various topic related sub-commands
    

    在调用时都是采用如下命令格式:

    ros2 COMMAND ...
    

    我们可以对比 ROS 和 ROS 2 中的几个命令,应该很容易找到其中的规律

    • 运行 ROS node
      - ROS: rosrun <package> <executive>
      - ROS 2:ros2 run <package> <executive>
    • 查看当前运行的 node
      - ROS : rosnode list
      - ROS 2: ros2 node list

    修改 node 名字

    在前边的例子中,默认 node 名字分别为 /minimal_publisher/minimal_subscriber。我们可以在启动 node 时用 __node:=NEW_NAME 命令修改他们的名字,例如修改 /minimal_publisher/my_publisher :

    ros2 run examples_rclcpp_minimal_publisher publisher_member_function __node:=my_publisher
    

    还可以通过 __ns:=NEW_NAMESPACE 修改 node 的 namespace。有些 node 的名字形式为 /a/b/c/d,其中 d 称为 basename,前边的 /a/b/c 被称为 namespace。
    例如,我们给刚才的 node /my_publisher 加个 namespace /mynode,即最后 node 的名字为 /mynode/my_publisher:

     ros2 run examples_rclcpp_minimal_publisher publisher_member_function __ns:=/mynode __node:=my_publisher
    

    修改 topic, service 的名字

    如果要修改 node 文件中的某个 topic 或者 service 的名字,则用 OLD_NAME:=NEW_NAME 的形式,例如原本 /minimal_publisher 中的 topic 名字为 topic ,这里修改成 my_topic:

    ros2 run examples_rclcpp_minimal_publisher publisher_member_function topic:=my_topic
    

    编写自己的 node 文件 (基于 rclpy)

    在 ROS 2 的 wiki 和 github 中提供了一些例子,这里整理总结一下,方便以后查阅。
    参考文献:
    https://github.com/ros2/examples
    https://github.com/ros2/demos/tree/master/demo_nodes_py

    Talker 与 Listener

    publish 与 subscribe 是 ROS / ROS 2 中最基本的场景。下边是一个简单的 publish 的例子。定义了一个 node 名为 talker,以 0.5s 每次的频率向 chatter 这个 topic 上发布消息,同时也在屏幕上显示消息内容:

    import rclpy
    from rclpy.node import Node
    from std_msgs.msg import String
    
    
    class Talker(Node):
    
        def __init__(self):
            super().__init__('talker')   # 继承 Node class 的初始化函数,生成 node 名为 talker
            self.pub = self.create_publisher(String, 'chatter', 10)
            timer_period = 0.5  
            self.timer = self.create_timer(timer_period, self.timer_callback)  # 设定计时器,到时间就调用 callback 函数
            self.i = 1
    
        def timer_callback(self):
            msg = String()
            msg.data = 'Hello World: %d' % self.i
            self.pub.publish(msg)    # 向 topic 上发布数据
            self.get_logger().info('Publishing: "%s"' % msg.data)   # 显示 log 信息
            self.i += 1
    
    
    def main(args=None):   # 这里的 args 是从命令行中接收的参数
        rclpy.init(args=args)     # 在初始化时不再设定 node 的名字
        talker = Talker()
        rclpy.spin(talker)
    
    
    if __name__ == '__main__':
        main()
    

    与之对应的,下边是 listener node 的内容:

    import rclpy
    from rclpy.node import Node
    from std_msgs.msg import String
    
    
    class Listener(Node):
    
        def __init__(self):
            super().__init__('listener')
            self.subscription = self.create_subscription(String, 'chatter', self.listener_callback, 10)
    
        def listener_callback(self, msg):
            self.get_logger().info('I heard: "%s"' % msg.data)
    
    
    def main(args=None):
        rclpy.init(args=args)
        listener = Listener()
        rclpy.spin(listener)
    
    
    if __name__ == '__main__':
        main()
    

    有了两个 python 文件,下边就是设置 ROS 2 package。

    设置 ROS 2 package

    假设现在的 ROS workspace 路径是 ~/ros2_example_ws/src。在其中新建 ROS 2 package 文件夹 pub_sub_pkg,内部文档目录结构如下:

    .
    ├── package.xml
    ├── pub_sub_examples
    │   ├── __init__.py
    │   ├── listener.py
    │   └── talker.py
    └── setup.py
    
    2 directories, 6 files
    

    其中

    • __init__.py 是个空文件,主要作用是表明当前文件夹是 python package

    • package.xml 文件内容如下:

      <?xml version="1.0"?>
      <?xml-model href="http://download.ros.org/schema/package_format2.xsd"   schematypens="http://www.w3.org/2001/XMLSchema"?>
      <package format="2">
      <name>pub_sub_examples</name>
      <version>0.0.1</version>
      <description>Examples of publisher and subscriber using rclpy.</description>
      
      <maintainer email="myemail@address">myname</maintainer>
      <license>Apache License 2.0</license>
      
      <exec_depend>rclpy</exec_depend>
      <exec_depend>std_msgs</exec_depend>
      
      <export>
      <build_type>ament_python</build_type>
      </export>
      </package>
      

      其中关键是指定 node 文件中依赖的 package,我们的 talker 和 listener 文件都比较简单,只用到了 rclpystd_msgs 两个 python package.

    • setup.py 内容如下:

      from setuptools import setup
      
      package_name = 'pub_sub_examples'
      
      setup(
        name=package_name,
        version='0.0.1',
        packages=[package_name],
        install_requires=['setuptools'],    
        zip_safe=True,
        entry_points={
            'console_scripts': [
                'my_talker = pub_sub_examples.talker:main',
                'my_listener = pub_sub_examples.listener:main'
            ]
        }
      )
      

      其中的关键是指定 console_scripts 命令和对应的函数。colcon 编译之后可以直接在命令行中执行 my_talkermy_listener 实现 pub 和 sub 的功能。关于 python package 中 setup.py 的编写,可以参考我们之前的文章

    使用 colcon 编译

    设定好了上述文件之后,在 workspace ~/ros2_example_ws/ 目录下编译:

    colcon build --symlink-install
    

    如果一切顺利,会在 ~/ros2_example_ws/install/pub_sub_examples/bin 中生成可执行文件 my_talkermy_listener。此时还要再 source 一下 install 文件夹中的 setup.bash,更新 ROS 2 package 路径信息,如果之前已经将 source setup.bash 加入 ~/.bashrc 文件,则 source ~/.bashrc 即可。source 之后,~/ros2_example_ws/install/pub_sub_examples/bin 就被加入了 PATH 环境变量中,在命令行中可以直接执行程序,效果如下:

    pub_sub.png

    由于我们编译时用了 --symlink-install 参数,所以对 src 中 python 源文件 talker.pylistener.py 的修改可以马上反映到 install 中的可执行文件中。打开 install 中的 talker 文件可以看到如下内容:

    #!/usr/bin/python3
    # EASY-INSTALL-ENTRY-SCRIPT: 'pub-sub-examples','console_scripts','my_talker'
    __requires__ = 'pub-sub-examples'
    import re
    import sys
    from pkg_resources import load_entry_point
    
    if __name__ == '__main__':
        sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
        sys.exit(
            load_entry_point('pub-sub-examples', 'console_scripts', 'my_talker')()
        )
    

    实际上就是执行了我们之前在 setup.py 文件中设定 console_scripts 中的内容。

    通过 ros2 run 方式执行

    上述编译方式得到的 node 不能用标准的 ros2 run <pkg> <node> 方式执行,ROS 2 默认要调用 install/<pkg>/<lib> 中的文件。如果要用标准方式启动 node 文件,需要做一些额外设置。在 setup.py 同一目录中添加如下 setup.cfg 文件:

    [develop]
    script-dir=$base/lib/pub_sub_examples
    [install]
    install-scripts=$base/lib/pub_sub_examples
    

    这里是将编译输出的目标文件夹设置为 install/pub_sub_examples/lib/pub_sub_examples 。再次用 colcon 编译,然后可以看到对应文件夹下有 my_talkermy_listener 两个文件。重新 source 一下 ~/.bashrc 文件,就可以用 ros2 run pub_sub_examples my_talker/my_listener 命令启用两个 node 了:

    pub_sub_ros.png

    相关文章

      网友评论

          本文标题:ROS 2 基本命令总结

          本文链接:https://www.haomeiwen.com/subject/atftdctx.html