一、文件整体结构
text
cmake_minimum_required(VERSION 3.22) ← 指定最低 CMake 版本
project(moveit_servo) ← 项目名称
【变量定义区】→ 定义所有目标名称
【依赖查找区】→ 查找所有依赖包
【库构建区】 → 构建核心库
【组件节点区】→ 构建 ROS2 组件节点
【可执行文件区】→ 构建独立节点
【安装配置区】→ 安装到工作空间
【测试区】 → 单元测试和集成测试
二、变量定义区(第3-25行)
cmake
# 库名称
set(SERVO_LIB_NAME moveit_servo_lib) # 核心库
set(SERVO_PARAM_LIB_NAME ${SERVO_LIB_NAME}_parameters) # 参数库
set(POSE_TRACKING pose_tracking) # 位姿追踪库
# 组件节点(共享库)
set(SERVO_COMPONENT_NODE servo_node) # 主服务节点
set(SERVO_CONTROLLER_INPUT servo_controller_input) # 手柄输入节点
# 可执行节点
set(SERVO_NODE_MAIN_NAME servo_node_main) # 主节点入口
set(POSE_TRACKING_DEMO_NAME servo_pose_tracking_demo) # 位姿追踪演示
set(FAKE_SERVO_CMDS_NAME fake_command_publisher) # 模拟命令发布器
设计意图:
-
使用变量集中管理所有目标名称,便于维护
-
避免在多处硬编码相同的字符串,降低拼写错误风险
-
当需要重命名时,只需修改变量定义处
三、依赖查找区(第32-53行)
3.1 引入 MoveIt 公共配置
cmake
find_package(moveit_common REQUIRED)
moveit_package()
-
moveit_common是 MoveIt 的公共 CMake 模块 -
moveit_package()是自定义宏,设置 MoveIt 包的通用编译选项
3.2 定义并查找所有依赖
cmake
set(THIS_PACKAGE_INCLUDE_DEPENDS
control_msgs # 控制消息
control_toolbox # 控制工具
geometry_msgs # 几何消息 (Twist, Pose)
moveit_core # MoveIt 核心库
moveit_msgs # MoveIt 消息
moveit_ros_planning # MoveIt 规划相关
pluginlib # 插件加载库
rclcpp # ROS2 C++客户端库
rclcpp_components # 组件节点支持
sensor_msgs # 传感器消息
std_msgs # 标准消息
std_srvs # 标准服务
tf2_eigen # TF2与Eigen转换
trajectory_msgs # 轨迹消息
)
# 循环查找每个依赖
foreach(Dependency IN ITEMS ${THIS_PACKAGE_INCLUDE_DEPENDS})
find_package(${Dependency} REQUIRED)
endforeach()
# 额外的依赖
find_package(eigen3_cmake_module REQUIRED)
find_package(Eigen3 REQUIRED)
为什么要分开写?
-
集中管理依赖列表,方便增删
-
避免重复的
find_package调用 -
ament_target_dependencies在后续会一次性链接所有这些依赖
四、库构建区(第59-87行)
4.1 参数库 (SERVO_PARAM_LIB_NAME)
cmake
add_library(${SERVO_PARAM_LIB_NAME} SHARED
src/servo_parameters.cpp
src/parameter_descriptor_builder.cpp
)
ament_target_dependencies(${SERVO_PARAM_LIB_NAME} ${THIS_PACKAGE_INCLUDE_DEPENDS})
-
负责加载和管理 Servo 的配置参数
-
独立出来以便其他节点也能加载相同的参数
4.2 核心库 (SERVO_LIB_NAME)
cmake
add_library(${SERVO_LIB_NAME} SHARED
src/collision_check.cpp # 碰撞检测
src/enforce_limits.cpp # 限位约束
src/servo.cpp # 核心逻辑
src/servo_calcs.cpp # 运动学计算
src/utilities.cpp # 工具函数
)
target_link_libraries(${SERVO_LIB_NAME} ${SERVO_PARAM_LIB_NAME}) # 链接参数库
-
核心计算库,不依赖 ROS2 通信
-
可以独立测试和复用
4.3 位姿追踪库 (POSE_TRACKING)
cmake
add_library(${POSE_TRACKING} SHARED src/pose_tracking.cpp)
target_link_libraries(${POSE_TRACKING} ${SERVO_LIB_NAME})
-
实现位姿追踪功能
-
链接到核心库,复用计算能力
依赖传递关系
text
SERVO_PARAM_LIB_NAME (参数)
↓
SERVO_LIB_NAME (核心计算)
↓
POSE_TRACKING (位姿追踪)
五、组件节点区(第89-99行)
ROS2 组件节点是可动态加载的共享库,可以在运行时加载到容器进程中。
5.1 主 Servo 节点
cmake
add_library(${SERVO_COMPONENT_NODE} SHARED src/servo_node.cpp)
target_link_libraries(${SERVO_COMPONENT_NODE} ${SERVO_LIB_NAME})
rclcpp_components_register_nodes(${SERVO_COMPONENT_NODE} "moveit_servo::ServoNode")
-
rclcpp_components_register_nodes将节点注册为组件 -
可以在 launch 文件中用
ComposableNode加载
5.2 手柄输入节点
cmake
add_library(${SERVO_CONTROLLER_INPUT} SHARED src/teleop_demo/joystick_servo_example.cpp)
rclcpp_components_register_nodes(${SERVO_CONTROLLER_INPUT} "moveit_servo::JoyToServoPub")
-
处理游戏手柄输入
-
这里就是之前讨论的
joy包依赖处
六、可执行文件区(第101-119行)
6.1 主节点入口
cmake
add_executable(${SERVO_NODE_MAIN_NAME} src/servo_node_main.cpp)
target_link_libraries(${SERVO_NODE_MAIN_NAME} ${SERVO_COMPONENT_NODE})
-
这是一个独立的可执行文件
-
直接链接组件库,本质上和加载组件效果相同
6.2 演示程序
cmake
add_executable(${POSE_TRACKING_DEMO_NAME} src/cpp_interface_demo/pose_tracking_demo.cpp)
target_link_libraries(${POSE_TRACKING_DEMO_NAME} ${POSE_TRACKING})
- C++接口演示,不依赖 ROS2 通信
6.3 模拟命令发布器
cmake
add_executable(${FAKE_SERVO_CMDS_NAME} test/publish_fake_jog_commands.cpp)
ament_target_dependencies(${FAKE_SERVO_CMDS_NAME}
rclcpp
geometry_msgs
std_srvs
)
-
仅依赖少量包,用于测试
-
注意这里没有 列出
THIS_PACKAGE_INCLUDE_DEPENDS,而是单独指定
七、安装配置区(第121-149行)
7.1 安装库文件
cmake
install(
TARGETS
${SERVO_LIB_NAME}
${SERVO_LIB_NAME}_parameters
${POSE_TRACKING}
${SERVO_COMPONENT_NODE}
${SERVO_CONTROLLER_INPUT}
EXPORT export_${PROJECT_NAME} # 导出用于其他包
LIBRARY DESTINATION lib # 安装到 lib/
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
7.2 安装可执行文件
cmake
install(
TARGETS
${SERVO_NODE_MAIN_NAME}
${CPP_DEMO_NAME} # 注意:这个变量未在开头定义!
${POSE_TRACKING_DEMO_NAME}
${FAKE_SERVO_CMDS_NAME}
RUNTIME DESTINATION lib/${PROJECT_NAME} # 安装到 lib/moveit_servo/
)
⚠️ 注意 :${CPP_DEMO_NAME} 变量在开头没有定义,这是一个 CMake 警告,可能是开发者忘记删除。
7.3 安装目录
cmake
install(DIRECTORY include/ DESTINATION include)
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
install(DIRECTORY config DESTINATION share/${PROJECT_NAME})
-
头文件:
include/→install/include/moveit_servo/ -
launch 文件:
launch/→install/share/moveit_servo/launch/ -
config 文件:
config/→install/share/moveit_servo/config/
7.4 导出配置
cmake
ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET)
ament_export_dependencies(${THIS_PACKAGE_INCLUDE_DEPENDS})
-
其他包可以通过
find_package(moveit_servo)找到这个包 -
自动传递依赖关系
八、测试区(第151-194行)
8.1 禁用部分 Lint 测试
cmake
set(ament_cmake_copyright_FOUND TRUE)
set(ament_cmake_cpplint_FOUND TRUE)
set(ament_cmake_flake8_FOUND TRUE)
set(ament_cmake_uncrustify_FOUND TRUE)
-
强制让这些测试通过(即使代码不完美)
-
常见于大型项目,避免测试阻塞 CI
8.2 三种测试类型
| 测试名 | 类型 | 说明 |
|---|---|---|
test_servo_integration |
集成测试 | 测试 Servo 完整功能 |
test_servo_collision |
集成测试 | 测试碰撞检测 |
test_servo_pose_tracking |
集成测试 | 测试位姿追踪 |
servo_calcs_unit_tests |
单元测试 | 测试计算函数 |
cmake
# 集成测试示例
ament_add_gtest_executable(test_servo_integration
test/test_servo_interface.cpp
test/servo_launch_test_common.hpp
)
target_link_libraries(test_servo_integration ${SERVO_PARAM_LIB_NAME})
add_ros_test(test/launch/test_servo_integration.test.py TIMEOUT 120 ...)
-
add_ros_test是 ROS2 扩展,可以启动 ROS 节点进行测试 -
TIMEOUT 120限制测试最长运行时间
九、设计模式总结
| 设计模式 | 体现 |
|---|---|
| 分层架构 | 核心库 → 组件节点 → 可执行文件 |
| 组件化 | 使用 rclcpp_components 支持动态加载 |
| 依赖管理 | 集中定义 THIS_PACKAGE_INCLUDE_DEPENDS |
| 可测试性 | 核心库与ROS解耦,支持纯单元测试 |
| 可复用性 | 导出 export_${PROJECT_NAME} 供其他包使用 |
十、潜在问题
-
${CPP_DEMO_NAME}未定义:在 install 中使用了未声明的变量 -
joy包未声明 :虽然joystick_servo_example.cpp依赖/joy话题,但 CMake 中未显式声明(因为话题不强制编译依赖)
改进建议:
cmake
# 在依赖列表中添加 joy(如果需要编译依赖)
set(THIS_PACKAGE_INCLUDE_DEPENDS
...
joy # 添加这一行
)