news 2026/6/5 23:47:40

ros2 controller

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ros2 controller

1.Controller Manager

Controller Manager
/ \
/ \
joint_trajectory_controller InexbotHardware
↓ ↓
产生目标关节角 调用 servoJ / 读真实角度

是一个大管家,统管很多控制器

2.joint_trajectory_controller

是ros2里面专门执行,关节轨迹的标准控制器,

joint_trajectory_controller:
接收轨迹
管理 time_from_start
插值
输出目标关节角
判断轨迹执行状态

它接收的是:

control_msgs/action/FollowJointTrajectory

这个 action 里面装着一条:

trajectory_msgs/JointTrajectory

大概结构是这样:

control_msgs::action::FollowJointTrajectory::Goal goal; goal.trajectory.joint_names = { "Joint_1", "Joint_2", "Joint_3", "Joint_4", "Joint_5", "Joint_6" }; goal.trajectory.points[0].positions = {0.0, 0.2, -0.5, 0.1, 0.0, 0.0}; goal.trajectory.points[0].time_from_start = rclcpp::Duration::from_seconds(0.0); goal.trajectory.points[1].positions = {0.1, 0.3, -0.4, 0.2, 0.0, 0.1}; goal.trajectory.points[1].time_from_start = rclcpp::Duration::from_seconds(1.0); goal.trajectory.points[2].positions = {0.2, 0.4, -0.3, 0.3, 0.1, 0.2}; goal.trajectory.points[2].time_from_start = rclcpp::Duration::from_seconds(2.0);

这条轨迹的意思是:

第 0.0 秒:6 个关节在 positions[0] 第 1.0 秒:6 个关节到 positions[1] 第 2.0 秒:6 个关节到 positions[2]

joint_trajectory_controller的核心工作就是严格按着这些time_from_start来执行轨迹,但是它不负责执行,只负责下发

它怎么执行 time_from_start?

假设 MoveIt 发来这样一条轨迹:

t = 0.0s q = [0, 0, 0, 0, 0, 0] t = 1.0s q = [10, 5, -8, 3, 0, 2] t = 2.0s q = [20, 8, -12, 5, 1, 4]

如果控制器频率是 100Hz,也就是每 0.01 秒运行一次,那么joint_trajectory_controller会不断计算:

当前时间 elapsed = 0.37s 应该处在第 0 秒和第 1 秒之间 所以算出当前目标关节角 q_command

例如简单理解:

double ratio = (elapsed - t0) / (t1 - t0); q_command[j] = q0[j] + ratio * (q1[j] - q0[j]);

当然真实的joint_trajectory_controller内部比这个更完整,会处理速度、加速度、容差、停止、取消等情况。但你入门阶段可以先这样理解,它按照时间在轨迹点之间插值,周期性产生目标关节角。

joint_trajectory_controller不会直接调用:set_servoJ_pos(...),它只会把每个周期算出来的目标关节角,写到 ros2_control 的 command interface 里。如果你配置的是:command_interfaces:
- position

意思就是:joint_trajectory_controller 每个周期输出“目标关节位置”

完整闭环是:joint_trajectory_controller
↓ command position
hardware_interface::write()
↓ servoJ
真实机械臂
↓ get_current_position
hardware_interface::read()
↓ state position
joint_trajectory_controller / joint_state_broadcaster

3.hardware_interface

把 ros2_control 的标准关节命令
转换成 Inexbot SDK / servoJ 能理解的命令

同时把 Inexbot 真实反馈关节角
转换成 ROS2 能理解的关节状态

FollowJointTrajectory action → joint_trajectory_controller 轨迹时间执行和插值 → joint_trajectory_controller 控制循环 read → update → write → Controller Manager 调用 set_servoJ_pos() → hardware_interface::write() 调用 get_current_position() → hardware_interface::read() ros_rad 和 ctrl_deg 转换 → hardware_interface 连接机器人 IP / 端口 → hardware_interface::on_configure() 或 on_activate() servoJ 初始化 → hardware_interface::on_activate()

完整周期是这样的:

1. Controller Manager 调用 hardware_interface::read() 2. read() 从 Inexbot 读取真实角度 填入 state_positions_ 3. joint_trajectory_controller 读取 state_positions_ 知道当前机械臂在哪里 4. joint_trajectory_controller 根据轨迹 time_from_start 算出当前目标角 5. joint_trajectory_controller 写入 command_positions_ 6. Controller Manager 调用 hardware_interface::write() 7. write() 把 command_positions_ 转成 ctrl_deg 调用 set_servoJ_pos()

4.joint_state_broadcaster

是ros2_controller里面负责发布关节状态的标准组件,把 hardware_interface::read() 读到的关节状态,发布成 ROS2 标准话题 /joint_states。

一条是“命令方向”:

MoveIt ↓ joint_trajectory_controller ↓ hardware_interface::write() ↓ servoJ ↓ 真实机械臂

另一条是“状态方向”:

真实机械臂 ↓ hardware_interface::read() ↓ joint_state_broadcaster ↓ /joint_states ↓ MoveIt / RViz

在hardware_interface中,你的read()以后大概会这样:

hardware_interface::return_type InexbotHardware::read( const rclcpp::Time& time, const rclcpp::Duration& period) { std::vector<double> now_ctrl_deg; readCtrlJointsDeg(now_ctrl_deg); for (size_t i = 0; i < 6; ++i) { double ros_deg = (now_ctrl_deg[i] - OFF[i]) / SGN[i]; state_positions_[i] = ros_deg * M_PI / 180.0; } return hardware_interface::return_type::OK; }

这里的重点是:

state_positions_[i] = ...

state_positions_里保存的是当前真实机械臂的关节角,单位一般是rad

但是read()本身不会自动发布/joint_states

它只是把真实状态写进 ros2_control 的状态接口里。

然后joint_state_broadcaster会读取这些状态接口,再发布出去。

其中,state_interface可以理解为hardware_interface 暴露给 ros2_control 的状态插口

比如你的硬件接口声明:

std::vector<hardware_interface::StateInterface> InexbotHardware::export_state_interfaces() { std::vector<hardware_interface::StateInterface> interfaces; for (size_t i = 0; i < 6; ++i) { interfaces.emplace_back( joint_names_[i], hardware_interface::HW_IF_POSITION, &state_positions_[i]); } return interfaces; }

这表示你的硬件提供了这些状态:

Joint_1/position Joint_2/position Joint_3/position Joint_4/position Joint_5/position Joint_6/position

每个状态背后对应一个变量:

state_positions_[0] state_positions_[1] state_positions_[2] ...

joint_state_broadcaster就是从这些 state interface 里取数据,然后发布/joint_states

moveit规划出来的轨迹是一串离散的关节目标点,不是连续曲线,

它大概长这样:

trajectory: joint_names: - Joint_1 - Joint_2 - Joint_3 - Joint_4 - Joint_5 - Joint_6 points: 第 0 个点: time_from_start = 0.0s positions = [0.00, 0.10, -0.30, 0.20, 0.00, 0.00] 第 1 个点: time_from_start = 0.8s positions = [0.15, 0.20, -0.25, 0.25, 0.03, 0.02] 第 2 个点: time_from_start = 1.6s positions = [0.30, 0.25, -0.15, 0.35, 0.08, 0.05] 第 3 个点: time_from_start = 2.5s positions = [0.45, 0.30, -0.05, 0.40, 0.10, 0.08]

假设机械臂的频率是100hz,那就是一毫秒发一个点,但是moveit一般只给出几个关键点位,所以就需要进行插值

MoveIt 规划出来的点里有什么?

一个JointTrajectoryPoint通常可能包含:

trajectory_msgs::msg::JointTrajectoryPoint point; point.positions; point.velocities; point.accelerations; point.effort; point.time_from_start;

也就是:

positions:这个时间点的关节位置 velocities:这个时间点的关节速度 accelerations:这个时间点的关节加速度 time_from_start:从轨迹开始到这个点的时间
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 23:47:36

【HarmonyOS实战】 Logger日志封装:为什么不直接用console.log?

文章目录前言一、直接用 hilog vs 封装 Logger1.1 直接用 hilog&#xff08;不封装&#xff09;1.2 封装后&#xff08;Logger&#xff09;二、Logger.ets 完整解析三、hilog 参数详解3.1 domain 业务域3.2 %{public}s 是什么&#xff1f;四、日志级别对比五、项目中的使用示例…

作者头像 李华
网站建设 2026/6/5 23:42:36

从手机信号到无人机图传:揭秘‘自由空间公式’如何影响你身边所有无线设备的‘命脉’——距离

从手机信号到无人机图传&#xff1a;揭秘‘自由空间公式’如何影响你身边所有无线设备的‘命脉’——距离你是否曾在小区角落焦急地举着手机寻找信号&#xff1f;或是眼睁睁看着无人机图传画面突然卡顿&#xff1f;这些日常困扰背后&#xff0c;其实隐藏着一个被称为"自由…

作者头像 李华
网站建设 2026/6/5 23:42:35

教育工作者必看的AI学习整合实战手册(含教育部备案工具白名单)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;教育工作者必看的AI学习整合实战手册&#xff08;含教育部备案工具白名单&#xff09; 在“双减”与教育数字化转型双重背景下&#xff0c;AI技术正从辅助工具升级为教学设计的核心要素。本手册聚焦一线教师真…

作者头像 李华
网站建设 2026/6/5 23:37:12

uniapp打包原生App流程及兼容性适配

文档版本更新日期更新内容1.0.02026/6/31.app打包Android流程及兼容性问题适配 背景 开发环境 开发工具: Visual Studio Code/HBuilderX5.0.7 部署目标: cli编译器版本&#xff1a;3.3.13/vue 2 基础配置 1.将三方分包集成到当前框架项目里面&#xff08;框架源码&#x…

作者头像 李华
网站建设 2026/6/5 23:37:12

【毕业设计】基于springboot+微信小程序的视频点播微信小程序基于springboot后端的微信小程序视频点播(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华