text-to-cad SRDF 末端执行器指南:从<end_effector>语义到 MoveIt2 位姿求解交接
【免费下载链接】text-to-cadA library of agent skills for CAD, CAE and CAM项目地址: https://gitcode.com/GitHub_Trending/tex/text-to-cad
本文基于 text-to-cad 仓库 SRDF 技能的 end-effectors 参考文档,讲解 MoveIt2 SRDF 中末端执行器(end effector)的语义模型、必备台账字段、拓扑检查清单,以及如何把目标/TCP 连杆显式地交接给 CAD Viewer 的 MoveIt2 位姿求解协议。读完你可以独立编写<end_effector>条目、理解校验器对末端拓扑的报错含义,并构造可直接被srdf.solvePose协议消费的请求载荷。
末端执行器是什么:SRDF 中的语义标注而非物理结构
在 text-to-cad 的 SKILL.md 中明确了格式边界:URDF 拥有物理结构(links、joints、几何、惯性、限位),而 SRDF 拥有 MoveIt 规划语义——虚拟关节、被动关节、规划组、组状态、末端执行器与禁用碰撞对。末端执行器属于后者:它是对末端工具组(夹爪、传感器头、焊枪或其他终端部件)的语义命名,通常通过固定关节或附着连杆连接到父规划组上。
这一点决定了编写末端执行器时最常见的失败模式不是 XML 语法错误,而是"看起来合理、规划语义却是错的":组归属错了、TCP 连杆推断了、父连杆位置不对。因此该参考文档要求先记录台账字段、再写 XML,而不是凭视觉印象直接命名。
典型形态:<group>+<end_effector>组合
end-effectors.md 给出的典型形态如下:
<group name="gripper"> <joint name="finger_joint"/> </group> <end_effector name="gripper_eef" parent_link="tool0" group="gripper" parent_group="manipulator"/>四个属性各自的角色:
| 属性 | 含义 | 校验要求 |
|---|---|---|
name | 末端执行器唯一名称 | 必填,且不能与其他末端执行器重名 |
group | 末端执行器自身的规划组(如gripper) | 必须在同一 SRDF 中已定义 |
parent_group | 父规划组(如manipulator),可选 | 若填写则必须已定义,且必须满足拓扑约束 |
parent_link | 末端执行器附着在哪个 URDF 连杆上(如tool0) | 必须存在于配对 URDF 的 link 表中 |
源码层面,source.py 中的解析器会读取这四个属性并归一化为SrdfEndEffector数据类(source.py#L58-L63),遍历robot根节点下所有end_effector元素,name缺失直接报missing_end_effector_name错误(source.py#L252-L273),重名报duplicate_name。group与parent_group在此阶段允许为空字符串——它们的存在性与拓扑合法性推迟到与 URDF 交叉校验阶段处理。
规划台账:写 XML 之前先记录七个字段
参考文档要求"Required ledger fields"——在动笔前必须记录:
- 末端执行器名称;
- 末端执行器组(group);
- 父规划组(parent group);
- 末端执行器附着的父连杆(parent link);
- 用于 IK 与规划的目标/TCP 连杆;
- 末端执行器组与父组是否重叠;
- 父连杆在 URDF 图中是否与末端执行器组相邻。
这套字段与 planning-ledger.md 中"End effectors"表格一一对应(planning-ledger.md#L56-L62),表头为:
| Name | End-effector group | Parent group | Parent link | Target/TCP link | Overlap checked? | Adjacent? | Notes |其中两条设计原则值得强调:
- 末端执行器组通常不应与其父组共享连杆;
- 当目标/TCP 连杆与"推断出的组 tip"不一致时,必须显式记录 TCP 连杆,不能依赖运行时推断。
台账的意图是让规划假设在写 XML 之前就被显式化。SKILL.md 的工作流第 8 步也呼应了这一点:"Define end effectors after group membership is known"——末端执行器要在组归属确定之后定义。
编写前的检查清单
参考文档列出了六项编写前检查:
- 末端执行器组存在;
- 若指定了父组,父组存在;
- 父连杆存在于 URDF 中;
- 末端执行器组与父组不共享连杆;
- 父连杆属于父组,或紧邻末端执行器组;
- 目标/TCP 连杆与推断的组 tip 不同时,必须显式指定。
文档特别指出:"当前运行时会强制执行其中若干检查,但目标/TCP 的选择始终是语义决策。当规划目标是工具中心点(TCP)时,不要依赖推断。"这是本文档最核心的工程判断:运行时能证明的是拓扑一致性,证明不了你对 TCP 的意图。
校验器如何强制执行这些检查:源码级拆解
SRDF 技能自带校验器,命令形态见 SKILL.md:
python scripts/validate path/to/robot.srdf python scripts/validate path/to/robot.srdf --strict python scripts/validate path/to/robot.srdf --format json校验器一次性收集所有发现(severity、code、XML path),并与同目录、同名<robot name>的配对 URDF 做交叉验证。针对末端执行器,cli.py 中的_validate_srdf_against_urdf依次执行三类存在性检查(cli.py#L338-L367):
| 发现码 | 含义 | 触发条件 |
|---|---|---|
missing_end_effector_parent_link | parent_link引用的连杆在 URDF 中不存在 | 与配对 URDF 的 link 集合比对 |
missing_end_effector_group | group引用的规划组未定义 | 与本 SRDF 内已解析的组名比对 |
missing_end_effector_parent_group | parent_group引用的规划组未定义 | 同上,仅当该属性非空时检查 |
随后调用_validate_end_effector_topology(cli.py#L632-L683)执行三项拓扑检查,这正是检查清单中"可被运行时强制执行"的部分:
- 组重叠检查
end_effector_group_overlap:用_link_names_for_group(cli.py#L574-L615)分别展开末端组与父组的实际连杆集合——它不仅看组里直接写明的<link>,还会沿组内<joint>、<chain>(沿 URDF 树展开关节路径)和子组递归推导连杆。两个集合有交集即报错,并列出具体重叠连杆。 - 父连杆必须在父组内
end_effector_parent_link_outside_parent_group:parent_link若不属于父组的推导连杆集合,说明附着点与父规划组脱节。 - 父连杆必须与末端组相邻
end_effector_parent_link_not_adjacent:父连杆既不在末端组内,又不与末端组中任何连杆由某个 URDF 关节直接相连,即拓扑断裂。相邻判断由_joint_adjacent_to_any_link完成(cli.py#L618-L629),它遍历 URDF 关节表,看是否存在一个关节把parent_link与末端组内某连杆直接连成 parent-child。
注意这些检查都建立在配对 URDF 是一棵单根树的前提上;若 URDF 本身不是单根树,校验器会先发出paired_urdf_not_a_tree警告(cli.py#L243-L252),提示链式与相邻判断可能不可靠,建议先用 URDF 技能校验。
一个实用推论:校验器对"重叠"的判定基于推导连杆集而非字面成员。若父组用<chain base_link="base_link" tip_link="wrist_3_link"/>定义,末端组又包含wrist_3_link,即使两组文本上没有字面重复,end_effector_group_overlap仍会触发——写 chain 组的作者尤其要留意 tip 连杆是否同时落进了末端组。
目标/TCP 交接:CAD Viewer 的srdf.solvePose载荷
文档最后一节处理与$cad-viewer的交接:当把 SRDF 交给 CAD Viewer 做可选的 MoveIt2 交互控制时,应尽可能显式声明目标连杆。完整示例(直接摘自参考文档):
{ "protocolVersion": 1, "type": "srdf.solvePose", "payload": { "file": "robot.srdf", "target": { "endEffector": "gripper_eef", "targetLink": "tool0", "frame": "base_link", "xyz": [0.4, 0.0, 0.2], "quat_xyzw": [0, 0, 0, 1] }, "moveit2": { "planningGroup": "manipulator", "targetLink": "tool0" } } }各字段与前面台账的对应关系:
target.endEffector:引用 SRDF 中的<end_effector name="...">,即台账字段 1;target.targetLink:目标/TCP 连杆,即台账字段 5——即使与组 tip 相同也建议显式写出;target.frame+xyz+quat_xyzw:以base_link为基准系表达的位姿;moveit2.planningGroup:规划用哪个父规划组(台账字段 3);moveit2.targetLink:IK 求解指向的连杆,应与target.targetLink一致。
服务端行为可以从 cad-viewer 技能的 MoveIt2 服务源码确认:protocol.py 声明支持的请求类型为srdf.solvePose与srdf.planToPose,dispatcher.py 按request.type路由到对应处理器;context.py 解析时把moveit2.planningGroup与target.get("targetLink")/target.get("link")作为目标连杆的取值链(context.py#L780-L825),并会验证planningGroup非空、targetLink必须引用真实存在的 URDF 连杆,否则抛MotionProtocolError。
两条使用规则来自参考文档原文:
- 仅在姿态确实不需要约束时才使用 position-only IK——载荷中给出完整四元数(如示例中的单位四元数)意味着姿态参与求解;
- CAD Viewer 负责本地 MoveIt2 服务的启动与协议细节——SRDF 作者只负责把目标连杆语义写对,不需要在 SRDF 中嵌入任何服务配置。
MoveIt2 服务的本地启动方式见 moveit2-server.md:
npm --prefix scripts/viewer run moveit2:setup npm --prefix scripts/viewer run moveit2:check npm --prefix scripts/viewer run moveit2:serve服务默认监听ws://127.0.0.1:8765/ws;在 CAD Viewer 中打开.srdf文件后展开右侧MoveIt2面板,即可设置规划组、末端执行器、目标帧、XYZ 坐标,以及求解器超时、尝试次数与容差。该面板与上文 JSON 载荷是同一套协议的手工/自动两种入口。
收尾验证
完成<end_effector>编写后,回到 SKILL.md 的强制工作流:运行scripts/validate对每个新建或修改的.srdf做交叉验证,修复发现直至干净;--strict会把警告也当作失败,--format json输出机器可读的发现文档,便于接入 CI 或 Agent 循环。若本地有 MoveIt 环境,再跑 Setup Assistant 或项目 launch 做烟雾测试。
最后提醒参考文档反复强调的两点边界:其一,end_effector条目的正确性是"规划语义"问题,视觉渲染审阅不能证明规划正确;其二,凡是被推断出来的目标连杆、组归属或 TCP 选择,都应在交付说明中如实报告为假设,而不是当作已验证的事实。
【免费下载链接】text-to-cadA library of agent skills for CAD, CAE and CAM项目地址: https://gitcode.com/GitHub_Trending/tex/text-to-cad
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考