【ROS2概念】系列(八)——ROS2文件系統(tǒng)

目錄

一、工作空間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

ROS2工作空間的文件結(jié)構(gòu)
  • 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}
)

其效果如下:

庫文件
可執(zhí)行文件

如果希望其它的功能包能鏈接到這些庫,必須使用下面語句聲明這些功能包的頭文件和這些庫位置,以便其他功能包要依賴這些功能包時能順利找到對應(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容