ROS官網新手級教程總結


 

第 1 關卡:安裝和配置 ROS 環境

目標:在計算機上安裝和配置 ROS 環境。

安裝 ROS

按照 ROS 安裝說明進行安裝。

管理環境

確定環境變量 ROS_ROOTROS_PACKAGE_PATH 已經設置好了。以我的為例:

printenv | grep ROS

輸出為:

ROS_ETC_DIR=/opt/ros/melodic/etc/ros
ROS_ROOT=/opt/ros/melodic/share/ros
ROS_MASTER_URI=http://localhost:11311
ROS_VERSION=1
ROS_PYTHON_VERSION=2
ROS_PACKAGE_PATH=/opt/ros/melodic/share
ROSLISP_PACKAGE_DIRECTORIES=
ROS_DISTRO=melodic

每次會話進入的時候都需要執行一次(注意 melodic 根據版本不同而更換):

source /opt/ros/melodic/setup.bash

為了方便,可以把這條語句放在 .bashrc 腳本中。

創建 ROS 工作空間

相對於 rosbuild 而言,catkin 是目前官方推薦的用於管理代碼的方法。

創建一個 catkin 工作空間:

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make

這會創建出類似這樣的目錄結構:

.
├── build
│   ├── CATKIN_IGNORE
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.10.2
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   ├── a.out
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── a.out
│   │   │       └── tmp
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeError.log
│   │   ├── CMakeOutput.log
│   │   ├── CMakeRuleHashes.txt
│   │   ├── CMakeTmp
│   │   ├── Makefile.cmake
│   │   ├── Makefile2
│   │   ├── TargetDirectories.txt
│   │   ├── clean_test_results.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   └── progress.make
│   │   ├── cmake.check_cache
│   │   ├── download_extra_data.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   └── progress.make
│   │   ├── doxygen.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   └── progress.make
│   │   ├── feature_tests.bin
│   │   ├── feature_tests.c
│   │   ├── feature_tests.cxx
│   │   ├── progress.marks
│   │   ├── run_tests.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   └── progress.make
│   │   └── tests.dir
│   │       ├── DependInfo.cmake
│   │       ├── build.make
│   │       ├── cmake_clean.cmake
│   │       └── progress.make
│   ├── CTestConfiguration.ini
│   ├── CTestCustom.cmake
│   ├── CTestTestfile.cmake
│   ├── Makefile
│   ├── atomic_configure
│   │   ├── _setup_util.py
│   │   ├── env.sh
│   │   ├── local_setup.bash
│   │   ├── local_setup.sh
│   │   ├── local_setup.zsh
│   │   ├── setup.bash
│   │   ├── setup.sh
│   │   └── setup.zsh
│   ├── catkin
│   │   └── catkin_generated
│   │       └── version
│   │           └── package.cmake
│   ├── catkin_generated
│   │   ├── env_cached.sh
│   │   ├── generate_cached_setup.py
│   │   ├── installspace
│   │   │   ├── _setup_util.py
│   │   │   ├── env.sh
│   │   │   ├── local_setup.bash
│   │   │   ├── local_setup.sh
│   │   │   ├── local_setup.zsh
│   │   │   ├── setup.bash
│   │   │   ├── setup.sh
│   │   │   └── setup.zsh
│   │   ├── order_packages.cmake
│   │   ├── order_packages.py
│   │   ├── setup_cached.sh
│   │   └── stamps
│   │       └── Project
│   │           ├── _setup_util.py.stamp
│   │           ├── interrogate_setup_dot_py.py.stamp
│   │           ├── order_packages.cmake.em.stamp
│   │           └── package.xml.stamp
│   ├── catkin_make.cache
│   ├── cmake_install.cmake
│   ├── gtest
│   │   ├── CMakeFiles
│   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   └── progress.marks
│   │   ├── CTestTestfile.cmake
│   │   ├── Makefile
│   │   ├── cmake_install.cmake
│   │   └── googlemock
│   │       ├── CMakeFiles
│   │       │   ├── CMakeDirectoryInformation.cmake
│   │       │   ├── gmock.dir
│   │       │   │   ├── DependInfo.cmake
│   │       │   │   ├── __
│   │       │   │   │   └── googletest
│   │       │   │   │       └── src
│   │       │   │   ├── build.make
│   │       │   │   ├── cmake_clean.cmake
│   │       │   │   ├── depend.make
│   │       │   │   ├── flags.make
│   │       │   │   ├── link.txt
│   │       │   │   ├── progress.make
│   │       │   │   └── src
│   │       │   ├── gmock_main.dir
│   │       │   │   ├── DependInfo.cmake
│   │       │   │   ├── __
│   │       │   │   │   └── googletest
│   │       │   │   │       └── src
│   │       │   │   ├── build.make
│   │       │   │   ├── cmake_clean.cmake
│   │       │   │   ├── depend.make
│   │       │   │   ├── flags.make
│   │       │   │   ├── link.txt
│   │       │   │   ├── progress.make
│   │       │   │   └── src
│   │       │   └── progress.marks
│   │       ├── CTestTestfile.cmake
│   │       ├── Makefile
│   │       ├── cmake_install.cmake
│   │       └── gtest
│   │           ├── CMakeFiles
│   │           │   ├── CMakeDirectoryInformation.cmake
│   │           │   ├── gtest.dir
│   │           │   │   ├── DependInfo.cmake
│   │           │   │   ├── build.make
│   │           │   │   ├── cmake_clean.cmake
│   │           │   │   ├── depend.make
│   │           │   │   ├── flags.make
│   │           │   │   ├── link.txt
│   │           │   │   ├── progress.make
│   │           │   │   └── src
│   │           │   ├── gtest_main.dir
│   │           │   │   ├── DependInfo.cmake
│   │           │   │   ├── build.make
│   │           │   │   ├── cmake_clean.cmake
│   │           │   │   ├── depend.make
│   │           │   │   ├── flags.make
│   │           │   │   ├── link.txt
│   │           │   │   ├── progress.make
│   │           │   │   └── src
│   │           │   └── progress.marks
│   │           ├── CTestTestfile.cmake
│   │           ├── Makefile
│   │           └── cmake_install.cmake
│   └── test_results
├── devel
│   ├── _setup_util.py
│   ├── cmake.lock
│   ├── env.sh
│   ├── lib
│   ├── local_setup.bash
│   ├── local_setup.sh
│   ├── local_setup.zsh
│   ├── setup.bash
│   ├── setup.sh
│   └── setup.zsh
└── src
    └── CMakeLists.txt -> /opt/ros/melodic/share/catkin/cmake/toplevel.cmake

運行以下命令可以讓該工作空間添加到 ROS 環境變量中,使得 ROS 可以識別:

source devel/setup.bash

查看 ROS 環境變量確認這個目錄已經添加進來了:

echo $ROS_PACKAGE_PATH

我的環境變量如下:

/workspace/catkin_ws/src:/opt/ros/melodic/share

其中 /workspace/catkin_ws/src 是我新創建的工作空間。

第 2 關卡:瀏覽 ROS 文件系統 

目標:介紹 ROS 文件系統概念,用到了 roscdroslsrospack 命令。

准備工作

下載:

sudo apt-get install ros-<distro>-ros-tutorials

<distro> 對應相應的版本。

ROS 文件系統概念

  • 包(package):ROS 代碼的軟件組織單元。
  • Manifests(package.xml):包的描述性文件。

文件系統工具

rospack

獲取包信息:

rospack find roscpp

輸出:

/opt/ros/melodic/share/roscpp

roscd

改變當前目錄:

roscd roscpp
pwd

輸出:

/opt/ros/melodic/share/roscpp

注意,這幾個識別 ROS 包的命令都是根據 ROS 環境變量(ROS_PACKAGE_PATH)去找包的。

進入包內的子目錄:

roscd roscpp/cmake/
pwd

輸出:

/opt/ros/melodic/share/roscpp/cmake

進入 ROS 應用的日志目錄:

roscd log

注意:如果還沒有運行 ROS 輸出日志的話,會顯示:

No active roscore

之類的信息。

rosls

顯示包內的文件:

rosls roscpp_tutorials

輸出:

cmake  launch  package.xml  srv

第 3 關卡:包的創建

目標:使用 roscreate-pkg 和 catkin 創建包,rospack 列出包的依賴。

catkin 包必須滿足的條件:

  • 包含描述性文件 package.xml
  • 包含使用了 catkin 的 CMakeLists.txt 文件
  • 每個包都有獨立的目錄,沒有嵌套

最簡單的包結構如下:

my_package/
  CMakeLists.txt
  package.xml

典型的工作區中包結構的樣子:

workspace_folder/        -- WORKSPACE
  src/                   -- SOURCE SPACE
    CMakeLists.txt       -- 'Toplevel' CMake file, provided by catkin
    package_1/
      CMakeLists.txt     -- CMakeLists.txt file for package_1
      package.xml        -- Package manifest for package_1
    ...
    package_n/
      CMakeLists.txt     -- CMakeLists.txt file for package_n
      package.xml        -- Package manifest for package_n

創建 catkin 包

進入 src 目錄並創建 catkin 包:

cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp

catkin_create_pkg 會指定包名及其依賴:

# This is an example, do not try to run this
# catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

工作區中包的編譯

cd ~/catkin_ws
catkin_make
source devel/setup.bash

包依賴

查看一階依賴:

rospack depends1 beginner_tutorials 

輸出:

roscpp
rospy
std_msgs

這些依賴可以在包描述性文件中查看:

roscd beginner_tutorials
cat package.xml

輸出:

<?xml version="1.0"?>
<package format="2">
  <name>beginner_tutorials</name>
  <version>0.0.0</version>
  <description>The beginner_tutorials package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <!-- Example:  -->
  <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
  <maintainer email="root@todo.todo">root</maintainer>


  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>TODO</license>


  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <!-- Example: -->
  <!-- <url type="website">http://wiki.ros.org/beginner_tutorials</url> -->


  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <!-- Example: -->
  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->


  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <!-- Examples: -->
  <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
  <!--   <depend>roscpp</depend> -->
  <!--   Note that this is equivalent to the following: -->
  <!--   <build_depend>roscpp</build_depend> -->
  <!--   <exec_depend>roscpp</exec_depend> -->
  <!-- Use build_depend for packages you need at compile time: -->
  <!--   <build_depend>message_generation</build_depend> -->
  <!-- Use build_export_depend for packages you need in order to build against this package: -->
  <!--   <build_export_depend>message_generation</build_export_depend> -->
  <!-- Use buildtool_depend for build tool packages: -->
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  <!-- Use exec_depend for packages you need at runtime: -->
  <!--   <exec_depend>message_runtime</exec_depend> -->
  <!-- Use test_depend for packages you need only for testing: -->
  <!--   <test_depend>gtest</test_depend> -->
  <!-- Use doc_depend for packages you need only for building documentation: -->
  <!--   <doc_depend>doxygen</doc_depend> -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>

依賴也會有自己的依賴,比如:

rospack depends1 roscpp

輸出:

cpp_common
message_runtime
rosconsole
roscpp_serialization
roscpp_traits
rosgraph_msgs
rostime
std_msgs
xmlrpcpp

要查看包的所有依賴(區別於一階依賴):

rospack depends beginner_tutorials

輸出:

cpp_common
rostime
roscpp_traits
roscpp_serialization
catkin
genmsg
genpy
message_runtime
gencpp
geneus
gennodejs
genlisp
message_generation
rosbuild
rosconsole
std_msgs
rosgraph_msgs
xmlrpcpp
roscpp
rosgraph
ros_environment
rospack
roslib
rospy

包的定制化

修改 package.xml:

roscd beginner_tutorials
vim package.xml

整個文件內容如下:

 1 <?xml version="1.0"?>
 2 <package format="2">
 3   <name>beginner_tutorials</name>
 4   <version>0.0.0</version>
 5   <description>The beginner_tutorials package</description>
 6 
 7   <!-- One maintainer tag required, multiple allowed, one person per tag -->
 8   <!-- Example:  -->
 9   <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
10   <maintainer email="root@todo.todo">root</maintainer>
11 
12 
13   <!-- One license tag required, multiple allowed, one license per tag -->
14   <!-- Commonly used license strings: -->
15   <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
16   <license>TODO</license>
17 
18 
19   <!-- Url tags are optional, but multiple are allowed, one per tag -->
20   <!-- Optional attribute type can be: website, bugtracker, or repository -->
21   <!-- Example: -->
22   <!-- <url type="website">http://wiki.ros.org/beginner_tutorials</url> -->
23 
24 
25   <!-- Author tags are optional, multiple are allowed, one per tag -->
26   <!-- Authors do not have to be maintainers, but could be -->
27   <!-- Example: -->
28   <!-- <author email="jane.doe@example.com">Jane Doe</author> -->
29 
30 
31   <!-- The *depend tags are used to specify dependencies -->
32   <!-- Dependencies can be catkin packages or system dependencies -->
33   <!-- Examples: -->
34   <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
35   <!--   <depend>roscpp</depend> -->
36   <!--   Note that this is equivalent to the following: -->
37   <!--   <build_depend>roscpp</build_depend> -->
38   <!--   <exec_depend>roscpp</exec_depend> -->
39   <!-- Use build_depend for packages you need at compile time: -->
40   <!--   <build_depend>message_generation</build_depend> -->
41   <!-- Use build_export_depend for packages you need in order to build against this package: -->
42   <!--   <build_export_depend>message_generation</build_export_depend> -->
43   <!-- Use buildtool_depend for build tool packages: -->
44   <!--   <buildtool_depend>catkin</buildtool_depend> -->
45   <!-- Use exec_depend for packages you need at runtime: -->
46   <!--   <exec_depend>message_runtime</exec_depend> -->
47   <!-- Use test_depend for packages you need only for testing: -->
48   <!--   <test_depend>gtest</test_depend> -->
49   <!-- Use doc_depend for packages you need only for building documentation: -->
50   <!--   <doc_depend>doxygen</doc_depend> -->
51   <buildtool_depend>catkin</buildtool_depend>
52   <build_depend>roscpp</build_depend>
53   <build_depend>rospy</build_depend>
54   <build_depend>std_msgs</build_depend>
55   <build_export_depend>roscpp</build_export_depend>
56   <build_export_depend>rospy</build_export_depend>
57   <build_export_depend>std_msgs</build_export_depend>
58   <exec_depend>roscpp</exec_depend>
59   <exec_depend>rospy</exec_depend>
60   <exec_depend>std_msgs</exec_depend>
61 
62 
63   <!-- The export tag contains other, unspecified, tags -->
64   <export>
65     <!-- Other tools can request additional information be placed here -->
66 
67   </export>
68 </package>

更新第 5 行的 description:The very good beginner-tutorials package

更新第 10 行的維護者標簽。

更新第 16 行的證書。

最終修改后的文件如下:

 1 <?xml version="1.0"?>
 2 <package format="2">
 3   <name>beginner_tutorials</name>
 4   <version>0.0.0</version>
 5   <description>The very good beginner_tutorials package</description>
 6 
 7   <!-- One maintainer tag required, multiple allowed, one person per tag -->
 8   <!-- Example:  -->
 9   <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
10   <maintainer email="1183851628@qq.com">heyulong</maintainer>
11 
12 
13   <!-- One license tag required, multiple allowed, one license per tag -->
14   <!-- Commonly used license strings: -->
15   <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
16   <license>BSD</license>
17 
18 
19   <!-- Url tags are optional, but multiple are allowed, one per tag -->
20   <!-- Optional attribute type can be: website, bugtracker, or repository -->
21   <!-- Example: -->
22   <!-- <url type="website">http://wiki.ros.org/beginner_tutorials</url> -->
23 
24 
25   <!-- Author tags are optional, multiple are allowed, one per tag -->
26   <!-- Authors do not have to be maintainers, but could be -->
27   <!-- Example: -->
28   <!-- <author email="jane.doe@example.com">Jane Doe</author> -->
29 
30 
31   <!-- The *depend tags are used to specify dependencies -->
32   <!-- Dependencies can be catkin packages or system dependencies -->
33   <!-- Examples: -->
34   <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
35   <!--   <depend>roscpp</depend> -->
36   <!--   Note that this is equivalent to the following: -->
37   <!--   <build_depend>roscpp</build_depend> -->
38   <!--   <exec_depend>roscpp</exec_depend> -->
39   <!-- Use build_depend for packages you need at compile time: -->
40   <!--   <build_depend>message_generation</build_depend> -->
41   <!-- Use build_export_depend for packages you need in order to build against this package: -->
42   <!--   <build_export_depend>message_generation</build_export_depend> -->
43   <!-- Use buildtool_depend for build tool packages: -->
44   <!--   <buildtool_depend>catkin</buildtool_depend> -->
45   <!-- Use exec_depend for packages you need at runtime: -->
46   <!--   <exec_depend>message_runtime</exec_depend> -->
47   <!-- Use test_depend for packages you need only for testing: -->
48   <!--   <test_depend>gtest</test_depend> -->
49   <!-- Use doc_depend for packages you need only for building documentation: -->
50   <!--   <doc_depend>doxygen</doc_depend> -->
51   <buildtool_depend>catkin</buildtool_depend>
52   <build_depend>roscpp</build_depend>
53   <build_depend>rospy</build_depend>
54   <build_depend>std_msgs</build_depend>
55   <build_export_depend>roscpp</build_export_depend>
56   <build_export_depend>rospy</build_export_depend>
57   <build_export_depend>std_msgs</build_export_depend>
58   <exec_depend>roscpp</exec_depend>
59   <exec_depend>rospy</exec_depend>
60   <exec_depend>std_msgs</exec_depend>
61 
62 
63   <!-- The export tag contains other, unspecified, tags -->
64   <export>
65     <!-- Other tools can request additional information be placed here -->
66 
67   </export>
68 </package>

第 4 關卡:包的編譯

目標:介紹編譯包的工具鏈。

首先記得確認激活環境配置,在工作區下輸入:

source devel/setup.bash

前面已經用過,使用 catkin_make 可以同時編譯多個包:

# In a catkin workspace
$ catkin_make [make_targets] [-DCMAKE_VARIABLES=...]

這會自動編譯 src 目錄下的包。如果包名是自定義的,比如說 my_src,那么應該這樣做:

# In a catkin workspace
catkin_make --source my_src

上一關卡修改了 package.xml,這里在工作區內輸入:

catkin_make

輸出:

Base path: /workspace/catkin_ws
Source space: /workspace/catkin_ws/src
Build space: /workspace/catkin_ws/build
Devel space: /workspace/catkin_ws/devel
Install space: /workspace/catkin_ws/install
####
#### Running command: "make cmake_check_build_system" in "/workspace/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /workspace/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /workspace/catkin_ws/devel;/opt/ros/melodic
-- This workspace overlays: /workspace/catkin_ws/devel;/opt/ros/melodic
-- Found PythonInterp: /usr/bin/python2 (found suitable version "2.7.15", minimum required is "2") 
-- Using PYTHON_EXECUTABLE: /usr/bin/python2
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /workspace/catkin_ws/build/test_results
-- Found gmock sources under '/usr/src/googletest': gmock will be built
-- Found PythonInterp: /usr/bin/python2 (found version "2.7.15") 
-- Found gtest sources under '/usr/src/googletest': gtests will be built
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.17
-- BUILD_SHARED_LIBS is on
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - beginner_tutorials
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'beginner_tutorials'
-- ==> add_subdirectory(beginner_tutorials)
-- Configuring done
-- Generating done
-- Build files have been written to: /workspace/catkin_ws/build
####
#### Running command: "make -j2 -l2" in "/workspace/catkin_ws/build"
####

第 5 關卡:認識 ROS 節點

目標:介紹 ROS 圖的概念,使用 roscorerosnoderosrun 命令。

准備條件

apt-get install ros-<distro>-ros-tutorials

圖概念介紹

  • 節點(node):使用 ROS 與其他節點通信的可執行文件。
  • 消息(message):當訂閱或者發布主題時使用的 ROS 數據類型。
  • 主題(topic):節點可以向主題發布消息,也可以向主題訂閱消息。
  • 主節點(master):ROS 的命名服務(name service),幫助各節點找到對方。
  • rosout:ROS 中的 stdout/stderr。
  • roscore:master、rosout、parameter 服務器。

節點

節點(node)就是 ROS 包中的可執行文件。ROS 節點使用 ROS 客戶端庫來與各節點進行通信。

節點可以發布或者訂閱主題。

節點也可以提供或者消費服務。

客戶端庫

ROS 客戶端可以允許用不同的語言編寫的節點進行通信。

  • rospy:Python 客戶端庫
  • roscpp:C++ 客戶端庫

roscore

運行 roscore 開啟 ROS 主服務:

roscore

輸出:

... logging to /root/.ros/log/c10afc7a-da7e-11e9-bbfa-0242ac110004/roslaunch-57c67ed2b135-886.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://57c67ed2b135:38567/
ros_comm version 1.14.3


SUMMARY
========

PARAMETERS
 * /rosdistro: melodic
 * /rosversion: 1.14.3

NODES

auto-starting new master
process[master]: started with pid [896]
ROS_MASTER_URI=http://57c67ed2b135:11311/

setting /run_id to c10afc7a-da7e-11e9-bbfa-0242ac110004
process[rosout-1]: started with pid [907]
started core service [/rosout]

使用 rosnode

在運行 roscore 后,另外打開一個終端,輸入:

rosnode list

輸出:

/rosout

這說明當前只有一個 rosout 節點在運行,該節點總是在運行,為了收集記錄節點的輸出日志。

查看某節點的信息:

rosnode info /rosout

輸出:

--------------------------------------------------------------------------------
Node [/rosout]
Publications: 
 * /rosout_agg [rosgraph_msgs/Log]

Subscriptions: 
 * /rosout [unknown type]

Services: 
 * /rosout/get_loggers
 * /rosout/set_logger_level


contacting node http://57c67ed2b135:34783/ ...
Pid: 907

使用 rosrun

rosrun 用於直接運行包中的節點:

rosrun [package_name] [node_name]

打開一個新的終端,輸入:

rosrun turtlesim turtlesim_node

 

 

 

 

 

打開一個新的終端,輸入:

rosnode list

輸出:

/rosout
/turtlesim

關閉 turtlesim 窗口,重新打開該節點,並且指定節點名:

rosrun turtlesim turtlesim_node __name:=my_turtle

在那個窗口輸入 rosnode list 后顯示:

/my_turtle
/rosout

查看與某節點之間的通信:

rosnode ping my_turtle

輸出:

rosnode: node is [/my_turtle]
pinging /my_turtle with a timeout of 3.0s
xmlrpc reply from http://57c67ed2b135:37817/    time=29.969931ms
xmlrpc reply from http://57c67ed2b135:37817/    time=1.099825ms
xmlrpc reply from http://57c67ed2b135:37817/    time=1.539230ms
xmlrpc reply from http://57c67ed2b135:37817/    time=2.717018ms
......

第 6 關卡:認識 ROS 主題

目標:介紹 ROS 主題以及 rostopicrqt_plot 命令的使用。

配置

接着上一關卡做的東西。

確保 roscore 以及運行起來了:

roscore

確保 turtlesim_node 運行起來了:

rosrun turtlesim turtlesim_node __name:=my_turtle

然后,在一個新的終端運行一個新節點:

rosrun turtlesim turtle_teleop_key

輸出:

Reading from keyboard
---------------------------
Use arrow keys to move the turtle.

在這個節點里動一動箭頭鍵:

 

 

ROS 主題

my_turtle 節點和 turtle_teleop_key 節點通過 ROS 主題(topic)進行通信。

turtle_teleop_key 以主題的方式發布按鍵,而 my_turtle 訂閱該主題接收按鍵。

rqt_graph

使用 rqt_graph 創建 ROS 系統動態圖結構。

安裝:

apt-get install ros-<distro>-rqt
apt-get install ros-<distro>-rqt-common-plugins

輸入:

rosrun rqt_graph rqt_graph

得到:

 

 

rostopic

rostopic 用於獲取 ROS 主題的信息。

rostopic -h

輸出:

rostopic is a command-line tool for printing information about ROS Topics.

Commands:
        rostopic bw     display bandwidth used by topic
        rostopic delay  display delay of topic from timestamp in header
        rostopic echo   print messages to screen
        rostopic find   find topics by type
        rostopic hz     display publishing rate of topic    
        rostopic info   print information about active topic
        rostopic list   list active topics
        rostopic pub    publish data to topic
        rostopic type   print topic or field type

Type rostopic <command> -h for more detailed usage, e.g. 'rostopic echo -h'

顯示主題的發布數據:

rostopic echo /turtle1/cmd_vel

在發布按鍵主題的節點中活動箭頭鍵后,這邊的 rostopic 會顯示出:

linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: 0.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 2.0
---
linear: 
  x: -2.0
  y: 0.0
  z: 0.0
angular: 
  x: 0.0
  y: 0.0
  z: 0.0
---
......

重新查看 rqt_graph 出的圖(刷新或者重啟),可以看到 rostopic 也訂閱了 /turtle1/cmd_vel 主題了。

 

 

查看當前發布和訂閱的主題:

rostopic list -v

輸出:

Published topics:
 * /turtle1/color_sensor [turtlesim/Color] 1 publisher
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher
 * /rosout [rosgraph_msgs/Log] 5 publishers
 * /rosout_agg [rosgraph_msgs/Log] 1 publisher
 * /turtle1/pose [turtlesim/Pose] 1 publisher

Subscribed topics:
 * /turtle1/cmd_vel [geometry_msgs/Twist] 2 subscribers
 * /rosout [rosgraph_msgs/Log] 1 subscriber
 * /statistics [rosgraph_msgs/TopicStatistics] 2 subscribers

主題間的通信通過節點間發送的 ROS 消息(message)來實現。發布者和訂閱者之間要通信,必須發送和接收相同的消息類型

消息類型定義了主題的類型。

查看發布主題的消息類型:

# rostopic type [topic]
rostopic type /turtle1/cmd_vel

輸出:

geometry_msgs/Twist

使用 rostopic pub 發送某一主題的數據:

# rostopic pub [topic] [msg_type] [args]
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'

效果如下:

 

 

發布穩定的數據流:

rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'

此時烏龜會不斷地轉圈移動:

 

 

查看目前的 ROS 圖:

 

 

訂閱查看 /turtle1/pose 主題:

rostopic echo /turtle1/pose

輸出:

......
x: 5.10276937485
y: 5.45978927612
theta: 0.407544314861
linear_velocity: 2.0
angular_velocity: -1.79999995232
---
x: 5.13250160217
y: 5.47162103653
theta: 0.37874430418
linear_velocity: 2.0
angular_velocity: -1.79999995232
---
x: 5.16256189346
y: 5.48259210587
theta: 0.349944293499
linear_velocity: 2.0
angular_velocity: -1.79999995232
---
......

查看目前的圖結構:

 

 

使用 rostopic hz 查看主題發布的頻率:

# rostopic hz [topic]
rostopic hz /turtle1/pose

輸出:

subscribed to [/turtle1/pose]
average rate: 30.777
        min: 0.030s max: 0.036s std dev: 0.00128s window: 29
average rate: 30.489
        min: 0.030s max: 0.042s std dev: 0.00181s window: 60
average rate: 30.510
        min: 0.029s max: 0.042s std dev: 0.00211s window: 90
average rate: 30.517
        min: 0.029s max: 0.042s std dev: 0.00208s window: 121
average rate: 30.354
        min: 0.029s max: 0.042s std dev: 0.00207s window: 151
average rate: 30.378
        min: 0.029s max: 0.062s std dev: 0.00296s window: 181
average rate: 30.380
        min: 0.029s max: 0.062s std dev: 0.00287s window: 212
average rate: 29.823
        min: 0.029s max: 0.062s std dev: 0.00439s window: 237
...

可以看到 my_turtle 以 30Hz 的速率不斷發數據。

查看消息的更多細節:

rostopic type /turtle1/cmd_vel | rosmsg show

輸出:

geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z

輸入:

rostopic type /turtle1/pose | rosmsg show

輸出:

float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity

rqt_plot

rqt_plot 可以顯示主題上發布的數據滾動式時間圖。

rosrun rqt_plot rqt_plot

第 7 關卡:ROS 服務和參數

目標:熟悉 ROS 服務和參數,使用 rosservicerosparam

服務(servece)節點相互通信的另一種方式。服務使得節點可以發送請求和接收響應

rosservice

rosservice 可以很容易用上 ROS 的客戶端/服務端框架。

rosservice -h

輸出:

Commands:
        rosservice args print service arguments
        rosservice call call the service with the provided args
        rosservice find find services by service type
        rosservice info print information about service
        rosservice list list active services
        rosservice type print service type
        rosservice uri  print service ROSRPC uri

Type rosservice <command> -h for more detailed usage, e.g. 'rosservice call -h'

rosservece list 顯示當前服務的列表:

rosservice list

輸出:

/clear
/kill
/my_turtle/get_loggers
/my_turtle/set_logger_level
/reset
/rosout/get_loggers
/rosout/set_logger_level
/rostopic_4219_1568863406310/get_loggers
/rostopic_4219_1568863406310/set_logger_level
/rqt_gui_py_node_3673/get_loggers
/rqt_gui_py_node_3673/set_logger_level
/rqt_gui_py_node_4098/get_loggers
/rqt_gui_py_node_4098/set_logger_level
/rqt_gui_py_node_4237/get_loggers
/rqt_gui_py_node_4237/set_logger_level
/spawn
/teleop_turtle/get_loggers
/teleop_turtle/set_logger_level
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative

rosservice type 查詢服務類型:

rosservice type /clear

輸出:

std_srvs/Empty

Empty 表示該服務既沒有參數也沒有返回值。

rosservice call 調用服務:

# rosservice call [service] [args]
rosservice call /clear

輸入之后 my_turtle 節點的背景軌跡清空了:

 

 

rossrv show 查詢 /spawn 的參數和返回值信息。

rosservice type /spawn | rossrv show

輸出:

float32 x
float32 y
float32 theta
string name
---
string name

調用 /spawn 創建另一只烏龜:

rosservice call /spawn 2 2 0.2 "good turtle"

效果如下:

name: "good_turtle"

 

 

rosparam

rosparam 可以存儲和操作 ROS 參數服務器(parameter server)上的數據。

參數服務器可以存儲整型、浮點數、布爾型、字典和列表。

rosparame 使用 YAML 標記語言的語法。

rosparam -h

輸出:

rosparam is a command-line tool for getting, setting, and deleting parameters from the ROS Parameter Server.

Commands:
        rosparam set    set parameter
        rosparam get    get parameter
        rosparam load   load parameters from file
        rosparam dump   dump parameters to file
        rosparam delete delete parameter
        rosparam list   list parameter names

setget

設置參數:

# rosparam set [param_name]
rosparam set /background_r 150
# refresh
rosservice call /clear

效果如下:

 

 

獲取參數:

# rosparam get [param_name]
rosparam get /background_g 

輸出:

86

顯示參數服務器中所有的參數:

rosparam get /

輸出:

background_b: 255
background_g: 86
background_r: 150
rosdistro: 'melodic

  '
roslaunch:
  uris: {host_57c67ed2b135__39317: 'http://57c67ed2b135:39317/'}
rosversion: '1.14.3

  '
run_id: 9a92ffb2-da87-11e9-9b98-0242ac110004

rosparam 進行參數的持久化和加載

# usage
# rosparam dump [file_name] [namespace]
# rosparam load [file_name] [namespace]

rosparam dump 進行持久化:

rosparam dump params.yaml

會在當前目錄生成一個 params.yaml 文件。

rosparam dump 加載參數到一個新的命名空間(如 version2):

rosparam load params.yaml version2
rosparam get /version2/background_b

返回:

255

第 8 關卡:使用 rqt_console 和 roslaunch

目標:使用 rqt_consolerqt_logger_level 進行調試;roslaunch 一次性啟動多個節點。

准備條件

apt-get install ros-melodic-rqt ros-melodic-rqt-common-plugins ros-melodic-turtlesim

使用 rqt_console 和 rqt_logger_level

rqt_console 利用了 ROS 的日志框架,用於顯示節點的輸出。

rqt_logger_level 可以用於調節日志的 verbosity 級別。

在兩個終端分別運行兩個命令,首先打開 console:

rosrun rqt_console rqt_console

 

 

 然后打開 logger level:

rosrun rqt_logger_level rqt_logger_level

 

 

重啟烏龜應用:

rosrun turtlesim turtlesim_node __name:=my_turtle

顯示出來一條日志:

 

 

使用 roslaunch

# Usage
# roslaunch [package] [filename.launch]

進入 beginner_tutorials 目錄:

source devel/setup.bash
roscd beginner_tutorials

創建 launch 目錄:

mkdir launch
cd launch

創建 Launch 文件:

vim turtlemimic.launch
 1 <launch>
 2     <group ns="turtlesim1">
 3         <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
 4     </group>
 5 
 6     <group ns="turtlesim2">
 7         <node pkg="turtlesim" name="sim" type="turtlesim_node"/>
 8     </group>
 9 
10     <node pkg="turtlesim" name="mimic" type="mimic">
11         <remap from="input" to="turtlesim1/turtle1"/>
12         <remap from="output" to="turtlesim2/turtle1"/>
13     </node>
14 
15 </launch>

注解:

  • 第 1 行:表示這是個 launch 文件。
  • 第 2-8 行:創建兩個 turtlesim 節點,其命名空間不沖突。
  • 第 10-13 行:創建一個 mimic 節點,其中 turtlesim2 會模仿 turtlesim1。 

啟動 launch 文件:

roslaunch beginner_tutorials turtlemimic.launch

再開一個終端向 turtlesim1 發送數據流:

rostopic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'

 

 

查看圖結構:

rqt_graph

第 9 關卡:使用 rosed 修改 ROS 文件

目標:使用 rosed 進行編輯。

 

 

# Usage
# rosed [package_name] [filename]

修改 roscpp 包中的 Logger.msg 文件(默認使用 vim 打開)。

第 10 關卡:創建 ROS msg 和 srv

目標:創建和構建 msgsrv 文件;使用 rosmsgrossrvroscp 命令。

msg 和 srv 的介紹

  • msg:msg 文件是描述 ROS 消息字段的文本文件。它用於生成不同編程語言的消息源代碼。
  • srv:srv 用於描述服務。它包含兩部分:請求響應

msg 文件存儲於包中的 msg 目錄,而 srv 文件存儲於 srv 目錄。

在 msg 文件中,每行都是一個字段類型和對應的字段名稱。

字段類型種類有:

  • int8, int16, int32, int64 (plus uint*)
  • float32, float64
  • string
  • time, duration
  • 其他 msg 文件
  • 變長數組 array[] 和固定長度數組 array[C]

ROS 中還有特殊的類型:Header,header 包含了時間戳坐標系信息。

一個 msg 文件的例子:

Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist

srv 文件與 msg 文件類似,只不過通過 --- 分割了請求和響應兩部分。

int64 A
int64 B
---
int64 Sum

創建 msg 及其配置

創建一個 Num.msg 文件:

roscd beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg

為了保證該文件轉換為其他語言的源代碼,打開 package.xml,確保添加了以下兩行:

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

message_generation 在編譯時有用,message_runtime 用在運行時。

打開 CMakeLists.txt 添加 message_generation 的依賴,這樣才能生成消息。

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

並且確保道出了 message_runtime 依賴:

catkin_package(
  ...
  CATKIN_DEPENDS message_runtime ...
  ...)

還要添加 msg 文件:

add_message_files(
  FILES
  Num.msg
)

確保 generate_messages 函數的調用:

generate_messages(
  DEPENDENCIES
  std_msgs
)

使用 rosmsg

# Usage
# rosmsg show [message type]

生成一個消息:

catkin_make
rosmsg show beginner_tutorials/Num

輸出:

int64 num

使用 srv

創建 srv 目錄:

roscd beginner_tutorials
mkdir srv

復制一個 srv 文件過去:

roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

文件的內容如下:

int64 a
int64 b
---
int64 sum

同樣,配置 package.xml 文件,確保 srv 文件會轉換為源代碼(前面已經完成)。

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

配置 CMakeLists.txt 文件,處理依賴(前面已經完成):

# Do not just add this line to your CMakeLists.txt, modify the existing line
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

注冊服務文件:

add_service_files(
  FILES
  AddTwoInts.srv
)

使用 rossrv

# Usage
# rossrv show <service type>

編譯后運行例子:

catkin_make
rossrv show beginner_tutorials/AddTwoInts

輸出:

int64 a
int64 b
---
int64 sum

第 11 關卡:編寫簡單的發布者和訂閱者(C++)

目標:用 C++ 編寫發布者和訂閱者。

編寫發布者節點

進入目錄:

roscd beginner_tutorials
mkdir src

創建 src/talker.cpp 文件:

 1 #include "ros/ros.h"
 2 #include "std_msgs/String.h"
 3 
 4 #include <sstream>
 5 
 6 /**
 7  * This tutorial demonstrates simple sending of messages over the ROS system.
 8  */
 9 int main(int argc, char **argv)
10 {
11   /**
12    * The ros::init() function needs to see argc and argv so that it can perform
13    * any ROS arguments and name remapping that were provided at the command line.
14    * For programmatic remappings you can use a different version of init() which takes
15    * remappings directly, but for most command-line programs, passing argc and argv is
16    * the easiest way to do it.  The third argument to init() is the name of the node.
17    *
18    * You must call one of the versions of ros::init() before using any other
19    * part of the ROS system.
20    */
21   ros::init(argc, argv, "talker");
22 
23   /**
24    * NodeHandle is the main access point to communications with the ROS system.
25    * The first NodeHandle constructed will fully initialize this node, and the last
26    * NodeHandle destructed will close down the node.
27    */
28   ros::NodeHandle n;
29 
30   /**
31    * The advertise() function is how you tell ROS that you want to
32    * publish on a given topic name. This invokes a call to the ROS
33    * master node, which keeps a registry of who is publishing and who
34    * is subscribing. After this advertise() call is made, the master
35    * node will notify anyone who is trying to subscribe to this topic name,
36    * and they will in turn negotiate a peer-to-peer connection with this
37    * node.  advertise() returns a Publisher object which allows you to
38    * publish messages on that topic through a call to publish().  Once
39    * all copies of the returned Publisher object are destroyed, the topic
40    * will be automatically unadvertised.
41    *
42    * The second parameter to advertise() is the size of the message queue
43    * used for publishing messages.  If messages are published more quickly
44    * than we can send them, the number here specifies how many messages to
45    * buffer up before throwing some away.
46    */
47   ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
48 
49   ros::Rate loop_rate(10);
50 
51   /**
52    * A count of how many messages we have sent. This is used to create
53    * a unique string for each message.
54    */
55   int count = 0;
56   while (ros::ok())
57   {
58     /**
59      * This is a message object. You stuff it with data, and then publish it.
60      */
61     std_msgs::String msg;
62 
63     std::stringstream ss;
64     ss << "hello world " << count;
65     msg.data = ss.str();
66 
67     ROS_INFO("%s", msg.data.c_str());
68 
69     /**
70      * The publish() function is how you send messages. The parameter
71      * is the message object. The type of this object must agree with the type
72      * given as a template parameter to the advertise<>() call, as was done
73      * in the constructor above.
74      */
75     chatter_pub.publish(msg);
76 
77     ros::spinOnce();
78 
79     loop_rate.sleep();
80     ++count;
81   }
82 
83 
84   return 0;
85 }

該代碼流程:

  • 初始化 ROS 系統。
  • 向 master 通知:以主題 chatter 發布 std_msgs/String 消息。
  • 以 10 Hz 的頻率進行消息發布。

編寫訂閱者節點

創建 listener.cpp 文件:

 1 #include "ros/ros.h"
 2 #include "std_msgs/String.h"
 3 
 4 /**
 5  * This tutorial demonstrates simple receipt of messages over the ROS system.
 6  */
 7 void chatterCallback(const std_msgs::String::ConstPtr& msg)
 8 {
 9   ROS_INFO("I heard: [%s]", msg->data.c_str());
10 }
11 
12 int main(int argc, char **argv)
13 {
14   /**
15    * The ros::init() function needs to see argc and argv so that it can perform
16    * any ROS arguments and name remapping that were provided at the command line.
17    * For programmatic remappings you can use a different version of init() which takes
18    * remappings directly, but for most command-line programs, passing argc and argv is
19    * the easiest way to do it.  The third argument to init() is the name of the node.
20    *
21    * You must call one of the versions of ros::init() before using any other
22    * part of the ROS system.
23    */
24   ros::init(argc, argv, "listener");
25 
26   /**
27    * NodeHandle is the main access point to communications with the ROS system.
28    * The first NodeHandle constructed will fully initialize this node, and the last
29    * NodeHandle destructed will close down the node.
30    */
31   ros::NodeHandle n;
32 
33   /**
34    * The subscribe() call is how you tell ROS that you want to receive messages
35    * on a given topic.  This invokes a call to the ROS
36    * master node, which keeps a registry of who is publishing and who
37    * is subscribing.  Messages are passed to a callback function, here
38    * called chatterCallback.  subscribe() returns a Subscriber object that you
39    * must hold on to until you want to unsubscribe.  When all copies of the Subscriber
40    * object go out of scope, this callback will automatically be unsubscribed from
41    * this topic.
42    *
43    * The second parameter to the subscribe() function is the size of the message
44    * queue.  If messages are arriving faster than they are being processed, this
45    * is the number of messages that will be buffered up before beginning to throw
46    * away the oldest ones.
47    */
48   ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
49 
50   /**
51    * ros::spin() will enter a loop, pumping callbacks.  With this version, all
52    * callbacks will be called from within this thread (the main one).  ros::spin()
53    * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
54    */
55   ros::spin();
56 
57   return 0;
58 }

該代碼的流程:

  • 初始化 ROS 系統。
  • 訂閱 chatter 主題。
  • spin:等待消息到達。
  • 當消息到達時,調用 chatterCallback()。

編譯節點

確保 CMakeLists.txt 的配置:

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})

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

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

構建后會創建兩個可執行文件 talker 和 listener,放在 ~/catkin_ws/devel/lib/<package name> 中。

在工作區上運行:

catkin_make

第 12 關卡:編寫簡單的發布者和訂閱者(Python)

目標:用 Python 編寫發布者和訂閱者。

編寫發布者節點

進入目錄:

roscd beginner_tutorials
mkdir scripts
cd scripts

創建 talker.py:

 1 #!/usr/bin/env python
 2 import rospy
 3 from std_msgs.msg import String
 4 
 5 def talker():
 6     pub = rospy.Publisher('chatter', String, queue_size=10)
 7     rospy.init_node('talker', anonymous=True)
 8     rate = rospy.Rate(10) # 10hz
 9     while not rospy.is_shutdown():
10         hello_str = "hello world %s" % rospy.get_time()
11         rospy.loginfo(hello_str)
12         pub.publish(hello_str)
13         rate.sleep()
14 
15 if __name__ == '__main__':
16     try:
17         talker()
18     except rospy.ROSInterruptException:
19         pass

編寫訂閱者節點

在 scripts 下面創建 listener.py 文件:

 1 #!/usr/bin/env python
 2 import rospy
 3 from std_msgs.msg import String
 4 
 5 def callback(data):
 6     rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
 7     
 8 def listener():
 9 
10     # In ROS, nodes are uniquely named. If two nodes with the same
11     # name are launched, the previous one is kicked off. The
12     # anonymous=True flag means that rospy will choose a unique
13     # name for our 'listener' node so that multiple listeners can
14     # run simultaneously.
15     rospy.init_node('listener', anonymous=True)
16 
17     rospy.Subscriber("chatter", String, callback)
18 
19     # spin() simply keeps python from exiting until this node is stopped
20     rospy.spin()
21 
22 if __name__ == '__main__':
23     listener()

編譯節點

注意需要運行的 Python 腳本必須修改成可執行文件,scripts 目錄下:

chmod +x talker.py
chmod +x listener.py

工作區下面輸入:

catkin_make

第 13 關卡:運行檢查簡單的發布者和訂閱者

目標:運行檢查編寫好的發布者和訂閱者。

運行發布者

工作區下面,啟動主節點:

roscore

另開啟一個終端激活環境:

source devel/setup.bash

運行發布者:

# C++
rosrun beginner_tutorials talker
# Python
rosrun beginner_tutorials talker.py

不斷輸出:

[INFO] [WallTime: 1314931831.774057] hello world 1314931831.77
[INFO] [WallTime: 1314931832.775497] hello world 1314931832.77
[INFO] [WallTime: 1314931833.778937] hello world 1314931833.78
[INFO] [WallTime: 1314931834.782059] hello world 1314931834.78
[INFO] [WallTime: 1314931835.784853] hello world 1314931835.78
[INFO] [WallTime: 1314931836.788106] hello world 1314931836.79

在另一個終端用同樣的方式運行訂閱者:

# C++
rosrun beginner_tutorials listener
# Python
rosrun beginner_tutorials listener.py

不斷輸出:

[INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26
[INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26
[INFO] [WallTime: 1314931971.266348] /listener_17657_1314931968795I heard hello world 1314931971.26
[INFO] [WallTime: 1314931972.270429] /listener_17657_1314931968795I heard hello world 1314931972.27
[INFO] [WallTime: 1314931973.274382] /listener_17657_1314931968795I heard hello world 1314931973.27
[INFO] [WallTime: 1314931974.277694] /listener_17657_1314931968795I heard hello world 1314931974.28
[INFO] [WallTime: 1314931975.283708] /listener_17657_1314931968795I heard hello world 1314931975.28

第 14 關卡:編寫簡單的服務器和客戶端(C++)

 目標:用 C++ 編寫服務器和客戶端節點。 

編寫服務器節點

在 beginner_tutorials/src 里編寫 add_two_ints_server.cpp:

 1 #include "ros/ros.h"
 2 #include "beginner_tutorials/AddTwoInts.h"
 3 
 4 bool add(beginner_tutorials::AddTwoInts::Request  &req,
 5          beginner_tutorials::AddTwoInts::Response &res)
 6 {
 7   res.sum = req.a + req.b;
 8   ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
 9   ROS_INFO("sending back response: [%ld]", (long int)res.sum);
10   return true;
11 }
12 
13 int main(int argc, char **argv)
14 {
15   ros::init(argc, argv, "add_two_ints_server");
16   ros::NodeHandle n;
17 
18   ros::ServiceServer service = n.advertiseService("add_two_ints", add);
19   ROS_INFO("Ready to add two ints.");
20   ros::spin();
21 
22   return 0;
23 }

編寫客戶端節點

再創建 add_two_ints_client.cpp 文件:

 1 #include "ros/ros.h"
 2 #include "beginner_tutorials/AddTwoInts.h"
 3 #include <cstdlib>
 4 
 5 int main(int argc, char **argv)
 6 {
 7   ros::init(argc, argv, "add_two_ints_client");
 8   if (argc != 3)
 9   {
10     ROS_INFO("usage: add_two_ints_client X Y");
11     return 1;
12   }
13 
14   ros::NodeHandle n;
15   ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
16   beginner_tutorials::AddTwoInts srv;
17   srv.request.a = atoll(argv[1]);
18   srv.request.b = atoll(argv[2]);
19   if (client.call(srv))
20   {
21     ROS_INFO("Sum: %ld", (long int)srv.response.sum);
22   }
23   else
24   {
25     ROS_ERROR("Failed to call service add_two_ints");
26     return 1;
27   }
28 
29   return 0;
30 }

編譯節點

在 beginner_tutorials 包中的 CMakeLists.txt 添加配置:

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

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

最后在工作區進行編譯:

# In your catkin workspace
cd ~/catkin_ws
catkin_make

第 15 關卡:編寫簡單的服務器和客戶端(Python)

目標:用 Python 編寫服務器和客戶端節點。 

編寫服務器節點

在 beginner_tutorials/scripts 里編寫 add_two_ints_server.py:

 1 #!/usr/bin/env python
 2 
 3 from beginner_tutorials.srv import AddTwoInts,AddTwoIntsResponse
 4 import rospy
 5 
 6 def handle_add_two_ints(req):
 7     print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b))
 8     return AddTwoIntsResponse(req.a + req.b)
 9 
10 def add_two_ints_server():
11     rospy.init_node('add_two_ints_server')
12     s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
13     print "Ready to add two ints."
14     rospy.spin()
15 
16 if __name__ == "__main__":
17     add_two_ints_server()

使腳本可執行:

chmod +x add_two_ints_server.py

編寫客戶端節點

再創建 add_two_ints_client.py 文件:

#!/usr/bin/env python

import sys
import rospy
from beginner_tutorials.srv import *

def add_two_ints_client(x, y):
    rospy.wait_for_service('add_two_ints')
    try:
        add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
        resp1 = add_two_ints(x, y)
        return resp1.sum
    except rospy.ServiceException, e:
        print "Service call failed: %s"%e

def usage():
    return "%s [x y]"%sys.argv[0]

if __name__ == "__main__":
    if len(sys.argv) == 3:
        x = int(sys.argv[1])
        y = int(sys.argv[2])
    else:
        print usage()
        sys.exit(1)
    print "Requesting %s+%s"%(x, y)
    print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))

使腳本可執行:

chmod +x add_two_ints_client.py

最后編譯一下即可。

第 16 關卡:運行檢查簡單的服務器和客戶端

目標:運行檢查編寫好的服務器和客戶端。

運行服務器

工作區下面,啟動主節點:

roscore

另開啟一個終端激活環境:

source devel/setup.bash

運行服務器:

# C++
rosrun beginner_tutorials add_two_ints_server
# Python
rosrun beginner_tutorials add_two_ints_server.py

輸出:

[ INFO] [1568949373.838082500]: Ready to add two ints.

運行客戶端

運行客戶端:

# C++
rosrun beginner_tutorials add_two_ints_client 1 3
# Python
rosrun beginner_tutorials add_two_ints_client.py 1 3

輸出:

[ INFO] [1568949401.094979600]: Sum: 4 

第 17 關卡:錄制和播放數據

目標:從運行的 ROS 系統上將數據錄制為 .bag 文件;重新播放 .bag 文件上的數據。

錄制數據

這里解決如何從運行的 ROS 系統中錄制主題數據,主題數據最終會匯聚為一個 bag 文件。

下面執行我們前面已經熟悉了幾個操作。

終端 1:

roscore

終端 2:

rosrun turtlesim turtlesim_node 

終端 3:

rosrun turtlesim turtle_teleop_key

查看當前運行系統中發布的主題列表:

rostopic list -v

輸出:

Published topics:
 * /turtle1/color_sensor [turtlesim/Color] 1 publisher
 * /rosout [rosgraph_msgs/Log] 2 publishers
 * /rosout_agg [rosgraph_msgs/Log] 1 publisher
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher
 * /turtle1/pose [turtlesim/Pose] 1 publisher

Subscribed topics:
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 subscriber
 * /rosout [rosgraph_msgs/Log] 1 subscriber

其中,/turtle1/cmd_vel 是 teleop_turtle 發布的主題數據。

打開新終端,運行命令錄制所有正在發布的主題數據(rosbag record):

mkdir ~/bagfiles
cd ~/bagfiles
rosbag record -a

然后在 turtle_teleop 終端里面滑動方向鍵,移動烏龜十來秒,接着在最新的 rosbag record 命令的終端窗口下面終止進程。查看當前目錄,可以發現已經生成了一個新的 bag 文件。

2019-09-19-17-55-44.bag

這個 bag 文件現在包含了所有 rosbag record 期間的所有節點發布的主題數據

檢查並播放 bag 文件

使用 rosbag info 檢查 bag 文件信息:

rosbag info 2019-09-19-17-55-44.bag

輸出:

path:        2019-09-19-17-55-44.bag
version:     2.0
duration:    57.9s
start:       Sep 19 2019 17:55:44.60 (1568886944.60)
end:         Sep 19 2019 17:56:42.50 (1568887002.50)
size:        413.4 KB
messages:    5842
compression: none [1/1 chunks]
types:       geometry_msgs/Twist [9f195f881246fdfa2798d1d3eebca84a]
             rosgraph_msgs/Log   [acffd30cd6b6de30f120938c17c593fb]
             turtlesim/Color     [353891e354491c51aabe32df673fb446]
             turtlesim/Pose      [863b248d5016ca62ea2e895ae5265cf9]
topics:      /rosout                    5 msgs    : rosgraph_msgs/Log   (2 connections)
             /turtle1/cmd_vel         105 msgs    : geometry_msgs/Twist
             /turtle1/color_sensor   2869 msgs    : turtlesim/Color    
             /turtle1/pose           2863 msgs    : turtlesim/Pose

接下來重播 bag 文件以生成 ROS 當時運行時的行為

首先中斷 teleop 程序,免得它一直發數據。而 turtlesim 繼續運行。

接着運行命令:

rosbag play <your bagfile>

進行播放,最后輸出:

[ INFO] [1568887897.273155900]: Opening 2019-09-19-17-55-44.bag

Waiting 0.2 seconds after advertising topics... done.

Hit space to toggle paused, or 's' to step.
 [RUNNING]  Bag Time: 1568887002.474074   Duration: 57.874504 / 57.904033               
Done.

這個時候烏龜會繼續按照之前記錄好的套路動起來。

記錄數據的子集

rosbag record 支持錄制特定主題的數據。

rosbag record -O subset /turtle1/cmd_vel /turtle1/pose

故技重施,生成了文件 subset.bag 文件。-O 指定了文件名,后面跟的兩個參數制定了特定的主題。

rosbag info subset.bag 

輸出:

path:        subset.bag
version:     2.0
duration:    38.1s
start:       Sep 19 2019 18:22:02.74 (1568888522.74)
end:         Sep 19 2019 18:22:40.83 (1568888560.83)
size:        144.5 KB
messages:    1787
compression: none [1/1 chunks]
types:       geometry_msgs/Twist [9f195f881246fdfa2798d1d3eebca84a]
             turtlesim/Pose      [863b248d5016ca62ea2e895ae5265cf9]
topics:      /turtle1/cmd_vel     72 msgs    : geometry_msgs/Twist
             /turtle1/pose      1715 msgs    : turtlesim/Pose

 

 

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM