基于C++与ROS的双UR10机械臂控制:从Gazebo仿真到真机迁移实战
2026/8/31 7:52:28 网站建设 项目流程

简介:本资源是一套基于C++与ROS开发的双机械臂协同控制系统完整实现,面向计算机、自动化、人工智能及机器人方向的本科生、研究生与工程实践者,解决多机械臂仿真建模、真实硬件通信控制与系统集成等核心问题,适用于课程设计、毕业设计及科研原型验证。压缩包共244个文件,含37个launch启动脚本(用于Gazebo仿真与真机驱动切换)、36个头文件与22个cpp源码(涵盖trajectory_follower、action_server、tcp_socket、rt_state等关键控制模块)、24个STL与21个DAE模型文件(支撑UR10机械臂高精度可视化)、14个YAML参数配置及12个XACRO宏定义文件(实现URDF可复用建模),整体大小为19.42MB。已有73人学习下载,项目源自高分毕设(答辩98分),提供完整可运行代码、详细文档说明及清晰目录结构,包含从Gazebo仿真到双UR10真机同步控制的全链路实现,特别适合希望深入理解ROS控制架构、实时通信机制与多体协同运动规划的学习者进阶使用。 做机器人控制这一行的人,应该都体会过“仿真里跑得好好的,一上真机就出问题”的滋味。这次要分享的项目,刚好把整条链路都走了一遍:基于C++和ROS控制双机械臂系统,先在Gazebo里搭好双UR10的仿真模型,再把同一套控制框架接到两台真实UR10机器人上。项目本身是毕业设计里拿了高分的那类,源码、文档都补得比较全,适合正在做双臂机械臂、ROS运动控制,或者准备拿机器人方向当课题的兄弟参考。

这个项目最值得聊的地方不是单臂控制,而是“双”字带来的所有麻烦:两个机械臂的模型怎么共存在一张URDF里、MoveIt的规划组怎么组织、关节名字怎么不打架、两套控制器的命名空间怎么隔离、真机调试时两台机器人谁来主导等等。我把整个项目的设计思路、仿真搭建、C++实现、真机迁移和踩坑过程完整走一遍,能帮你少走不少弯路。

1. 项目整体设计与思路拆解

1.1 核心需求:为什么一定要“双机械臂”

很多任务单臂根本干不了。比如双臂协同搬运一块长的工件,或者一手固定一手拧螺丝,这类场景一旦牵涉到两个机械臂之间的相对位姿关系和轨迹同步,整个控制系统的复杂度和单臂完全不在一个量级。

这项目在需求定义阶段就定死了三个目标:

  1. 在Gazebo仿真环境中,将两台UR10组成一个双臂系统,能独立控制每一台机械臂运动;
  2. 双臂之间能协调运行,实现类似双手同时到达指定位置的协同动作;
  3. 控制代码必须在仿真和真机之间可以迁移,也就是说,不能一套代码只活在Gazebo里,换到真实UR10就得重写。

第三个目标才是最考验架构设计的。很多做仿真的项目,代码全部绑死在Gazebo的接口上,一旦换真机,驱动层、通信层、控制层全部推倒重来。这个项目从一开始就要求控制层与驱动层解耦,仿真里跑的是MoveIt加ros_control,真机也走MoveIt,只是底层驱动从ros_control换成ur_robot_driver,上层代码基本不动。

1.2 技术选型:C++、ROS、Gazebo、UR10各自的定位

这套组合放在今天依然是工业机械臂研究里最主流的方案,每一环的选择都有明确理由。

C++作为ROS的客户端库,性能比Python稳,更重要的是机械臂运动控制需要对关节状态做高频处理,Python在实时性和内存管理上的开销在某些场景下会拖后腿。尤其当项目后面要扩展力矩控制、实时伺服这类功能时,C++几乎是唯一选择。

ROS负责整机的通信和调度。话题、服务、动作三大通信机制正好覆盖机械臂控制的全部需求:关节状态用话题持续广播,参数查询走服务,轨迹执行用动作,执行过程中可以实时反馈状态、被取消、被替换,非常贴合机械臂控制场景。

Gazebo作为仿真器,和ROS的集成度是其他仿真器很难比的。UR10在ROS社区里有现成的URDF模型、驱动接口、MoveIt配置,Gazebo里启动一台UR10基本属于开箱即用。相比之下,MuJoCo这类物理引擎虽然精度高,但和ROS生态的衔接成本更高,更适合做强化学习这类需要大量并行采样的场景。

UR10本身是优傲公司的六自由度工业机械臂,负载10公斤,工作半径1.3米,实验室和工厂里都很常见。它自带ExternalControl外部控制接口,允许用户通过以太网从外部发送关节指令接管机器人,这也是后来真机迁移能顺利落地的关键前提。

1.3 整体架构:三层分离的机械臂控制系统

整个系统我拆成了三层,每层职责单一,替换起来也很方便:

  • 任务/控制层:C++写的主控节点,负责生成运动目标、调用规划、触发执行;
  • 规划/决策层:MoveIt负责运动规划、避碰检测、轨迹插值,向上给控制层提供MoveGroup接口,向下发关节轨迹;
  • 驱动/执行层:仿真环境里是Gazebo加ros_control的关节轨迹控制器,真机环境里是ur_robot_driver加UR控制柜。

这套结构和ROS社区标准的机械臂控制架构完全一致。好处在于:MoveIt的接口是跨仿真和真机的,所以控制层代码可以做到“一次编写,两处运行”。实际写代码时,我基本没有为仿真和真机准备两套业务代码,区别只出现在启动文件里。

2. 仿真环境搭建与双机械臂模型构建

2.1 环境准备:ROS与Gazebo版本怎么配合不折腾

这个项目用的是Ubuntu 20.04加ROS Noetic加Gazebo 11的组合,这也是目前兼容性最稳的一套。ROS Noetic是Ubuntu 20.04的官方支持版本,本身内置了Gazebo 11,不用额外折腾版本匹配。如果你还在用Ubuntu 18.04,对应的是ROS Melodic加Gazebo 9,也可以跑,但MoveIt相关包的版本会旧一点。

安装方式最稳妥的是用ROS官方教程一步步装,镜像源配置不好的话,也可以直接用社区维护的一键安装脚本,装完就把rosdep、依赖包、Gazebo全搞定。装完之后一定要验证一下环境:

source /opt/ros/noetic/setup.bash roscore rosrun gazebo_ros gazebo

roscore能正常起来、Gazebo里能拖进一个简单模型,说明基础环境没问题。这一步别跳过,后面的坑多半能在这一步提前暴露。

2.2 双UR10共存一张URDF的关键设计

这是整个仿真搭建里最核心的部分。直接在网上找一个UR10的URDF文件,然后把两份复制粘贴到一起,十个关节名会出现两组shoulder_pan_joint、shoulder_lift_joint,MoveIt一加载就会因为关节重名直接崩溃。

正确做法是给每个机械臂的所有关节加前缀,比如左臂叫arm1_shoulder_pan_joint,右臂叫arm2_shoulder_pan_joint。UR官方提供的ur_description包里,UR10的xacro宏自带prefix参数,可以传入自定义前缀,所以不需要手改关节名:

<xacro:include filename="$(find ur_description)/urdf/ur10.urdf.xacro" /> <xacro:ur10_robot prefix="arm1_" joint_limited="true" base_link_pose="0 0.4 0 0 0 0" /> <xacro:ur10_robot prefix="arm2_" joint_limited="true" base_link_pose="0 -0.4 0 0 0 0" />

base_link_pose参数控制每台UR10基座在全局坐标系里的位置,我这里让两台机械臂左右对称摆放,中间留0.8米间距,既方便观察双臂运动,又给后续的避碰规划留下操作空间。对称布局还有个好处,就是两个机械臂的笛卡尔工作空间在数学上是对称的,写控制代码时可以用一套参数生成左右两边的目标点。

加载这份合并后的URDF时,用xacro命令转成纯URDF:

rosrun xacro xacro ur10_dual.urdf.xacro > ur10_dual.urdf

然后由robot_state_publisher读取robot_description参数,就能在TF树上看到完整的坐标关系:base_link下挂两个臂的基座,每只臂分出一串连杆和关节。

2.3 Gazebo控制器配置:两套controller怎么不打架

在Gazebo里让机械臂动起来,靠的是ros_control框架。仿真中的关节由Gazebo物理引擎模拟,ros_control通过控制插件把话题指令转成关节力矩,从而驱动模型运动。

这里最需要注意的是两套控制器各自的命名空间。controllers.yaml文件里,每个控制器都要有独立的节点名和独立的关节列表:

arm1_controller: type: position_controllers/JointTrajectoryController joints: - arm1_shoulder_pan_joint - arm1_shoulder_lift_joint - arm1_elbow_joint - arm1_wrist_1_joint - arm1_wrist_2_joint - arm1_wrist_3_joint state_publish_rate: 100 arm2_controller: type: position_controllers/JointTrajectoryController joints: - arm2_shoulder_pan_joint - arm2_shoulder_lift_joint - arm2_elbow_joint - arm2_wrist_2_joint - arm2_wrist_3_joint

写这份配置时我踩过一次坑:如果关节列表写错,比如两个控制器都写了同一组关节,controller_manager加载时不会报错,但只有一个控制器会真正拥有这些关节的控制权,另一个控制器的指令发出去完全没反应,查起来非常费时间。

启动Gazebo时,需要先spawn模型,再加载控制器:

<launch> <param name="robot_description" command="$(find xacro)/xacro $(find ur10_dual_description)/urdf/ur10_dual.urdf.xacro" /> <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" /> <node name="spawn_ur10_dual" pkg="gazebo_ros" type="spawn_model" args="-param robot_description -urdf -model ur10_dual" /> <node name="controller_spawner" pkg="controller_manager" type="spawner" args="joint_state_controller arm1_controller arm2_controller" /> </launch>

加载完成后,通过rostopic list验证一下,正常情况下应该能看到:

  • /arm1_controller/command
  • /arm2_controller/command
  • /joint_states

能同时看到两套控制器的command话题,就说明两个机械臂已经被独立接管了。

2.4 MoveIt配置:双规划组的生成与碰撞矩阵修正

在Gazebo里已经能控制关节运动之后,下一步是把MoveIt加进来。用MoveIt Setup Assistant加载ur10_dual.urdf.xacro,生成ur10_dual_moveit_config包,这里有两个操作要点。

第一,规划组要分别定义。左臂所有关节放进arm1_manipulator,右臂所有关节放进arm2_manipulator,还要加上每个臂的末端连杆作为规划组的tip link。这样后面的C++代码里,就能用两个独立的MoveGroupInterface对象分别控制左右臂。

第二,要检查Self-Collision Matrix。Setup Assistant默认会自动生成碰撞检测矩阵,但双臂场景下,它往往只检测到单臂内部的连杆碰撞,左臂和右臂之间的碰撞检测可能缺失。这个一定要在Setup Assistant的Collision Matrix页面手动重新计算全量碰撞矩阵,不然双臂做协调运动时,两臂直接撞上了MoveIt都不会拦。

如果项目里需要更强的一体化规划,还可以额外创建一个dual_manipulator规划组,把十个关节全部放进去。这个组的好处是MoveIt能统一规划出两条机械臂的联合轨迹,完全避免互相碰撞;坏处是灵活性低,目标姿态指定起来很别扭。我这项目的做法是两种规划组并存:日常协同用两个独立组加避碰场景,需要严格联动时用联合组。

3. C++与ROS控制核心实现

3.1 功能包规划与代码组织结构

工作空间我建议这样分,源码结构清晰,后面换成真机也方便:

src/ ├── ur10_dual_description/ # URDF、xacro、meshes ├── ur10_dual_gazebo/ # Gazebo启动、控制器配置 ├── ur10_dual_moveit_config/ # MoveIt Setup Assistant生成 └── ur10_dual_control/ # C++控制节点、launch文件

控制逻辑全部集中在ur10_dual_control里,这是整个项目里唯一要频繁改代码的地方。把模型、仿真、配置和控制四个维度拆开,意味着你在真机调试时直接换一个launch文件,其他包的改动基本为零。

3.2 双臂运动控制的C++主程序

控制代码的核心其实就是两件事:给MoveIt设定目标,然后让它执行。但双臂和单臂最大的区别在于,你要同时维护两个MoveGroupInterface对象,并且要处理两条规划轨迹之间的时序关系。

下面这段是项目里最基础的双臂同步运动示例:

#include <ros/ros.h> #include <moveit/move_group_interface/move_group_interface.h> #include <moveit/planning_scene_interface/planning_scene_interface.h> int main(int argc, char** argv) { ros::init(argc, argv, "dual_arm_demo"); ros::NodeHandle nh; ros::AsyncSpinner spinner(2); spinner.start(); moveit::planning_interface::MoveGroupInterface arm1("arm1_manipulator"); moveit::planning_interface::MoveGroupInterface arm2("arm2_manipulator"); arm1.setPlanningTime(5.0); arm2.setPlanningTime(5.0); geometry_msgs::Pose target_arm1; target_arm1.orientation.w = 1.0; target_arm1.position.x = 0.45; target_arm1.position.y = 0.35; target_arm1.position.z = 0.55; geometry_msgs::Pose target_arm2; target_arm2.orientation.w = 1.0; target_arm2.position.x = 0.45; target_arm2.position.y = -0.35; target_arm2.position.z = 0.55; arm1.setPoseTarget(target_arm1); arm2.setPoseTarget(target_arm2); moveit::planning_interface::MoveGroupInterface::Plan plan_arm1; moveit::planning_interface::MoveGroupInterface::Plan plan_arm2; bool ok_arm1 = arm1.plan(plan_arm1); bool ok_arm2 = arm2.plan(plan_arm2); if (ok_arm1 && ok_arm2) { arm1.execute(plan_arm1); arm2.execute(plan_arm2); } else { ROS_WARN("Planning failed: arm1=%d, arm2=%d", ok_arm1, ok_arm2); } ros::shutdown(); return 0; }

这个代码框架有两个关键点:

  1. 两个规划组的目标位姿是对称的,左右镜像,目的就是验证双臂能同时到达各自位置,这是协同任务的基础;
  2. execute()调用本身是异步返回的,实际机械臂执行轨迹需要时间,如果后面还要跟别的动作,这里一定要加等待机制,否则程序继续往下跑,机械臂还没到位,后续指令会堆积。

更实用的办法是执行后等待关节状态收敛,而不是直接sleep固定时间。我在真机上就吃过亏,仿真里固定sleep几秒没问题,换到真机上运动速度不同,固定延时要么等太久要么不够,改成读关节状态判断到位才稳。

3.3 双臂协同:同步执行与避碰的取舍

双臂协同最大的难点在于,两个规划组各自规划出来的轨迹,执行时没有一个统一的控制器来保证它们“同时”运动。我在项目里试过两种思路,分别适合不同场景。

思路一:维持两个规划组,在控制层手动同步。具体做法是执行前把两条轨迹的期望执行时间对齐,让两条轨迹的time_from_start分布一致。由于MoveIt执行时两个控制器各自独立,时间对齐能大幅减少不同步的程度。适用于左右臂目标位姿独立、只要求“大致同时”到达的场合。

思路二:合并成一个dual_manipulator规划组,让MoveIt在规划阶段就生成十关节联合轨迹。这样做的好处是两条臂的轨迹天然同步,而且MoveIt会统一做避碰检测。缺点是目标指定麻烦,因为一个规划组只能设定一个目标组合,实际应用时一般是先算出一条臂的末端位姿,再根据相对位置关系算出另一条臂的位姿,一起丢给规划组。

避碰方面,如果坚持用两个独立规划组,务必要把另一个机械臂的模型加进PlanningScene的碰撞物体里。我写过一个小工具,把arm1的所有link作为障碍物发布到planning scene的collision objects里,让arm2规划时避让,反之亦然。这样虽然没有联合规划那么优雅,但胜在改动小,而且对已有的单臂规划逻辑几乎零侵入。

3.4 关节空间控制:setJointValueTarget的适用场景

笛卡尔空间的目标位姿不是万能的。有些工况,比如回零位、微调某个关节角度、做关节空间插值,直接用setJointValueTarget更方便。比如让左臂回到零位:

std::vector<double> home_position = {0.0, -1.5708, 0.0, -1.5708, 0.0, 0.0}; arm1.setJointValueTarget(home_position); arm1.move();

这种方式跳过了逆运动学计算,规划速度更快,成功率也更高。需要提醒的是,关节角度数组的顺序必须和URDF中该规划组的关节顺序一致,不要凭感觉填。可以从MoveIt的getCurrentJointValues()里打印出来核对,否则很容易出现“目标看起来对,实际机械臂满世界乱跑”的情况。

4. 从Gazebo仿真迁移到真实UR10

4.1 仿真与真机环境的差异对照

仿真跑得再好,迁移到真机时总会被现实狠狠教育一顿。我把两者差别整理成一张对照表,也方便你提前做好预期管理:

对比项Gazebo仿真真实UR10
底层驱动ros_control + Gazebo插件ur_robot_driver + ExternalControl
关节状态反馈仿真器内部计算真实编码器值
速度/加速度上限无强制限制受安全配置和物理极限限制
碰撞检测物理引擎自动处理依赖外部安全配置,控制层需自行避碰
末端负载默认忽略必须设置质量、质心、惯量
通信方式共享内存/ROS话题以太网TCP通信
时间同步时钟可加速/暂停必须实时,具备硬实时要求

这套表是我做真机迁移前自己列的,后来每一次调试基本都在跟这几行差异打交道。如果你们实验室正好有UR10,建议先按这个清单逐项确认,能省不少现场排查时间。

4.2 连接真实UR10:驱动与通信配置实操

UR10真机控制的完整链路是:PC上的ur_robot_driver节点通过网络向UR控制柜发送ExternalControl指令,控制柜里的实时解释器执行这些指令并驱动电机,然后通过dashboard和状态话题把关节状态、IO状态、安全状态反馈回来。

具体配置步骤大概是:

  1. 把PC和控制柜用网线直连,配置静态IP,比如PC设为192.168.1.100,控制柜保持192.168.1.10,先互相ping通;
  2. 在UR示教器上安装ExternalControl的URCap插件,并在安装配置里填写PC的IP地址和端口号,UR10默认走50001端口;
  3. 在示教器程序里插入ExternalControl节点,执行程序后机器人进入外部控制模式,等待PC端连接;
  4. 在PC上启动ur_robot_driver节点,并用MoveIt的launch加载同一套moveit_config。

双臂真机需要同时启动两个驱动节点,分别指向两台机械臂的IP,同时加载两台机器人相应的MoveIt配置。这里有个要注意的地方:如果你的两台UR10控制柜IP相同,一定要先改控制柜IP,保证网络里两台机器人都能被唯一识别。

UR10的ExternalControl本身需要你提供一套硬件工具坐标等参数,在示教器上记得把TCP信息设置正确,否则控制器计算的逆运动学结果和实际TCP点对不上,机械臂末端位置会有几十毫米的偏差。

4.3 从仿真代码到真机代码:到底要改什么

好消息是,在仿真里跑通的C++控制代码,在真机上几乎不用改。这正是当初选择MoveIt加ros_control这套架构的价值所在。真机环境只是把“谁执行关节轨迹”这件事从Gazebo的ros_control换成了ur_robot_driver,对上层MoveGroupInterface的调用方式没有影响。

真正需要调整的主要是启动文件和参数:

  • 速度与加速度限幅:真机上线第一件事就是把move_group里的max_velocity_scaling_factor和max_acceleration_scaling_factor调到0.1左右,让它慢速爬行,确认每个关节运动方向、正向反向都符合预期后,再逐步加快;
  • 安全IO联动:把急停、安全门等信号接入控制逻辑,一旦触发立即取消所有轨迹执行;
  • 关节限位核对:UR10在非受限模式下关节范围很大,但MoveIt配置包里的joint_limits.yaml要和你示教器上的实际配置一致,不一致会导致规划器以为某些角度可达,执行时却被控制柜安全机制拦停;
  • 负载参数:如果末端装了夹爪或工具,要在代码里设置工具的重量和质心,否则机械臂运动到某些位姿时会因为负载补偿不对产生明显抖动。

我刚开始上真机调试时,第一件事永远是让机械臂以最低速度回零点,确认两臂间不会发生任何干涉,然后才逐步跑完整的协同任务。真机上最大的成本不是电费,是撞一次机的维修费。

4.4 真机调试的时间同步与状态监控

真机环境和仿真一个很大的区别是,UR控制柜的实时性要求很高,外部控制指令的发送周期必须稳定。ur_robot_driver内部已经做了实时调度,但你的控制程序如果跑在普通PC上,要注意别让进程被其他任务抢占太多CPU时间。

一个很有效的方法是给控制节点设置实时优先级,用chrt命令或者线程优先级接口,把控制线程的调度策略改成SCHED_FIFO,并设置较高优先级。修改前务必先确认内核支持PREEMPT_RT补丁或者至少是低延迟内核,否则直接设置优先级可能会导致系统不稳定。

调试过程中建议全程用rosbag记录关节状态和各控制话题:

rosbag record -O robot_debug.bag /joint_states /arm1_controller/command /arm2_controller/command /tf

真机出现异常时,回放bag一边看关节指令和实际状态的误差,一边看时间戳是否有跳变,定位问题会比现场瞎猜快很多。这套方法我在仿真阶段就开始用,到真机阶段直接延续,帮了大忙。

5. 常见问题与排查技巧实录

5.1 高频问题速查表

项目从仿真到真机的全过程中,我整理了下面这些遇到过的典型问题,按现象、可能原因和排查方法列成了速查表:

现象可能原因排查方法
Gazebo中机械臂刚加载就塌陷/抖动URDF缺少collision标签或inertial参数异常打开模型面板,检查base_link碰撞体积,确认模型放在地面上
/arm1_controller/command话题不存在controllers.yaml配置错误或controller没有成功加载查看controller_manager日志,rosparam检查控制器关节列表
双机械臂中只有一只臂能动两个控制器的关节列表重复,控制权被抢检查controllers.yaml中两个控制器的joints列表
MoveIt报No planning group foundmoveit_config中规划组名和代码不一致检查ompl_planning.yaml和move_group.launch中的group名
双臂规划时互相不避碰Self-Collision Matrix缺少臂间碰撞对在Setup Assistant里重新计算全量碰撞矩阵
真机连接失败,driver启动即退出IP配置错误、URCap没装、端口被占用示教器ping PC,确认ExternalControl脚本处于运行状态
UR10执行途中安全急停关节限位配置不一致或负载设置错误对比joint_limits.yaml与控制柜实际配置
双臂执行不同步两个规划组分别执行时轨迹时间轴不对齐统一规划联合轨迹,或手动对齐轨迹的time_from_start
真机运动到手未端时明显抖动工具质量、质心未设置或设置错误在代码中配置工具的weight和center of gravity

5.2 Gazebo模型“飞天”和“塌陷”的终极解法

Gazebo里机械臂加载后莫名掉下去或者乱弹,这是做仿真的新手遇到最多的现象。根因通常有两个。

第一,URDF的collision标签缺失或尺寸过小。Gazebo的物理引擎需要碰撞几何体来计算接触,如果你只定义了visual标签,模型会直接穿过地面。解决办法是把每个link的collision标签补上,尺寸适当大于实际视觉模型,给物理引擎留点容错空间。

第二,机械臂基座没有固定到世界坐标系。双机械臂项目里,两台UR10的base_link如果都只放在空中,不落到地面上,重力一加上去模型就直接掉到地表以下。处理方式是在URDF里把base_link设计成固定的底座,或把base_link的z坐标设置在合理高度,并用fixed joint把底座和world链接起来。我后来加了一个简单的长方体底座模型,把两臂都固定在底座上,问题彻底解决。

5.3 双臂关节命名冲突的排查思路

“一共有十个关节,MoveIt却只认六个,还有一个规划组为空”这类问题,十有八九是关节重名或前缀没统一。排查时按这个顺序来:

  1. 用rosrun urdfdom check_urdf ur10_dual.urdf检查URDF合法性;
  2. 用rosrun tf view_frames查看完整TF树,确认每个关节是否都带正确前缀;
  3. 启动move_group后,用rosparam get /move_group/joint_model_group里的GroupState,确认每个规划组包含的关节列表;
  4. 如果发现某个规划组关节数量和预期不符,回Setup Assistant重新生成配置包。

5.4 真机调试时最容易忽略的安全细节

这个问题已经不仅是技术问题,而是和人机安全直接相关。真机调试时不管代码多急,以下几点永远不要妥协:

  • 所有首次上真机的运动,速度缩放系数一律不超过0.1;
  • 双手动的急停按钮必须处于可触及状态,且调试前把急停接进控制逻辑,触发后立即停止轨迹发布;
  • 人站在机械臂工作半径之外,千万别为了近距离观察点位伸头进机械臂运动区间;
  • 修改任何运动学参数后,先做一次空载低速回零,再看末端实际位姿是否符合预期。

结尾:一些个人经验和建议

做完这个项目最大的体会是,先把仿真里的整条控制链路彻底跑通,再上真机调试,能省掉一半的排查时间。仿真的价值不只是写代码方便,更重要的是它能让你把控制逻辑、参数配置、问题诊断手段全部沉淀下来,到了真机环境,你面对的就只剩下通信、安全和物理参数这三类问题。

还有一个小技巧分享给后面做机械臂项目的朋友:调试时不要迷信“一次规划,一次执行”这种最简流程,写一个简单的状态监控终端,把两个规划组的当前关节角度、目标关节角度、执行状态、规划耗时全部实时打印出来。看起来土,但在真机上排查“这个点怎么没到”“为什么这么慢”这类问题,比看Rviz里的动画直观得多。

这套基于C++和ROS的双机械臂控制框架,往后扩展的空间还很大。比如加一套视觉伺服,让两台UR10靠摄像头识别来抓取工件,或者把控制方式从位置控制升级到力控,做双臂协同打磨,这些都是顺着现在这个框架往上加功能就能做到的。希望这篇分享对正在做双机械臂项目的人有帮助。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询