ROS2学习之文件结构

从零开始学习ROS2,作为一个小白有各种奇怪的问题。主要还是对整个项目的构建过程不太了解。我学习的资源主要来自B站赵虚左老师以及鱼香ROS。下面是一些学习过程中遇到的关于文件结构和配置相关的疑惑:

1.ros2项目里面的文件结构以及对应的功能是什么?

1.1 首先以一个经典的项目目录举例(一个功能包,一个节点):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ros2_ws/
├── src/
│ ├── my_package/
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/
│ │ │ └── my_node.cpp
│ │ ├── launch/
│ │ │ └── my_launch.launch.py
│ │ ├── msg/
│ │ │ └── MyMessage.msg
│ │ ├── srv/
│ │ │ └── MyService.srv
│ │ ├── config/
│ │ │ └── params.yaml
│ │ └── tests/
│ │ └── test_my_node.cpp
----my_package2
├── build/
├── install/
└── log/

1.2 实际开发过程中,要实现整体机器的运转,需要多个功能包,每个功能包下面可能包含一个或者多个节点:(以自动避开障碍物的小车为例子:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
ros2_ws/ # 工作空间
├── src/ # 源代码目录
│ ├── sensor_interface/ # 传感器相关功能包
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/
│ │ │ ├── lidar_node.cpp # 激光雷达数据处理
│ │ │ ├── camera_node.cpp # 摄像头数据处理
│ │ │ └── imu_node.cpp # IMU 数据处理
│ │ ├── launch/
│ │ │ └── sensor_launch.py # 传感器启动文件
│ │ ├── config/
│ │ │ └── params.yaml # 传感器参数配置文件
│ │ └── urdf/ # (如果需要)传感器模型描述文件

│ ├── perception/ # 感知功能包
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/
│ │ │ ├── obstacle_detection_node.cpp # 障碍物检测
│ │ │ └── obstacle_tracker_node.cpp # 障碍物追踪
│ │ ├── launch/
│ │ │ └── perception_launch.py # 整合感知功能节点启动
│ │ └── config/
│ │ └── detection_params.yaml # 障碍物检测参数

│ ├── planning/ # 路径规划功能包
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/
│ │ │ ├── local_planner_node.cpp # 本地路径规划
│ │ │ └── global_planner_node.cpp # 全局路径规划(可选)
│ │ ├── launch/
│ │ │ └── planning_launch.py # 路径规划启动文件
│ │ └── config/
│ │ └── planner_params.yaml # 路径规划参数

│ ├── control/ # 小车运动控制功能包
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── src/
│ │ │ ├── speed_controller_node.cpp # 速度控制
│ │ │ ├── steering_controller_node.cpp# 转向控制
│ │ └── launch/
│ │ └── control_launch.py # 控制节点启动文件

│ ├── simulation/ # 仿真相关功能包(可选)
│ │ ├── package.xml
│ │ ├── CMakeLists.txt
│ │ ├── worlds/
│ │ │ └── test_world.world # 仿真环境场景
│ │ ├── launch/
│ │ │ └── gazebo_launch.py # Gazebo 仿真启动
│ │ ├── models/
│ │ │ ├── car_description.urdf.xacro # 小车的 URDF 模型
│ │ │ └── lidar_sensor.urdf.xacro # 激光雷达模型
│ │ └── src/
│ │ └── sim_bridge_node.cpp # ROS 与仿真环境交互节点

│ └── bringup/ # 系统整合功能包
│ ├── package.xml
│ ├── CMakeLists.txt
│ ├── launch/
│ │ ├── all_system_launch.py # 启动整个系统的文件
│ │ ├── rviz_launch.py # 启动 RViz 可视化界面
│ └── config/
│ └── rviz_config.rviz # RViz 配置文件
├── build/ # 构建目录(colcon 自动生成)
├── install/ # 安装目录(构建后生成)
└── log/ # 日志目录(colcon 自动生成)

2.如何从零构建一个ros2项目?

  • 1.创建工作空间:手动创建工作空间以及对应的代码目录srcmkdir -p work_space/src

进入src,创建功能包ros2 pkg create --build-type ament_cmake base_interfaces_demo,其中(--build-type ament_cmake 这个是默认的,也可以不写。base_interface_demo是功能包的名字)

  • 2.创建功能包:进入src目录里面的base_interfaces_demo功能包里面创建节点:(节点可以使用python也可以使用cpp,但是里面的接口只能使用cpp来创建,什么是接口呢?下面会写)

    • 基于cppros2 pkg create cpp_01_topic --build-type ament_cmake --dependencies rclcpp std_msgs base_interfaces_demo 这里写这个base_interfaces_demo就是将这个认为成依赖

    • 基于pythonros2 pkg create py01_topic --build-type ament_python --dependencies rclpy std_msgs base_interfaces_demo

    1. 编辑配置文件
    1. 编译:colcon build --package-select py01_topic(选择编译哪一个功能包)
    1. 重新设置路径: . install/setup.bash
    1. 运行节点:ros2 run py01_topic demo01_talker_str_py这里run的参数分别是功能包名称和节点名称

(c++功能包的构建信息主要在package.xml以及CMakeLists.txt中,而python功能包的构建信息主要包含在packages.xml以及setup.py中)

3.项目里面涉及到了cpp的使用,那么里面的CmakeList.txt包含的内容是什么意思,xml文件是如何去配置的?

  1. CMakeList.txt的一个例子:首先这个文件的作用是什么?

  2. 定义构建规则CMakeLists.txt 告诉 CMake 如何编译和链接代码,生成可执行文件或库。

  3. 管理依赖项:它会声明当前项目所依赖的其他库或包(如 ROS 的核心库)。

  4. 配置测试:如果项目包含测试代码,CMakeLists.txt 也会定义如何运行测试。

  5. 设置编译选项:可以通过它设置编译器选项(如警告级别、优化选项等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cmake_minimum_required(VERSION 3.8) # 作用:指定 CMake 的最低版本要求。这里要求 CMake 的版本至少为 3.8
project(base_interfaces_demo) # 定义项目的名称,这里是 base_interfaces_demo(和packages.xml里面的保持一致)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 作用:为 GCC 或 Clang 编译器添加编译选项。
-Wall:启用所有常见的警告。
-Wextra:启用额外的警告。
-Wpedantic:启用严格的标准兼容性警告。


# find dependencies
find_package(ament_cmake REQUIRED) 查找并加载 ament_cmake 包,这是 ROS 2 的核心构建工具之一。
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED) 如果项目依赖于 rclcpp,可以添加 find_package(<rclcpp> REQUIRED)

if(BUILD_TESTING) # 如果启用了测试(BUILD_TESTING 为 ON),则查找并加载 ament_lint_auto 包
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()

ament_package() # 标记当前项目为一个 ROS 2


关于xml文件,下面展示一个基础的常用的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
----------------这个是调用了自定义接口的功能包
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>py01_topic</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="tyfelix@todo.todo">tyfelix</maintainer>
<license>TODO: License declaration</license>

<!-- 依赖 -->
<depend>rclpy</depend>
<depend>std_msgs</depend>
<depend>base_interfaces_demo</depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>


---------------------------这个是自定义接口的功能包
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>base_interfaces_demo</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="tyfelix@todo.todo">tyfelix</maintainer>
<license>TODO: License declaration</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<!-- 自定义接口 --> 因为这个地方需要去生成功能包,所以这样写的
<!-- 编译依赖 -->
<build_depend>rosidl_default_generators</build_depend>
<!-- 执行依赖 -->
<exec_depend>rosidl_default_runtime</exec_depend>
<!-- 声明当前包所属的功能包组 -->
<member_of_group>rosidl_interface_packages</member_of_group>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>


描述:
1.根标签
<package>:该标签为整个 xml 文件的根标签,format 属性用来声明文件的格式版本。
2.元信息标签
<name>:包名;
<version>:包的版本号;
<description>:包的描述信息;
<maintainer>:维护者信息;
<license>:软件协议;
<url>:包的介绍网址;
<author>:包的作者信息。
3.依赖项
<buildtool_depend>:声明编译工具依赖;
<build_depend>:声明编译依赖;
<build_export_depend>:声明根据此包构建库所需依赖;
<exec_depend>:声明执行时依赖;
<depend>:相当于<build_depend><build_export_depend><exec_depend>三者的集成;(相当于直接调用就写一个,如果是自己创建的接口,那么需要指明使用三个)
<test_depend>:声明测试依赖;
<doc_depend>:声明构建文档依赖。

对于python功能包,描述如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from setuptools import setup
package_name = 'pkg02_helloworld_py'
setup(
name=package_name, # 包名
version='0.0.0', # 版本
packages=[package_name], # 功能包列表
data_files=[ #需要被安装的文件以及安装路径
猛狮集训营
42
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'], # 安装依赖
zip_safe=True,
maintainer='ros2', # 维护者
maintainer_email='ros2@todo.todo', # 维护者 email
description='TODO: Package description', # 包描述
license='TODO: License declaration', # 软件协议
tests_require=['pytest'], # 测试依赖
entry_points={
'console_scripts': [
# 映射源文件与可执行文件
'helloworld = pkg02_helloworld_py.helloworld:main'
],
},
)