
简介本资源是面向ROS2初学者与机器人方向课程设计、毕业设计学生的水下机器人控制系统开发包聚焦水下作业场景下的建模、动力学仿真与推进器协同控制等核心问题。压缩包共59个文件涵盖22个Python节点与管理脚本如thrusterManager_node.py、narval_state_publisher_node.py、6个3D模型文件obj/dae格式用于Gazebo仿真、3个xacro宏定义构建URDF机器人描述、2个RViz配置文件可视化姿态与推进器状态及多组PWM/RPM标定数据支持T200推进器精确建模整体大小为10.47MB。已有90人学习下载适合开展水下机器人建模仿真、ROS2节点开发、推进器分配矩阵实现与真实硬件接口调试等实践任务。资源结构清晰含完整launch启动脚本、参数配置yaml、单元测试框架及详细README说明可直接用于课程大作业或毕业课题的快速原型验证与系统集成。1. 项目概述这不是一个普通压缩包而是一套面向真实水下作业场景的ROS2控制中枢“水下机器人ROS2控制包.zip”——光看名字很多人第一反应是“又一个教学Demo打包”点开解压后发现几个launch文件和一堆yaml配置就以为是照着教程跑通小车导航的复刻版。但真正做过水下机器人系统集成的人一眼就能看出区别这个包里没有/cmd_vel直连电机驱动的粗暴写法没有用gazebo_ros_control硬塞进仿真环境的临时方案更没有把nav2参数表直接拷贝过来改个话题名就交差。它从底层通信协议开始设计所有节点都默认启用rmw_cyclonedds_cpp中间件所有传感器数据流都经过sensor_msgs/msg/FluidPressure和geometry_msgs/msg/PointStamped的水下坐标系校准连tf2树里都多了一层base_link → pressure_frame → depth_sensor_frame的刚性偏移链。我去年在舟山渔港调试ROV时就因为没意识到水下重力补偿必须在controller_manager加载前完成导致PID控制器在5米水深就开始震荡。这个包最硬核的地方在于它把水下特有的物理约束——密度梯度引起的声速变化、水流扰动对姿态估计的耦合影响、高压密封舱带来的CAN总线信号衰减——全部转化成了可配置的ROS2参数组。比如hydrodynamic_damping_factor不是写死的0.8而是通过ros2 param set /controller_node hydrodynamic_damping_factor 0.73实时调节pressure_compensation_enabled开关一开底层imu_filter_madgwick节点会自动切换到基于静水压强的零偏修正模式。它解决的不是“能不能跑起来”的问题而是“在真实海况下能不能稳住姿态、能不能精准悬停、能不能抗住3节横流持续作业4小时”的工程级痛点。适合两类人深度参考一是正在做AUV/ROV产品化落地的嵌入式工程师需要直接复用其ros2_control硬件接口层二是高校课题组做水下SLAM或路径规划算法的研究生它的urdf模型里预置了真实水下推进器的推力-转速非线性映射表比Gazebo里理想化的effort_controllers靠谱得多。2. 核心架构设计与技术选型逻辑2.1 为什么放弃ROS1而坚定选择ROS2 Humble LTS版本很多团队还在纠结“ROS1够用为啥要换”但在水下场景里这个决策不是技术情怀而是物理规律倒逼的结果。我拆过三款商用ROV的主控箱发现它们共同的瓶颈不是算力——Jetson AGX Orin跑SLAM绰绰有余——而是时间确定性。水下声呐数据每200ms刷新一次如果ROS1的rospy回调被Python GIL锁住超过150ms下一帧点云就和IMU数据严重不同步构建的八叉树地图会出现“鬼影”。ROS2的rclcpp节点天然支持std::chrono::steady_clock高精度定时器配合rmw_cyclonedds_cpp的DDS_QOS_POLICY_RELIABILITY_BEST_EFFORT策略在实测中能把声呐-IMU时间戳偏差稳定在±8ms内。更关键的是ros2_control框架的硬件抽象层HAL设计ROS1时代每个电机驱动都要自己写rosserial协议解析而这个包里的underwater_hardware_interface继承自hardware_interface::SystemInterface只需实现read()和write()两个纯虚函数就能把BlueROV2的Triton电机控制器、Deep Rover的CANopen驱动器、甚至国产海翼水下滑翔机的串口舵机统统接入同一套controller_manager。我对比过ROS1的robot_localization和ROS2的nav2在水下定位的表现前者依赖tf广播的瞬时性一旦网络抖动就丢帧后者通过nav2_bt_navigator的WaitForData行为树节点能主动挂起导航任务直到/sensors/depth和/sensors/dvl数据同时就绪。这不是版本升级而是把水下作业的“数据就绪”逻辑从应用层下沉到了中间件层。2.2 控制包分层结构从物理层到任务层的四层解耦这个压缩包的目录结构不是随意堆砌而是严格遵循ROS2的分层控制理念water_robot_control/ ├── config/ # 全局参数配置非launch文件 │ ├── hardware/ # 硬件抽象层参数 │ │ ├── triton_motor.yaml # BlueROV2电机PID参数电流限幅 │ │ └── dvl_sensor.yaml # Teledyne RDI DVL的声速校准表 │ ├── navigation/ # 导航层参数 │ │ ├── nav2_params.yaml # 启用dwb_controller而非bt_navigator │ │ └── slam_params.yaml # slam_toolbox的map_frame设为odom_water │ └── perception/ # 感知层参数 │ └── sonar_processing.yaml # 声呐图像的median_filter_size: 5 ├── launch/ # 启动入口只负责组装不写业务逻辑 │ ├── bringup_launch.py # 主启动脚本Python写支持条件启动 │ └── simulation_launch.py # 仿真专用自动检测是否运行在Gazebo ├── src/ # 核心代码C为主Python仅用于工具 │ ├── underwater_hardware_interface/ # 硬件接口继承SystemInterface │ │ ├── src/ # 实现read()读取压力传感器原始值 │ │ └── include/ # 定义UnderwaterHardware类 │ ├── controller_nodes/ # 控制器节点每个对应一种控制目标 │ │ ├── depth_controller/ # 深度保持用pid_controller而非joint_state_controller │ │ └── heading_controller/ # 航向保持融合磁罗盘陀螺仪自动补偿地磁倾角 │ └── sensor_fusion/ # 多源融合核心是ekf_sea节点 │ └── ekf_sea_node.cpp # 扩展卡尔曼滤波器状态向量含[px,py,pz,vx,vy,vz,qw,qx,qy,qz] └── urdf/ # 水下专用URDF含流体动力学参数 └── water_robot.urdf.xacro # fluid_density value1025.0/单位kg/m³这种分层的价值在于当客户要求把ROV从淡水湖改成渤海湾作业时你只需要修改config/hardware/dvl_sensor.yaml里的salinity: 32.5‰再调整urdf/water_robot.urdf.xacro中的fluid_density值整个系统就能自动重新计算浮力补偿系数。而ROS1方案往往要把DVL驱动、EKF融合、PID控制器全重写一遍。我见过某团队为适应不同海域硬编码了7个版本的dvl_driver.cpp最后连自己都搞不清哪个版本对应黄海。2.3 关键技术点水下特有的三个“不可绕过”的设计1压力-深度转换的非线性校准水下机器人的深度传感器如MS5837输出的是绝对压力值Pa但实际作业需要的是相对于海平面的深度m。简单用depth pressure / (ρ·g)会出错因为海水密度ρ随盐度、温度变化重力加速度g随纬度变化。这个包里sensor_fusion/ekf_sea_node.cpp的pressure_to_depth()函数采用国际标准UNESCO公式double EKFSensorFusionNode::pressure_to_depth(double pressure_pa, double temp_c, double salinity_psu) { // UNESCO 1983公式计算海水密度 double rho 1000.0 0.8 * salinity_psu 0.016 * temp_c - 0.0001 * pow(temp_c, 2); // 重力加速度随纬度φ修正取北纬36°典型值 double g 9.780327 * (1 0.0053024 * sin(36*M_PI/180)*sin(36*M_PI/180) - 0.0000058 * sin(2*36*M_PI/180)*sin(2*36*M_PI/180)); return (pressure_pa - 101325.0) / (rho * g); // 101325为标准大气压 }提示实测发现若忽略温度补偿假设恒温20℃在舟山海域夏季表层水温28℃会导致深度读数偏高0.32米——这对海底管道巡检是致命误差。2推进器推力-转速的流体动力学建模水下推进器不是电机转得越快推力越大。在低速区300RPM推力近似线性但超过临界转速后叶梢涡脱落导致推力饱和甚至下降。这个包的urdf/water_robot.urdf.xacro里为每个推进器定义了xacro:macro namepropeller paramsname parent_link joint name${name}_joint typecontinuous parent link${parent_link}/ child link${name}_link/ axis xyz0 0 1/ /joint link name${name}_link inertial mass value0.15/ inertia ixx0.0001 iyy0.0001 izz0.0002/ /inertial visual geometrycylinder radius0.05 length0.1//geometry /visual !-- 关键流体动力学参数 -- fluid_dynamics thrust_coefficient0.0023/thrust_coefficient !-- k_t实测标定 -- torque_coefficient0.0008/torque_coefficient !-- k_q -- stall_rpm1200/stall_rpm !-- 推力饱和点 -- max_thrust12.5/max_thrust !-- N -- /fluid_dynamics /link /xacro:macroros2_control的JointTrajectoryController在生成轨迹时会调用underwater_hardware_interface的write()函数根据当前RPM查表得到真实推力再反算所需PWM占空比。这比ROS1时代用rosserial发固定PWM值可靠得多。3水下坐标系的动态TF树管理水下没有GPSmap帧无法全局固定。这个包采用三级TF树odom_water由DVLIMU积分得到的局部里程计原点随ROV移动base_linkROV本体坐标系Z轴向上符合ROS conventionpressure_frame压力传感器安装点需补偿机械臂偏移tf2广播不是静态的ekf_sea_node每50ms更新一次odom_water → base_link的变换而pressure_frame的变换由static_transform_publisher在启动时根据URDF中的origin自动计算。特别注意rviz2里显示的/tf树必须包含base_link → pressure_frame这一环否则深度图会错位。我曾因漏掉这行启动命令导致声呐图像始终偏移1.2米——整整调试了两天。3. 核心模块详解与实操配置指南3.1 硬件接口层如何让ROS2真正“读懂”水下设备underwater_hardware_interface是整个控制包的基石它解决了ROS2与真实水下硬件的“语言翻译”问题。以BlueROV2的Triton电机控制器为例其原生协议是CANopen但ROS2节点不能直接发CAN帧。该接口层做了三层封装物理层驱动can_interface.cpp使用socketcan库打开can0设备设置波特率1Mbps协议栈层canopen_master.cpp实现CANopen SDO协议能读写Triton的0x2001对象字典电机电流限幅、0x2002PID参数ROS2抽象层UnderwaterHardware::read()函数将CAN总线读到的原始数据映射为hardware_interface::StateInterface的position、velocity、effort三类状态。实操配置步骤以Ubuntu 22.04 ROS2 Humble为例启用CAN接口sudo ip link set can0 type can bitrate 1000000 sudo ip link set up can0 # 验证candump can0 应看到Triton心跳帧ID0x001编译硬件接口cd ~/ros2_ws/src/water_robot_control/src/underwater_hardware_interface colcon build --packages-select underwater_hardware_interface source ~/ros2_ws/install/setup.bash启动硬件接口节点ros2 run underwater_hardware_interface underwater_hardware_node \ --ros-args -p can_interface:can0 -p motor_ids:[1,2,3,4,5,6]注意motor_ids必须按Triton控制器拨码开关顺序填写顺序错一个ROV就会原地打转。我第一次调试时把5号电机ID写成6结果垂直推进器全功率上浮——幸好安全绳够长。验证状态接口ros2 interface list | grep hardware_interface # 应看到state_interfaces ros2 control list_hardware_interfaces # 显示6个电机的position/velocity/effort关键参数在config/hardware/triton_motor.yaml中triton_motor: can_interface: can0 motor_ids: [1,2,3,4,5,6] pid_gains: p: 120.0 # 比陆地机器人高3倍水阻大 i: 0.5 # 积分项必须小否则深度震荡 d: 25.0 current_limit: 15.0 # A防止电机过热实测心得CAN总线抗干扰技巧水下设备常因屏蔽不良引入噪声。我在舟山实测发现当ROV靠近渔船柴油发电机时CAN总线错误帧率飙升至5%。解决方案在CAN_H/CAN_L线上并联120Ω终端电阻Triton控制器自带但线缆过长时需外加使用双绞屏蔽线屏蔽层单端接地接ROV主控箱金属壳socketcan驱动中启用restart-ms: 100参数自动恢复总线。3.2 控制器层深度、航向、位置三类控制器的差异化实现ROS2的ros2_control框架允许为不同控制目标部署独立控制器。这个包预置了三类核心控制器控制器类型对应节点核心算法关键参数文件depth_controllerdepth_controller_nodePID带压力补偿前馈config/navigation/depth_pid.yamlheading_controllerheading_controller_nodePD融合磁罗盘陀螺仪config/navigation/heading_pd.yamlpose_controllerpose_controller_nodeLQR状态反馈需nav2提供目标位姿config/navigation/pose_lqr.yaml深度控制器的特殊设计陆地机器人用joint_state_controller就够了但水下必须考虑静水压强补偿。depth_controller_node的控制律为u Kp·e Ki·∫e·dt Kd·de/dt Kf·P_measured其中Kf·P_measured是前馈项P_measured来自压力传感器。config/navigation/depth_pid.yaml中depth_controller: gains: p: 85.0 # 比常规PID高因水阻大 i: 0.1 # 积分限幅必须设防饱和 d: 12.0 f: 0.00015 # 前馈系数单位N/Pa state_interfaces: - position # 深度设定值m - effort # 推进器推力N command_interfaces: - effort # 输出推力指令启动命令ros2 control load_start_controller depth_controller --set-state active注意state_interfaces必须包含position深度设定值这是ros2_control的强制要求。若误写成velocity控制器会报错退出。航向控制器的磁偏角补偿水下磁罗盘受ROV自身铁磁干扰且地球磁场在不同纬度倾角不同。heading_controller_node采用双传感器融合磁罗盘提供绝对航向但易受干扰陀螺仪提供角速度积分但有漂移。其config/navigation/heading_pd.yaml中heading_controller: gains: p: 15.0 # 比陆地高因水流扰动大 d: 3.0 # 抑制高频晃动 magnetic_declination: 5.2 # 当地磁偏角舟山约5.2° gyro_drift_compensation: true # 启用陀螺仪零偏在线估计实测发现若不设magnetic_declinationROV在直线航行时会缓慢右偏——因为磁罗盘读数比真实航向小5.2°控制器误以为“偏左”而持续右打舵。3.3 导航与感知层水下SLAM与定位的实战配置水下没有GPSnav2的amcl定位器完全失效。这个包采用slam_toolboxdvlpressure的组合方案SLAM建图配置要点config/navigation/slam_params.yaml关键设置slam_toolbox: map_frame: map odom_frame: odom_water # 不是odom必须用DVL积分里程计 base_frame: base_link scan_topic: /sensors/sonar/image_raw # 声呐图像非激光雷达 mode: mapping # 关键禁用激光雷达假设 use_scan_matching: false use_pose_extrapolation: true # 用EKF预测位姿启动建图ros2 launch water_robot_control slam_launch.py mode:mapping提示声呐图像分辨率低通常640×480slam_toolbox的minimum_travel_distance需设为0.3m陆地常用0.1m否则频繁触发建图导致卡顿。定位模式Localization配置建图完成后切换定位模式ros2 launch water_robot_control slam_launch.py mode:localization此时slam_toolbox会加载map.pgm但关键在config/navigation/nav2_params.yamllocal_costmap: ros__parameters: global_frame: map robot_base_frame: base_link # 水下特有添加深度层 plugins: [static_layer, obstacle_layer, depth_layer] depth_layer: plugin: nav2_costmap_2d::DepthLayer enabled: true topic: /sensors/depth z_resolution: 0.1 # 深度栅格分辨率mdepth_layer插件将压力传感器数据转为2D成本图使dwb_controller在规划路径时避开浅水区成本值设为254。RVIZ2可视化配置rviz2配置文件rviz2_config.rviz已预设水下专用显示/sensors/sonar/image_raw用Image插件色彩映射设为Viridis比Jet更易分辨声呐回波/tf勾选pressure_frame确保深度图对齐/sensors/depth用MarkerArray显示深度数值单位m字体大小设为0.3。启动命令rviz2 -d ~/ros2_ws/src/water_robot_control/rviz2_config.rviz4. 实操全流程与典型场景复现4.1 从零搭建Ubuntu 22.04 ROS2 Humble环境虽然网上有“鱼香ROS2一键安装”但水下机器人开发必须手动配置原因有三ros2_control依赖realtime内核补丁一键脚本常忽略水下设备驱动如CAN、DVL串口需特定内核模块rviz2渲染需OpenGL 4.5WSL2不支持。完整步骤实测有效安装基础系统Ubuntu 22.04.3 LTS Desktop非Server版需GUIsudo apt update sudo apt upgrade -y安装ROS2 Humble官方源非鱼香sudo apt install curl gnupg lsb-release sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null sudo apt update sudo apt install ros-humble-desktop ros-humble-ros2control ros-humble-ros2controllers ros-humble-nav2-bringup ros-humble-slam-toolbox -y启用实时调度关键sudo apt install linux-image-lowlatency sudo reboot # 启动后验证 uname -r # 应显示*-lowlatency sudo usermod -a -G realtime $USER echo rtkit-daemon soft rtprio 95 | sudo tee -a /etc/security/limits.conf安装水下专用依赖sudo apt install can-utils libcanopen-dev python3-can python3-serial pip3 install pyserial canopen创建工作空间mkdir -p ~/ros2_ws/src cd ~/ros2_ws source /opt/ros/humble/setup.bash colcon build --symlink-install source install/setup.bash实测心得若跳过linux-image-lowlatency步骤ros2_control的update_rate设为100Hz时实际循环周期波动达±15ms——对深度控制是灾难性的。4.2 真实ROV调试从启动到悬停的七步操作以BlueROV2为硬件平台演示完整调试流程Step 1连接硬件并验证CAN通信# 查看CAN设备 ip link show can0 # 监听Triton心跳ID0x001周期100ms candump can0 | grep 001 # 应看到类似can0 001 [8] 00 00 00 00 00 00 00 00Step 2启动硬件接口ros2 run underwater_hardware_interface underwater_hardware_node \ --ros-args -p can_interface:can0 -p motor_ids:[1,2,3,4,5,6]验证ros2 control list_hardware_interfaces应显示18个接口6电机×3状态。Step 3加载并启动深度控制器ros2 control load_start_controller depth_controller # 设置深度目标悬停在5米 ros2 topic pub /depth_controller/commands std_msgs/msg/Float64 {data: 5.0}Step 4观察深度响应曲线在rqt_plot中订阅/sensors/depth和/depth_controller/state若深度在5±0.1m内稳定说明PID参数合适若持续震荡降低i增益如从0.1→0.05若响应过慢提高p增益如从85→100。Step 5启动航向控制器ros2 control load_start_controller heading_controller # 设置航向目标正北 ros2 topic pub /heading_controller/commands std_msgs/msg/Float64 {data: 0.0}Step 6启动SLAM建图ros2 launch water_robot_control slam_launch.py mode:mapping # ROV缓慢移动采集声呐数据 # 建图完成后保存地图 ros2 run slam_toolbox save_map /home/user/mapStep 7切换定位模式并导航# 停止SLAM启动定位 ros2 launch water_robot_control slam_launch.py mode:localization # 发送导航目标相对当前位置 ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose {pose: {header: {frame_id: map}, pose: {position: {x: 10.0, y: 5.0, z: -5.0}, orientation: {w: 1.0}}}}注意z: -5.0表示深度5米ROS conventionZ向上为正水下为负。若误写z: 5.0ROV会全力上浮撞顶。4.3 仿真验证Gazebo中复现水下物理特性虽不能替代实机但Gazebo仿真对算法验证至关重要。该包的simulation_launch.py已预置水下物理引擎启用Buoyancy Pluginurdf/water_robot.urdf.xacro中包含gazebo plugin namebuoyancy_plugin filenamelibgazebo_ros_buoyancy.so fluid_density1025.0/fluid_density center_of_buoyancy0 0 0.1/center_of_buoyancy /plugin /gazebo添加水流扰动config/gazebo/currents.yaml定义currents: velocity: [0.5, 0.0, 0.0] # 东向0.5m/s流速 turbulence: 0.1 # 湍流强度启动仿真ros2 launch water_robot_control simulation_launch.py此时rviz2中可见ROV在水流中缓慢漂移/sensors/dvl/velocity话题输出非零值——这才是真实水下环境。5. 常见问题排查与独家避坑指南5.1 硬件层典型故障与诊断现象可能原因排查命令解决方案ros2 control list_hardware_interfaces无输出CAN接口未启用或波特率错误ip link show can0candump can0 -tsudo ip link set can0 type can bitrate 1000000电机不响应/joint_states指令Triton控制器拨码开关ID与motor_ids不匹配candump can0 | grep 200读SDO响应用cansend can0 020 20 01 00 00 00 00 00读取ID压力传感器读数跳变屏蔽线未接地或电源纹波大ros2 topic echo /sensors/pressure加装DC-DC隔离模块屏蔽层单端接地DVL数据丢失率10%声速设置错误或安装角度偏差ros2 topic hz /sensors/dvl/velocity在config/hardware/dvl_sensor.yaml中校准sound_speed: 1500.0实测心得DVL安装必须严格水平倾角0.5°我用激光水平仪校准后数据丢失率从23%降至1.2%。5.2 控制层振荡问题根因分析水下控制器振荡是最常见问题根源往往不在PID参数振荡特征根本原因解决方案深度缓慢周期性波动周期10sEKF状态估计发散odom_water漂移检查DVL安装紧固性清洁透镜增大ekf_sea_node的process_noise_covariance航向高频抖动频率~5Hz陀螺仪零偏未校准或磁罗盘受电机干扰运行ros2 run sensor_fusion calibrate_imu将磁罗盘远离电机30cm以上推进器发出“咔嗒”声CAN总线错误帧导致控制器指令中断cat /proc/net/can_stats查看tx_errors更换屏蔽更好的CAN线独家技巧用ros2 topic hz诊断数据流瓶颈# 测量压力传感器发布频率 ros2 topic hz /sensors/pressure # 正常应为10Hz若低于8Hz检查I2C总线负载 # 测量EKF输出频率 ros2 topic hz /odometry/filtered # 应稳定在50Hz若波动大检查CPU占用率top -p pgrep ekf_sea_node5.3 导航层定位失败的三大陷阱1map帧与odom_water帧的时间不同步现象rviz2中机器人模型“瞬移”。诊断ros2 run tf2_tools view_frames生成PDF检查map → odom_water的delay是否0.5s。根因slam_toolbox的transform_publish_period默认0.05s但EKF发布/odometry/filtered间隔为0.02s。解决方案在config/navigation/slam_params.yaml中设transform_publish_period: 0.02。2声呐图像畸变未校正现象SLAM建图出现“拉伸”伪影。诊断rviz2中/sensors/sonar/image_raw显示边缘模糊。根因声呐镜头有水膜或未启用cv_bridge的undistort。解决方案在sonar_driver_node中添加cv::Mat undistorted; cv::undistort(image, undistorted, camera_matrix, dist_coeffs);3深度层成本图覆盖导航路径现象nav2规划路径绕开深水区即使目标就在浅水。诊断rviz2中Depth Layer显示大片红色成本254。根因depth_layer的z_resolution设为0.01m太细导致浅水区成本溢出。解决方案config/navigation/nav2_params.yaml中改为z_resolution: 0.2。5.4 性能优化实战Jetson Orin上的资源分配在Jetson AGX Orin上部署时CPU/GPU/内存需精细分配组件默认占用优化后效果ekf_sea_nodeCPU 85%绑定到CPU0-3taskset -c 0-3 ros2 run...CPU占用降为42%延迟稳定在8msslam_toolboxGPU 100%禁用cuda加速use_cuda: falseGPU占用降为5%SLAM仍达5Hzrviz2内存泄漏启动时加--display-config rviz2_config.rviz禁用PointCloud2实时渲染内存占用从3.2GB→1.8GB最后分享一个小技巧在bringup_launch.py中加入健康检查节点启动时自动验证所有传感器# 启动后5秒检查关键话题 health_check_node Node( packagewater_robot_control, executablehealth_check, parameters[{topics: [/sensors/pressure, /sensors本文还有配套的精品资源点击获取