目录
- 一、工作空间workspace
- 1.1 初始化工作空间
- 1.2 文件结构介绍
- 二、软件包package
- 2.1 package结构
- 2.2 package的创建
- 三、CMakeLists.txt文件
- 3.1 CMakeLists.txt作用
- 3.2 CMakeLists.txt写法
- 3.3 CMakeLists例子
- 四、package.xml文件
- 4.1 package.xml作用
- 4.2 package.xml写法
- 4.3 pacakge.xml例子
- 五、其它常见文件类型
- 5.1 launch文件
- 5.2 msg/srv/action文件
- 5.3 urdf/xacro文件
- 5.4 yaml文件
- 5.5 dae/stl文件
- 5.6 rviz2文件
一、工作空间workspace
1.1 初始化工作空间
mkdir -p ~/dev_ws/src
1.2 文件结构介绍
ROS2工作空间的文件结构由四个目录组成,分别是build/
、install/
、src/
、log/
,其中src/
目录下存放最小ROS2构建部件成为功能包package
。
- src:代码空间,未来编写的代码、脚本,都需要人为的放置到这里;
- build:编译空间,保存编译过程中产生的中间文件;
- install:安装空间,放置编译得到的可执行文件和脚本;
- log:日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。
总体来讲,这四个空间的文件夹,我们绝大部分操作都是在src中进行的,编译成功后,就会执行install里边的结果,build和log两个文件夹用的很少。
二、软件包package
任何ROS2的代码无论是C++还是Python都要放到package中,这样才能正常的编译和运行。
一个package可以编译出来多个目标文件(可执行程序、动态静态库、头文件等等)。
2.1 package结构
一个package下常见的文件路径有:
├── CMakeLists.txt #package的编译规则(必须)
├── package.xml #package的描述信息(必须)
├── src/ #源代码文件
├── include/ #C++头文件
├── scripts/ #可执行脚本
├── msg/ #自定义消息
├── srv/ #自定义服务
├── models/ #3D模型文件
├── urdf/ #urdf文件
├── launch/ #launch文件
其中定义package的是CMakeLists.txt和package.xml,这两个文件是package中必不可少的。colcon编译系统在编译前,首先就要解析这两个文件。这两个文件就定义了一个package。
CMakeLists.txt:定义package的包名、依赖、源文件、目标文件等编译规则,是package不可少的成分
package.xml:描述package的包名、版本号、作者、依赖等信息,是package不可少的成分
src/:存放ROS的源代码,包括C++的源码和(.cpp)以及Python的module(.py)
include/:存放C++源码对应的头文件
scripts/:存放可执行脚本,例如shell脚本(.sh)、Python脚本(.py)
msg/:存放自定义格式的消息(.msg)
srv/:存放自定义格式的服务(.srv)
models/:存放机器人或仿真场景的3D模型(.sda, .stl, .dae等)
urdf/:存放机器人的模型描述(.urdf或.xacro)
launch/:存放launch文件(.launch或.xml)
通常ROS文件组织都是按照以上的形式,这是约定俗成的命名习惯,建议遵守。以上路径中,只有CMakeLists.txt和package.xml是必须的,其余路径根据软件包是否需要来决定。
2.2 package的创建
创建一个package需要在catkin_ws/src下,用到ros2 pkg
命令,用法是:
ros2 pkg create
其中
创建ROS2的的C++包
ros2 pkg create --build-type ament_cmake
创建ROS2的的python包
ros2 pkg create --build-type ament_python
例如,新建一个package叫做test_pkg,依赖rclccpp(常用依赖)。
ros2 pkg create example_cpp --build-type ament_cmake --dependencies rclcpp
这样就会在当前路径下新建test_pkg软件包,包括:
├── CMakeLists.txt
├── include
│ └── test_pkg
├── package.xml
└── src
ros2 pkg完成了软件包的初始化,填充好了CMakeLists.txt和package.xml,并且将依赖项填进了这两个文件中。
三、CMakeLists.txt文件
3.1 CMakeLists.txt作用
CMakeLists.txt原本是Cmake编译系统的规则文件,而colcon编译系统基本沿用了CMake的编译风格,只是针对ROS2工程添加了一些宏定义。所以在写法上,colcon的CMakeLists.txt与CMake的基本一致。
这个文件直接规定了这个package要依赖哪些package,要编译生成哪些目标,如何编译等等流程。所以CMakeLists.txt非常重要,它指定了由源码到目标文件的规则,colcon编译系统在工作时首先会找到每个package下的CMakeLists.txt,然后按照规则来编译构建。
3.2 CMakeLists.txt写法
CMakeLists.txt的基本语法都还是按照CMake,而colcon在其中加入了少量的宏,总体的结构如下:
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() #生成可执行二进制文件
add_dependencies() #定义目标文件依赖于其他目标文件,确保其他目标已被构建
ament_target_dependencies() #新加宏,它将使依赖项的库、头文件和自身的依赖项被正常找到
target_link_libraries() #链接
install() #安装至本机
ament_package # 新加宏,安装项目,在CMakeLists.txt文件中的最后一个调用。
3.3 CMakeLists例子
第一条语句指定了cmake的最低版本,第二条语句设定了构建的功能包名称。注意,这个名称必须和package.xml中的名称保持一致。
cmake_minimum_required(VERSION 3.5)
project(nav2_costmap_2d)
接下来,查找系统中的依赖项。另外在构建库或执行文件时需要添加这些依赖项。
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语句用于构建库。可以看到,下面使用了两个不同的语句来添加依赖。ament_target_dependencies是官方推荐的方式去添加依赖项。它将使依赖项的库、头文件和自身的依赖项被正常找到。
通常来说,若依赖项为ROS2功能包时,则使用ament_target_dependencies。若功能包有多个库,它也将一并包含。
target_link_libraries添加依赖项目时需写明具体库的名称。也就是说,添加的每一条都是一个库。比如下面的nav2_costmap_2d_core就是添加了libnav2_costmap_2d_core.so库。
add_executable用于构建执行文件。
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
)
最后是安装库、可执行文件和其它文件的语句,它将在install/nav2_costmap_2d/lib安装库文件,install/nav2_costmap_2d/lib/nav2_costmap_2d安装可执行文件,把头文件、launch文件和参数文件安装在/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}
)
其效果如下:
如果希望其它的功能包能链接到这些库,必须使用下面语句声明这些功能包的头文件和这些库位置,以便其他功能包要依赖这些功能包时能顺利找到对应的头文件和库文件。同时要使用ament_export_dependencies命令,该命令会将依赖项导出到下游软件包。这样该库使用者也就不必为那些依赖项调用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
)
若要该库可以被其它工作空间(不在同一个工作空间下)的功能包使用,也可以将这些库作为插件,可以导出插件的描述文件,以便其它工作空间的包可以调用pluginlib::ClassLoader类可以找到这些包的插件。
pluginlib_export_plugin_description_file(nav2_costmap_2d costmap_plugins.xml)
最后,项目安装是通过ament_package()完成的,并且每个软件包必须恰好执行一次这个调用。ament_package()会安装package.xml文件,用ament索引注册该软件包,并安装CMake的配置(和可能的目标)文件,以便其他软件包可以用find_package找到该软件包。由于ament_package()会从CMakeLists.txt文件中收集大量信息,因此它应该是CMakeLists.txt文件中的最后一个调用。
ament_package()
四、package.xml文件
package.xml也是一个colcon的package必备文件,它是这个软件包的描述文件,用于描述pacakge的基本信息。
4.1 package.xml作用
pacakge.xml包含了package的名称、版本号、内容描述、维护人员、软件许可、编译构建工具、编译依赖、运行依赖等信息。
实际上ros2 pkg find
、rosdep
等命令之所以能快速定位和分析出package的依赖项信息,就是直接读取了每一个pacakge中的package.xml文件。它为用户提供了快速了解一个pacakge的渠道。
4.2 package.xml写法
pacakge.xml遵循xml标签文本的写法,由于版本更迭原因,现在有两种格式并存(format1与format2),不过区别不大。老版本(format1)的pacakge.xml通常包含以下标签:
根标记文件
包名
版本号
内容描述
维护者
软件许可证
编译构建工具,通常为catkin
编译依赖项,与Catkin中的
运行依赖项
其中1-6为必备标签,1是根标签,嵌套了其余的所有标签,2-6为包的各种属性,7-9为编译相关信息。
在新版本(format2)中,包含的标签为:
根标记文件
包名
版本号
内容描述
维护者
软件许可证
编译构建工具,通常为catkin
指定依赖项为编译、导出、运行需要的依赖,最常用
编译依赖项
导出依赖项
运行依赖项
测试用例依赖项
文档依赖项
由此看见新版本的pacakge.xml格式上增加了
目前ROS和ROS2都同时支持两种版本的package.xml,所以无论选哪种格式都可以。
4.3 pacakge.xml例子
以turtlesim软件包为例,其pacakge.xml文件内容如下
turtlesim
0.0.0
turtlesim ros2 package.
sukha
Apache License 2.0
ament_cmake
rosidl_default_generators
rclcpp
std_msgs
geometry_msgs
libqt4-dev
qt4-qmake
rmw_implementation_cmake
rosidl_default_runtime
ament_cmake
本示例为新版本的pacakge.xml,pacakge为根标签,写在最外面,其中format属性为2,表示是新版本的格式,编译工具为ament_cmake,用depend来整合build_depend和run_depend,build_depend标签未变,run_depend要改为exec_depend,同时export该包为ament_cmake构建类型,表明是一个C++类型的功能包。
五、其它常见文件类型
在ROS2的pacakge中,还有其他许多常见的文件类型,这里做个总结。
5.1 launch文件
ROS2的launch文件有三种,分别是python、xml、yaml,其中ROS2官方推荐的时python方式编写launch文件。
它对ROS需要运行程序进行了打包,通过一句命令来启动。一般launch文件中会指定要启动哪些package下的哪些可执行程序,指定以什么参数启动,以及一些管理控制的命令。 launch文件通常放在软件包的launch/路径中中。
5.2 msg/srv/action文件
ROS2程序中有可能有一些自定义的消息/服务/动作文件,为程序的发者所设计的数据结构,这类的文件以.msg,.srv,.action结尾,通常放在package的msg/,srv/,action/路径下。
5.3 urdf/xacro文件
urdf/xacro文件是机器人模型的描述文件,以.urdf或.xacro结尾。它定义了机器人的连杆和关节的信息,以及它们之间的位置、角度等信息,通过urdf文件可以将机器人的物理连接信息表示出来。并在可视化调试和仿真中显示。
5.4 yaml文件
yaml文件一般存储了ROS2需要加载的参数信息,一些属性的配置。通常在launch文件或程序中读取.yaml文件,把参数加载到参数服务器上。通常我们会把yaml文件存放在param/路径下
5.5 dae/stl文件
dae或stl文件是3D模型文件,机器人的urdf或仿真环境通常会引用这类文件,它们描述了机器人的三维模型。相比urdf文件简单定义的性状,dae/stl文件可以定义复杂的模型,可以直接从solidworks或其他建模软件导出机器人装配模型,从而显示出更加精确的外形。
5.6 rviz2文件
rviz2文件本质上是固定格式的文本文件,其中存储了RViz2窗口的配置(显示哪些控件、视角、参数)。通常rviz2文件不需要我们去手动修改,而是直接在RViz2工具里保存,下次运行时直接读取。
参考:
https://sychaichangkun.gitbooks.io/ros-tutorial-icourse163/content/chapter2/2.4.html
https://www.guyuehome.com/38615