目錄
一、工作空間workspace
1.1 初始化工作空間
mkdir -p ~/dev_ws/src
1.2 文件結(jié)構(gòu)介紹
ROS2工作空間的文件結(jié)構(gòu)由四個目錄組成,分別是build/、install/、src/、log/,其中src/目錄下存放最小ROS2構(gòu)建部件成為功能包package。

- src:代碼空間,未來編寫的代碼、腳本,都需要人為的放置到這里;
- build:編譯空間,保存編譯過程中產(chǎn)生的中間文件;
- install:安裝空間,放置編譯得到的可執(zhí)行文件和腳本;
- log:日志空間,編譯和運行過程中,保存各種警告、錯誤、信息等日志。
總體來講,這四個空間的文件夾,我們絕大部分操作都是在src中進(jìn)行的,編譯成功后,就會執(zhí)行install里邊的結(jié)果,build和log兩個文件夾用的很少。
二、軟件包package
任何ROS2的代碼無論是C++還是Python都要放到package中,這樣才能正常的編譯和運行。
一個package可以編譯出來多個目標(biāo)文件(可執(zhí)行程序、動態(tài)靜態(tài)庫、頭文件等等)。
2.1 package結(jié)構(gòu)
一個package下常見的文件路徑有:
├── CMakeLists.txt #package的編譯規(guī)則(必須)
├── package.xml #package的描述信息(必須)
├── src/ #源代碼文件
├── include/ #C++頭文件
├── scripts/ #可執(zhí)行腳本
├── msg/ #自定義消息
├── srv/ #自定義服務(wù)
├── models/ #3D模型文件
├── urdf/ #urdf文件
├── launch/ #launch文件
其中定義package的是CMakeLists.txt和package.xml,這兩個文件是package中必不可少的。colcon編譯系統(tǒng)在編譯前,首先就要解析這兩個文件。這兩個文件就定義了一個package。
CMakeLists.txt:定義package的包名、依賴、源文件、目標(biāo)文件等編譯規(guī)則,是package不可少的成分
package.xml:描述package的包名、版本號、作者、依賴等信息,是package不可少的成分
src/:存放ROS的源代碼,包括C++的源碼和(.cpp)以及Python的module(.py)
include/:存放C++源碼對應(yīng)的頭文件
scripts/:存放可執(zhí)行腳本,例如shell腳本(.sh)、Python腳本(.py)
msg/:存放自定義格式的消息(.msg)
srv/:存放自定義格式的服務(wù)(.srv)
models/:存放機器人或仿真場景的3D模型(.sda, .stl, .dae等)
urdf/:存放機器人的模型描述(.urdf或.xacro)
launch/:存放launch文件(.launch或.xml)
通常ROS文件組織都是按照以上的形式,這是約定俗成的命名習(xí)慣,建議遵守。以上路徑中,只有CMakeLists.txt和package.xml是必須的,其余路徑根據(jù)軟件包是否需要來決定。
2.2 package的創(chuàng)建
創(chuàng)建一個package需要在catkin_ws/src下,用到ros2 pkg命令,用法是:
ros2 pkg create <package-name> --build-type {cmake,ament_cmake,ament_python} --dependencies <dependencies -name>
其中<package-name>是包名,--build-type 用來指定該包的編譯類型,一共有三個可選項ament_python、ament_cmake、cmake,--dependencies 指的是這個功能包的依賴,可以依賴多個軟件包。
創(chuàng)建ROS2的的C++包
ros2 pkg create --build-type ament_cmake <package_name>
創(chuàng)建ROS2的的python包
ros2 pkg create --build-type ament_python <package_name>
例如,新建一個package叫做test_pkg,依賴rclccpp(常用依賴)。
ros2 pkg create example_cpp --build-type ament_cmake --dependencies rclcpp
這樣就會在當(dāng)前路徑下新建test_pkg軟件包,包括:
├── CMakeLists.txt
├── include
│ └── test_pkg
├── package.xml
└── src
ros2 pkg完成了軟件包的初始化,填充好了CMakeLists.txt和package.xml,并且將依賴項填進(jìn)了這兩個文件中。
三、CMakeLists.txt文件
3.1 CMakeLists.txt作用
CMakeLists.txt原本是Cmake編譯系統(tǒng)的規(guī)則文件,而colcon編譯系統(tǒng)基本沿用了CMake的編譯風(fēng)格,只是針對ROS2工程添加了一些宏定義。所以在寫法上,colcon的CMakeLists.txt與CMake的基本一致。
這個文件直接規(guī)定了這個package要依賴哪些package,要編譯生成哪些目標(biāo),如何編譯等等流程。所以CMakeLists.txt非常重要,它指定了由源碼到目標(biāo)文件的規(guī)則,colcon編譯系統(tǒng)在工作時首先會找到每個package下的CMakeLists.txt,然后按照規(guī)則來編譯構(gòu)建。
3.2 CMakeLists.txt寫法
CMakeLists.txt的基本語法都還是按照CMake,而colcon在其中加入了少量的宏,總體的結(jié)構(gòu)如下:
cmake_minimum_required() #CMake的版本號
project() #項目名稱
find_package() #找到編譯需要的其他CMake/Catkin package
add_message_files() #新加宏,添加自定義Message/Service/Action文件
add_service_files()
add_action_files()
generate_message() #新加宏,生成不同語言版本的msg/srv/action接口
add_library() #生成庫
add_executable() #生成可執(zhí)行二進(jìn)制文件
add_dependencies() #定義目標(biāo)文件依賴于其他目標(biāo)文件,確保其他目標(biāo)已被構(gòu)建
ament_target_dependencies() #新加宏,它將使依賴項的庫、頭文件和自身的依賴項被正常找到
target_link_libraries() #鏈接
install() #安裝至本機
ament_package # 新加宏,安裝項目,在CMakeLists.txt文件中的最后一個調(diào)用。
3.3 CMakeLists例子
第一條語句指定了cmake的最低版本,第二條語句設(shè)定了構(gòu)建的功能包名稱。注意,這個名稱必須和package.xml中的名稱保持一致。
cmake_minimum_required(VERSION 3.5)
project(nav2_costmap_2d)
接下來,查找系統(tǒng)中的依賴項。另外在構(gòu)建庫或執(zhí)行文件時需要添加這些依賴項。
find_package(ament_cmake REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(laser_geometry REQUIRED)
find_package(map_msgs REQUIRED)
find_package(message_filters REQUIRED)
find_package(nav2_common REQUIRED)
find_package(nav2_msgs REQUIRED)
find_package(nav2_util REQUIRED)
find_package(nav2_voxel_grid REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(pluginlib REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(rmw REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(std_msgs REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(tf2_sensor_msgs REQUIRED)
find_package(visualization_msgs REQUIRED)
find_package(angles REQUIRED)
添加非ROS2功能包的依賴項時,需要將其對于的頭文件路徑在include_directories中寫明。而對于依賴項為ROS2功能包時,則無需此操作。
find_package(Eigen3 REQUIRED)
include_directories(
include
${EIGEN3_INCLUDE_DIRS}
)
add_library語句用于構(gòu)建庫??梢钥吹?,下面使用了兩個不同的語句來添加依賴。ament_target_dependencies是官方推薦的方式去添加依賴項。它將使依賴項的庫、頭文件和自身的依賴項被正常找到。
通常來說,若依賴項為ROS2功能包時,則使用ament_target_dependencies。若功能包有多個庫,它也將一并包含。
target_link_libraries添加依賴項目時需寫明具體庫的名稱。也就是說,添加的每一條都是一個庫。比如下面的nav2_costmap_2d_core就是添加了libnav2_costmap_2d_core.so庫。
add_executable用于構(gòu)建執(zhí)行文件。
add_library(nav2_costmap_2d_core SHARED
src/array_parser.cpp
src/costmap_2d.cpp
src/layer.cpp
src/layered_costmap.cpp
src/costmap_2d_ros.cpp
src/costmap_2d_publisher.cpp
src/costmap_math.cpp
src/footprint.cpp
src/costmap_layer.cpp
src/observation_buffer.cpp
src/clear_costmap_service.cpp
src/footprint_collision_checker.cpp
src/costmap_collision_checker.cpp
src/costmap_collision_checker_ros.cpp
plugins/costmap_filters/costmap_filter.cpp
)
set(dependencies
geometry_msgs
laser_geometry
map_msgs
message_filters
nav2_msgs
nav2_util
nav2_voxel_grid
nav_msgs
pluginlib
rclcpp
rclcpp_lifecycle
sensor_msgs
std_msgs
tf2
tf2_geometry_msgs
tf2_ros
tf2_sensor_msgs
visualization_msgs
angles
)
ament_target_dependencies(nav2_costmap_2d_core
${dependencies}
)
add_library(layers SHARED
plugins/inflation_layer.cpp
plugins/static_layer.cpp
plugins/obstacle_layer.cpp
src/observation_buffer.cpp
plugins/voxel_layer.cpp
plugins/range_sensor_layer.cpp
)
ament_target_dependencies(layers
${dependencies}
)
target_link_libraries(layers
nav2_costmap_2d_core
)
add_library(filters SHARED
plugins/costmap_filters/keepout_filter.cpp
plugins/costmap_filters/speed_filter.cpp
)
ament_target_dependencies(filters
${dependencies}
)
target_link_libraries(filters
nav2_costmap_2d_core
)
add_library(nav2_costmap_2d_client SHARED
src/footprint_subscriber.cpp
src/costmap_subscriber.cpp
src/costmap_topic_collision_checker.cpp
)
ament_target_dependencies(nav2_costmap_2d_client
${dependencies}
)
target_link_libraries(nav2_costmap_2d_client
nav2_costmap_2d_core
)
add_executable(nav2_costmap_2d_markers src/costmap_2d_markers.cpp)
target_link_libraries(nav2_costmap_2d_markers
nav2_costmap_2d_core
)
ament_target_dependencies(nav2_costmap_2d_markers
${dependencies}
)
add_executable(nav2_costmap_2d_cloud src/costmap_2d_cloud.cpp)
target_link_libraries(nav2_costmap_2d_cloud
nav2_costmap_2d_core
)
add_executable(nav2_costmap_2d src/costmap_2d_node.cpp)
ament_target_dependencies(nav2_costmap_2d
${dependencies}
)
target_link_libraries(nav2_costmap_2d
nav2_costmap_2d_core
layers
filters
)
最后是安裝庫、可執(zhí)行文件和其它文件的語句,它將在install/nav2_costmap_2d/lib安裝庫文件,install/nav2_costmap_2d/lib/nav2_costmap_2d安裝可執(zhí)行文件,把頭文件、launch文件和參數(shù)文件安裝在/share/${PROJECT_NAME}下。
install(TARGETS
nav2_costmap_2d_core
layers
filters
nav2_costmap_2d_client
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(TARGETS
nav2_costmap_2d
nav2_costmap_2d_markers
nav2_costmap_2d_cloud
RUNTIME DESTINATION lib/${PROJECT_NAME}
)
install(FILES costmap_plugins.xml
DESTINATION share/${PROJECT_NAME}
)
install(DIRECTORY include/
DESTINATION include/
)
install(
DIRECTORY include launch params
DESTINATION share/${PROJECT_NAME}
)
其效果如下:


如果希望其它的功能包能鏈接到這些庫,必須使用下面語句聲明這些功能包的頭文件和這些庫位置,以便其他功能包要依賴這些功能包時能順利找到對應(yīng)的頭文件和庫文件。同時要使用ament_export_dependencies命令,該命令會將依賴項導(dǎo)出到下游軟件包。這樣該庫使用者也就不必為那些依賴項調(diào)用find_package了。
ament_export_include_directories(include)
ament_export_libraries(layers filters nav2_costmap_2d_core nav2_costmap_2d_client)
ament_export_dependencies(${dependencies})
聲明好后,其他功能包要鏈接這些庫時只需使用下面兩條命令即可!
find_package(nav2_costmap_2d REQUIRED)
ament_target_dependencies(example_node
nav2_costmap_2d
)
若要該庫可以被其它工作空間(不在同一個工作空間下)的功能包使用,也可以將這些庫作為插件,可以導(dǎo)出插件的描述文件,以便其它工作空間的包可以調(diào)用pluginlib::ClassLoader類可以找到這些包的插件。
pluginlib_export_plugin_description_file(nav2_costmap_2d costmap_plugins.xml)
最后,項目安裝是通過ament_package()完成的,并且每個軟件包必須恰好執(zhí)行一次這個調(diào)用。ament_package()會安裝package.xml文件,用ament索引注冊該軟件包,并安裝CMake的配置(和可能的目標(biāo))文件,以便其他軟件包可以用find_package找到該軟件包。由于ament_package()會從CMakeLists.txt文件中收集大量信息,因此它應(yīng)該是CMakeLists.txt文件中的最后一個調(diào)用。
ament_package()
四、package.xml文件
package.xml也是一個colcon的package必備文件,它是這個軟件包的描述文件,用于描述pacakge的基本信息。
4.1 package.xml作用
pacakge.xml包含了package的名稱、版本號、內(nèi)容描述、維護(hù)人員、軟件許可、編譯構(gòu)建工具、編譯依賴、運行依賴等信息。
實際上ros2 pkg find、rosdep等命令之所以能快速定位和分析出package的依賴項信息,就是直接讀取了每一個pacakge中的package.xml文件。它為用戶提供了快速了解一個pacakge的渠道。
4.2 package.xml寫法
pacakge.xml遵循xml標(biāo)簽文本的寫法,由于版本更迭原因,現(xiàn)在有兩種格式并存(format1與format2),不過區(qū)別不大。老版本(format1)的pacakge.xml通常包含以下標(biāo)簽:
<pacakge> 根標(biāo)記文件
<name> 包名
<version> 版本號
<description> 內(nèi)容描述
<maintainer> 維護(hù)者
<license> 軟件許可證
<buildtool_depend> 編譯構(gòu)建工具,通常為catkin
<build_depend> 編譯依賴項,與Catkin中的
<run_depend> 運行依賴項
其中1-6為必備標(biāo)簽,1是根標(biāo)簽,嵌套了其余的所有標(biāo)簽,2-6為包的各種屬性,7-9為編譯相關(guān)信息。
在新版本(format2)中,包含的標(biāo)簽為:
<pacakge> 根標(biāo)記文件
<name> 包名
<version> 版本號
<description> 內(nèi)容描述
<maintainer> 維護(hù)者
<license> 軟件許可證
<buildtool_depend> 編譯構(gòu)建工具,通常為catkin
<depend> 指定依賴項為編譯、導(dǎo)出、運行需要的依賴,最常用
<build_depend> 編譯依賴項
<build_export_depend> 導(dǎo)出依賴項
<exec_depend> 運行依賴項
<test_depend> 測試用例依賴項
<doc_depend> 文檔依賴項
由此看見新版本的pacakge.xml格式上增加了 <depend> 、<build_export_depend> 、<exec_depend> 、<test_depend>和 <doc_depend>,相當(dāng)于將之前的build和run依賴項描述進(jìn)行了細(xì)分。
目前ROS和ROS2都同時支持兩種版本的package.xml,所以無論選哪種格式都可以。
4.3 pacakge.xml例子
以turtlesim軟件包為例,其pacakge.xml文件內(nèi)容如下
<?xml version="0.0"?>
<package format="2">
<name>turtlesim</name>
<version>0.0.0</version>
<description>turtlesim ros2 package.</description>
<maintainer email="1-lion@163.com">sukha</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>geometry_msgs</depend>
<depend>libqt4-dev</depend>
<build_depend>qt4-qmake</build_depend>
<build_depend>rmw_implementation_cmake</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
本示例為新版本的pacakge.xml,pacakge為根標(biāo)簽,寫在最外面,其中format屬性為2,表示是新版本的格式,編譯工具為ament_cmake,用depend來整合build_depend和run_depend,build_depend標(biāo)簽未變,run_depend要改為exec_depend,同時export該包為ament_cmake構(gòu)建類型,表明是一個C++類型的功能包。
五、其它常見文件類型
在ROS2的pacakge中,還有其他許多常見的文件類型,這里做個總結(jié)。
5.1 launch文件
ROS2的launch文件有三種,分別是python、xml、yaml,其中ROS2官方推薦的時python方式編寫launch文件。
它對ROS需要運行程序進(jìn)行了打包,通過一句命令來啟動。一般launch文件中會指定要啟動哪些package下的哪些可執(zhí)行程序,指定以什么參數(shù)啟動,以及一些管理控制的命令。 launch文件通常放在軟件包的launch/路徑中中。
5.2 msg/srv/action文件
ROS2程序中有可能有一些自定義的消息/服務(wù)/動作文件,為程序的發(fā)者所設(shè)計的數(shù)據(jù)結(jié)構(gòu),這類的文件以.msg,.srv,.action結(jié)尾,通常放在package的msg/,srv/,action/路徑下。
5.3 urdf/xacro文件
urdf/xacro文件是機器人模型的描述文件,以.urdf或.xacro結(jié)尾。它定義了機器人的連桿和關(guān)節(jié)的信息,以及它們之間的位置、角度等信息,通過urdf文件可以將機器人的物理連接信息表示出來。并在可視化調(diào)試和仿真中顯示。
5.4 yaml文件
yaml文件一般存儲了ROS2需要加載的參數(shù)信息,一些屬性的配置。通常在launch文件或程序中讀取.yaml文件,把參數(shù)加載到參數(shù)服務(wù)器上。通常我們會把yaml文件存放在param/路徑下
5.5 dae/stl文件
dae或stl文件是3D模型文件,機器人的urdf或仿真環(huán)境通常會引用這類文件,它們描述了機器人的三維模型。相比urdf文件簡單定義的性狀,dae/stl文件可以定義復(fù)雜的模型,可以直接從solidworks或其他建模軟件導(dǎo)出機器人裝配模型,從而顯示出更加精確的外形。
5.6 rviz2文件
rviz2文件本質(zhì)上是固定格式的文本文件,其中存儲了RViz2窗口的配置(顯示哪些控件、視角、參數(shù))。通常rviz2文件不需要我們?nèi)ナ謩有薷?,而是直接在RViz2工具里保存,下次運行時直接讀取。
參考:
https://sychaichangkun.gitbooks.io/ros-tutorial-icourse163/content/chapter2/2.4.html
https://www.guyuehome.com/38615