第 1 關卡:安裝和配置 ROS 環境
目標:在計算機上安裝和配置 ROS 環境。
安裝 ROS
按照 ROS 安裝說明進行安裝。
管理環境
確定環境變量 ROS_ROOT 和 ROS_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 文件系統概念,用到了 roscd,rosls,rospack 命令。
准備工作
下載:
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 圖的概念,使用 roscore、rosnode、rosrun 命令。
准備條件
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 主題以及 rostopic 和 rqt_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 服務和參數,使用 rosservice 和 rosparam。
服務(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
set 和 get:
設置參數:
# 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_console 和 rqt_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
目標:創建和構建 msg 和 srv 文件;使用 rosmsg、rossrv、roscp 命令。
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
參考: