基于ROS与MoveIt的达闼Gluon 6L3机械臂控制源码解析
简介达闼Gluon 6L3机械臂的化学实验辅助系统源码面向机械臂编程与实验自动化开发者用于实现试管操作、加样混合等精细动作的编程与控制。压缩包共2000个文件大小56.31MB主要包含Makefile和CMake构建配置、TypeScript/JavaScript/Python脚本、C/C控制算法、Shell自动化任务、头文件与消息定义等覆盖从底层控制到上层接口的完整链路。目前已有384人学习。借助Mintasca开源SDK源码支持抓取、放置、移动、倾倒、翻转、刮取、搅拌等操作并可集成Unity数字孪生实现实时联动适合在化学实验中做方案验证与二次开发。通过阅读源码可掌握机械臂动作规划、ROS消息交互与运动控制模块的组织方式对搭建同类科研辅助系统有直接参考价值。1. 从化学实验台的重复性痛点说起化学实验里最容易出问题的是移液、加样、搅拌这类重复操作。手抖、计时偏差、加样顺序错乱都会直接影响实验结论。达闼 Gluon 6L3 这套源码的价值是把这些操作变成可保存、可回放、可编辑的程序序列而不是依赖操作员临场发挥。项目的硬件本体是达闼 Gluon 6L3 六轴机械臂控制层基于 Mintasca 开源 SDK规划层接入了 ROS 生态MoveIt 与 LERP 插值规划插件上层则同时提供 Python / C / TypeScript 等多语言接口并保留了 Unity 数字孪生联动的完整消息链路。源码总量 2870 个文件既有底层 C 算法实现也有前端交互面板和 Shell 部署脚本。适合正在做 ROS 机械臂控制、实验自动化改造、或者想用数字孪生做离线验证的工程师参考。2. 从文件列表还原 Gluon 6L3 的 ROS 工程骨架.so 插件、ASD 消息与 Mintasca SDK拿到源码先不要急着跑把文件列表过一遍能看出这个项目真实的运行链路。工程里几个关键文件直接把架构暴露得很清楚libmoveit_lerp_planner_plugin.so.0.1.0是 MoveIt 的线性插值规划插件libmoveit_tutorials.so.0.1.0是随 MoveIt 教程编译出来的示例插件actuatorcontroller_ros-srv.asd与actuatorcontroller_ros-msg.asd是执行器控制器的服务与消息定义unity_robotics_demo_msgs-srv.asd和unity_robotics_demo_msgs-msg.asd是 Unity 联调用的消息协议gluon-msg.asd与gluon_control-msg.asd则是 Gluon 机械臂本体状态与控制指令的定义。这里的.asd后缀可以理解为该项目对 ROS.msg/.srv接口文件的工程化封装编译期会生成对应的 Python / C / TypeScript 绑定。整套源码的分层结构大致如下表所示文件类型工程角色对应技术层*.so.0.1.0插件MoveIt 规划器插件、算法模块C 规划层*-msg.asd/*-srv.asd机械臂状态、控制指令、Unity 消息消息中间件层TypeScript / JavaScript前端控制面板、操作界面Web UI 层Python 脚本与字节码动作序列编排、实验流程控制应用逻辑层C / C 源码与头文件核心算法、硬件抽象、SDK 对接底层驱动与控制层Makefile / Shell 脚本编译构建、节点启动、任务调度工程部署层2.1 先搞清 MoveIt 规划插件加载链路libmoveit_lerp_planner_plugin.so.0.1.0是整套规划链路的发动机。MoveIt 默认的 OMPL 规划器适合复杂避障场景但化学实验里大部分动作是从 A 点到 B 点走一条确定路径线性插值规划器反而更合适。编译产物以.so.0.1.0命名通常是被 MoveIt 的插件描述文件moveit_controller_manager或 planner 配置的plugins.xml加载。在 launch 文件中需要显式声明加载该插件launch !-- 启动 MoveIt 主节点使用 LERP 规划器插件 -- node namemove_group pkgmoveit_ros_move_group typemove_group respawnfalse outputscreen param nameplanning_plugin valuelerp_planner/LERPPlanner / param namerequest_adapters valuedefault_planner_request_adapters/AddTimeParameterization default_planner_request_adapters/FixWorkspaceBounds default_planner_request_adapters/FixStartStateBounds default_planner_request_adapters/FixStartStateCollision / param namestart_state_max_bounds_error value0.1 / /node !-- Mintasca SDK 桥接节点把机械臂关节状态发布到 /joint_states -- node namemintasca_bridge pkggluon_bringup typemintasca_ros_bridge outputscreen param namesdk_config_path value$(find gluon_bringup)/config/mintasca.yaml / /node /launch这段配置里planning_plugin参数直接指定了 LERP 规划器request_adapters是 MoveIt 在规划前对请求做的标准化处理其中AddTimeParameterization负责给规划出的路径点分配时间和速度FixWorkspaceBounds用于把起点约束到机械臂工作空间内。start_state_max_bounds_error控制允许的关节角度误差范围如果机械臂零点标定有偏差这个值设得太小会导致规划直接报Start state is out of bounds错误。2.2 Mintasca SDK 与 ROS 桥接的关系Mintasca 是达闼提供的机械臂控制 SDK它本身不依赖 ROS但在这套源码里通过桥接节点把底层的关节控制能力映射成了 ROS topic 和 service。actuatorcontroller_ros-srv.asd定义的就是这类控制服务典型接口包含enable_actuator、set_joint_position、get_joint_state等。桥接节点内部做的事情本质上并不神秘读取 Mintasca 的回调数据同步发布到/joint_states接收/gluon_control/joint_command指令调 SDK 下发到底层执行器。实际调试时先用下面的命令检查桥接是否在正常发布数据# 查看关节状态话题是否在发布 rostopic echo /joint_states -n 1 # 手动发一个关节位置指令验证控制链路 rostopic pub /gluon_control/joint_command std_msgs/Float64MultiArray \ data: [0.0, 0.0, 0.5, 0.0, 0.0, 0.0] -r 10rostopic pub发送的是六个关节的目标角度单位是弧度。发布频率设成 10Hz 是防止指令骤变导致执行器报过流。如果执行器没有反应先把ActuatorLogTool拉起来看日志这个工具在源码里对应执行器状态诊断模块能输出每个关节的电流、温度和位置误差排查顺序一般是桥接是否发布 → 执行器是否使能 → 日志是否报限位错误。2.3 多语言构建在工程上如何共处2870 个文件覆盖了 TypeScript、C、C、Python、JavaScript、Shell。编译顺序并不难梳理先用 Makefile 编译 C / C 核心库与.so插件再生成 Python 的.pyc字节码最后编译 TypeScript 到 JavaScript 给前端用。一个容易踩的坑是头文件的 include 路径Gluon 的 SDK 头文件和 MoveIt 的头文件如果混在一个 include 目录下可能会出现符号冲突。常见做法是分目录管理C 层的 CMakeLists 里用target_link_libraries显式依赖而不是全局include_directories# 控制层核心库依赖 Mintasca SDK 与 MoveIt 接口 add_library(gluon_core SHARED src/gluon_driver.cpp src/lerp_executor.cpp) target_include_directories(gluon_core PRIVATE ${MINTASCA_SDK_INCLUDE} ${MOVEIT_INCLUDE_DIRS} ) target_link_libraries(gluon_core ${catkin_LIBRARIES} ${Mintasca_LIBRARIES} )这样处理后编译期只暴露必要的头文件减少命名空间污染。Python 层的.pyc字节码文件在部署机上可以提升导入速度但注意如果换了 Python 版本旧的字节码会导致Bad magic number报错部署脚本里需要加上rm -rf __pycache__的清理步骤。3. 抓取、倾倒、搅拌的动作原语拆解LERP 规划器与 MoveIt 参数实测化学实验动作序列拆到底层不外乎点位运动和姿态变换。Gluon 6L3 这套源码把抓取pick、放置place、移动move、倾倒pour、翻转flip、刮取scrape、搅拌stir封装成了可调用的动作函数再往上就是实验流程编排。这一层最关键的选择在规划器MoveIt 默认 OMPL 在无避障需求时会生成看起来合理但路径不可预测的轨迹而 LERP 规划器生成的路径严格沿关节空间直线插值每次规划结果完全一致。3.1 为什么化学实验场景偏爱 LERP 而不是 OMPLOMPL 基于采样规划结果是概率完备的同样的起点和终点两次规划出来的路径可能完全不同。放在化学实验场景里这意味着机械臂每一次倾倒的路径都有细微差异对于追求可复现性的实验来说很不利。LERP 则不同它在关节空间对每个关节做线性插值路径确定、可预测代价是不做避障。如果实验台上有试管架、烧杯这些固定障碍物就用 MoveIt 的碰撞检测场景Planning Scene做一次离线验证把障碍物加进去跑一遍确认路径不会碰撞。下面是基于moveit_commander的抓取与倾倒实现#!/usr/bin/env python3 import rospy import moveit_commander from geometry_msgs.msg import PoseStamped, Quaternion import math moveit_commander.roscpp_initialize(sys.argv) rospy.init_node(gluon_chem_demo, anonymousTrue) arm moveit_commander.MoveGroupCommander(gluon_arm) arm.set_planner_id(LERPPlanner) arm.set_planning_time(5.0) arm.set_goal_position_tolerance(0.005) # 位置容差 5mm arm.set_goal_orientation_tolerance(0.01) # 姿态容差约 0.01rad def pick_at(x, y, z, roll0.0, pitchmath.pi/2, yaw0.0): 控制末端移动到指定坐标姿态默认水平前伸pitch90°用于夹爪取物 target PoseStamped() target.header.frame_id base_link target.pose.position.x x target.pose.position.y y target.pose.position.z z q Quaternion() q.w math.cos(pitch / 2) q.y math.sin(pitch / 2) target.pose.orientation q arm.set_pose_target(target) plan arm.plan() if plan: arm.execute(plan, waitTrue) else: rospy.logerr(pick 规划失败请检查目标点是否在工作空间内) def pour_into(target_x, target_y, target_z, tilt_anglemath.radians(45)): 倾倒动作腕部关节旋转使容器口向下倾斜指定角度 joint_target arm.get_current_joint_values() # 假设第 5 个关节是腕部旋转轴调整到目标倾斜角 joint_target[4] tilt_angle arm.set_joint_value_target(joint_target) plan arm.plan() if plan: arm.execute(plan, waitTrue)代码里的关键点是set_planner_id(LERPPlanner)指定规划器如果这个 ID 和 launch 里配置的插件名不一致MoveIt 会直接抛出规划器 ID 无效的异常。set_goal_position_tolerance和set_goal_orientation_tolerance这两个参数决定了规划终止的条件取值越小对末端定位精度要求越高但 LERP 是纯运动学插值实际精度还是取决于底层伺服控制的表现。化学加样场景位置容差 5mm 通常够用如果做试管内壁刮取可以收紧到 2mm但要确认机械臂本体重复定位精度能支撑。3.2 轨迹规划请求里的速度与加速度约束MoveIt 的轨迹规划请求支持对每个关节设置速度缩放因子。AddTimeParameterization适配器会根据路径长度和目标速度自动生成时间戳。实际用下来Gluon 6L3 做倒液动作时速度缩放建议设在 0.2 到 0.4 之间太快液体容易溅出太慢又影响效率。对应的参数通过set_max_velocity_scaling_factor设置# 倾倒动作降低速度保证液体不飞溅 arm.set_max_velocity_scaling_factor(0.3) arm.set_max_acceleration_scaling_factor(0.5) # 执行搅拌动作时可以采用笛卡尔路径循环 from moveit_commander.conversions import pose_to_list waypoints [] for i in range(4): waypoints.append(compute_stir_pose(radius0.03, anglei * math.pi / 2)) (plan, fraction) arm.compute_cartesian_path(waypoints, 0.01, 0.0) arm.execute(plan, waitTrue)compute_cartesian_path是 MoveIt 里做笛卡尔空间轨迹的标准方式第二个参数 0.01 是笛卡尔路径的最大步长米步长越小轨迹越平滑但规划越耗时。第三个参数 0.0 是跳点阈值jump threshold如果相邻路径点之间关节角度跳变超过这个值规划器会拒绝路径。调这个函数有个经验当fraction返回小于 1.0 时说明路径在笛卡尔空间不可达或碰到了奇异点需要把步长调大一点或者把末端路径改成多个小段分别规划再拼接。3.3 动作原语的坐标帧设计抓取、放置、倾倒这些动作能不能精确执行很大程度取决于坐标系定得对不对。源码里的gluon-msg.asd与gluon_control-msg.asd消息定义中控制指令的坐标帧基准是base_link而实验台的位置需要单独标定。一个我在实际部署时常用到的标定顺序是先让机械臂末端分别对准实验台的三个已知角点记录下末端在base_link下的坐标然后通过三点法求出实验台坐标系到基坐标系的变换矩阵。其中用于记录位置的代码可以复用前面的pick_at函数把实际物理位置与代码中传入的坐标做映射。4. TypeScript 控制面板到 Unity 数字孪生的联动链路这套源码的上层交互并不是只能敲命令行。TypeScript 和 JavaScript 文件构成了一个 Web 控制面板通过 roslibjs 连接到 rosbridge server在浏览器里完成状态监控和动作触发。与此同时 Unity 端通过unity_robotics_demo_msgs-msg.asd的消息定义接收机械臂关节状态驱动数字孪生模型做实时同步。这两条链路在工程上是平行的共用同一个 ROS 主网络。4.1 TypeScript 动作面板的订阅与发布浏览器里直接访问 ROS 话题需要rosbridge_server提供 WebSocket 转发。以下是 TypeScript 侧最常见的连接与指令下发代码import ROSLIB from roslib; const ros new ROSLIB.Ros({ url: ws://localhost:9090, }); // 订阅关节状态驱动前端机械臂模型同步 const jointStateListener new ROSLIB.Topic({ ros: ros, name: /joint_states, messageType: sensor_msgs/JointState, }); jointStateListener.subscribe((msg: any) { // msg.position 是长度为 6 的关节角度数组 updateArmModel(msg.position); lastJointState msg.position; }); // 发布轨迹执行指令路径点序列 const trajectoryPublisher new ROSLIB.Topic({ ros: ros, name: /gluon_control/execute_trajectory, messageType: gluon_control/JointTrajectory, }); export function runPourAction() { const goal new ROSLIB.Message({ joint_names: [joint1, joint2, joint3, joint4, joint5, joint6], points: [ { positions: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], time_from_start: 0.0 }, { positions: [0.0, -0.3, 0.8, 0.0, 0.7, 0.0], time_from_start: 2.0 }, ], }); trajectoryPublisher.publish(goal); }runPourAction里发布的是一条完整的关节轨迹目标点数组里的每个元素包含六个关节的目标角度和目标时间偏移。这种设计的好处是轨迹的合理性由调用方保证MoveIt 侧只负责执行适合已经通过离线验证的固定动作序列。第一次联调时如果发现机械臂没有反应优先检查 WebSocket 是否连上、/joint_states有没有数据、以及发布的消息结构是否和.asd定义完全一致。4.2 Unity 数字孪生的消息对接Unity 端不再走 WebSocket而是用 Unity Robotics Hub 的 TCP 连接直接收 ROS 消息。工程里unity_robotics_demo_msgs-msg.asd定义的消息类型通常是UnityArmPose包含六个关节角度和末端坐标Unity 脚本收到后直接给机械臂模型的六个关节赋值。同步的实时性依赖两件事发布频率和时间戳对齐。机械臂桥接节点以 50Hz 发布关节状态时Unity 端每帧读取一次就能保证视觉上的平滑。而时间戳对齐要做的事是在消息里带上header.stampUnity 收到后和时间基准做差超出阈值就丢弃这一帧避免网络抖动造成的模型抖动。4.3 数字孪生做离线验证的价值数字孪生在化学实验里最实用的场景是先模拟后执行。写好的动作序列先在 Unity 里跑一遍看看末端会不会撞到虚拟烧杯、试管架摆放是否在可达范围内确认无误再切换到物理机械臂执行。这里有一个值得注意的细节Unity 中的机械臂模型必须和真实机械臂的杆长、关节限位完全一致否则模拟通过的动作在实机上会报Out of range。源码里相关的参数集中在.yaml配置中把 MoveIt 的 URDF 和 Unity 的模型参数放在同一个版本控制下维护是减少这类偏差最有效的手段。5. 化学实验场景的部署校验坐标系标定、动作录制与回放最后这部分是把前面所有链路串起来落到一个可以实际验收的实验动作上。部署顺序先用 Shell 脚本拉起 ROS master、rosbridge server 和 Mintasca 桥接节点然后启动 MoveIt加载 LERP 规划插件最后打开 Web 控制面板在 Unity 里加载数字孪生模型。部署脚本里有一个常见坑桥接节点和 MoveIt 同时启动时move_group会等待/joint_states发布数据如果桥接启动太慢MoveIt 超时会报Robot model out of date。建议在脚本里加一句等待# 等待桥接节点发布关节状态最多等 20 秒 timeout 20 bash -c until rostopic info /joint_states /dev/null 21; do sleep 1; done校准环节我用过一张固定的验收表先把末端移到实验台三个已知角点记录位置均方根误差再执行一次空置的抓取-倾倒动作测量动作耗时是否符合速度缩放设置最后连续运行十次观察轨迹是否完全一致。LERP 规划器最大的优势在这个环节体现得特别明显——轨迹的可重复性是天然的。调试过程中值得关注的一个方向是录制-回放把一次手动示教的关键点位和时间戳保存成 JSON 配方文件后续实验直接加载配方执行。这套源码本身支持可编辑程序设计这意味着实验流程的调整不需要改代码只改配方文件即可。配合 Git 管理配方文件每个实验版本的状态都可以追溯。实际效果立竿见影一个移液-搅拌-静置的流程从手动操作到全自动执行切换成本和误差都明显下降。本文还有配套的精品资源点击获取