CORE-05 · PRINCIPLE LAB · 原理补充模块(新增)

机械臂抓取 · 原理实验室

把"抓取"拆回六个原理:坐标系与 TF → 正运动学 → 逆运动学 → 轨迹与分步移动 → 六阶段抓取(3D)→ 视觉定位。每个模块 = 一句原理 + 一个讲义真实案例 + 一台可拖可调的交互演示;数学概念(坐标变换、运动学、轨迹插值)全部配交互式图示,滑块一动、数值与图形同步变。3D 部分基于 three.js 本地渲染,离线可用。

🧩 6 个原理模块🎯 每模块 1 个讲义案例🎚 全参数滑块可调🤖 three.js 3D 抓取台📐 数学公式互动图示
TEACHING OUTLINE · 教学大纲

新增模块总览:目标 · 案例 · 交互方案 · 衔接

下表即本补充内容的结构化大纲;每行可点击跳入对应模块,"衔接"列指回 7 个实验页的真实章节锚点。

模块教学目标对应案例(讲义出处)可视化 / 交互方案与现有教程衔接
M1 坐标系与 TF 变换 同一空间点在 base_link 与相机光学系下的两套读数互换;理解 4×4 齐次变换"乘点"与 REP-103 轴向差异 EXP-07 视觉链:相机系点 (0,0,0.5) → base_link (0.55,0,0.40)(讲义·实验7 步骤4);EXP-06 set_pick_target 必须写 frame_id='base_link'(实验6 步骤1) TF 树路径高亮 + 源/目标系查询;外参滑块(平移 t_x/t_z、俯仰 γ)驱动双面板(俯视+侧视)实时重投影;旋转矩阵数值联动 EXP-01 §TF 链与角色 · EXP-06 §实操
M2 正运动学 FK "关节角 → 末端坐标"的确定性映射;会用 2R 平面模型手算并与代码互验;理解 PWM↔rad 换算 EXP-02/05 安全红线"抓取前先预演":pwm_to_fk_demo 输入五路 PWM 2150/1200/2200/2275/1500(讲义·实验5 步骤2) 5 路 PWM 滑块(500~2500)→ 实时换算弧度 → 双视图(俯视 x-y、侧视 r-z)画连杆;公式逐项高亮联动 EXP-03 §FK 试算台 · EXP-03 §实操
M3 逆运动学 IK 三段式解析思路(q1 指向 → 2R 余弦定理 → pitch 补偿);多解(肘上/肘下)与不可达判定;闭环验证意识 EXP-06 固定抓取目标 (0.28, 0, 0.10)、pitch=180° 即四元数 {0,1,0,0}(讲义·实验6 步骤1) 侧视工作平面内拖拽目标点 → 实时解出 θ1/θ2/θ3 与余弦定理代入过程;肘上/下切换;一键"喂回 FK"闭环测毫米误差;附 DLS 阻尼 λ 迭代动画 EXP-03 §动画 · EXP-04 §实操
M4 轨迹与分步移动 理解"为什么不能一步到位":插值、限位、PWM 安全边界;看懂 _move_stepwise 的 40%/60% 分段逻辑 EXP-06 分步移动:J1 偏转 >0.05 rad 时先单动 J1(40% 时长)再动 J2–J5(60%)(讲义·实验6 知识点 6.1.3);EXP-05 YAML 逐行回放同理 q(t) 曲线可调:起点/终点/总时长/分段开关/平滑度/限位开关;t 指针播放 + 侧视小棒人同步;PWM 越界实时红警 EXP-05 §回放实操 · EXP-06 §六阶段动画
M5 六阶段抓取 · 3D 以 three.js 观察机械臂结构、运动姿态与完整抓取过程;把 gripper_depth / pre_grasp_height 的语义变成"看得见的位置" EXP-06 _execute_pick 六步:开爪 → 移到 z+pre_h → 下探 z−gripper_depth → 合爪 → 退回 z+pre_h → 移到 post_grasp_pose(讲义·实验6 知识点 6.3.2),目标 (0.28,0,0.10)、放置点 (0.059,0.074,0.219) three.js 3D 视口(可旋转缩放):结构模式下 3 关节滑块调姿态 + TF 三色轴开关;抓取模式下六阶段按钮/自动播放 + 参数滑块(gripper_depth、pre_grasp_height、目标点)实时重算 IK 并动画执行,附 TCP 读数与坐标四元数 EXP-06 §参数调优台 · EXP-06 §实操 · EXP-01 §URDF 联动
M6 视觉定位 掌握抓取链的"眼睛":HSV 为什么抗光照、阈值怎么调;针孔反投影从 (u,v,d) 到 (X,Y,Z);再经 TF 换算给 IK EXP-07 全链路:红色两段 HSV 阈值 (0,100,80)~(15,255,255)∪(160,100,80)~(180,255,255)、fx=fy=614/cx=320/cy=240、深度 uint16 mm÷1000、性能基线成功率 >85%(讲义·实验7 步骤2/3 与步骤12) 合成场景实时 HSV 掩膜(H/S/V 双段滑块 + 质心拾取)→ 深度 d、焦距 fx 滑块 + 点击像素 → 反投影相机系 → 一键 TF 到 base_link,俯视图画目标点 EXP-07 §HSV 调参台 · EXP-07 §三乐章动画
使用建议:按 M1→M6 顺序过一遍(正好是抓取数据流:定位 → 求解 → 运动 → 抓 → 看);每模块先读"案例"再玩演示,最后点底部回链到对应实验页完成实操。数学弱的读者先拖滑块看现象,再对照公式逐项核——每个公式的每一项都有对应滑块实时改写。
MODULE 1 · 坐标变换

M1 · 坐标系与 TF 变换链:同一个点,两套读数

教学目标:① 说清 base_link 与 camera_depth_optical_frame 的轴向差异(REP-103);② 会读 4×4 齐次变换并手动"乘"一个点;③ 明白抓取链里为什么视觉算出的点必须先 transform 到 base_link 才能喂给 IK。

🎯 案例(讲义 · 实验7 步骤4「TF 坐标变换」):深度反投影先得到相机系的点——把红色方块放在相机前 0.5m,点是 (0, 0, 0.5);而抓取 IK 只认 base_link,经过 TF 变换后同一个点变成 (0.55, 0, 0.40)。帧链:map → odom → base_link → link1…link5 → tcp_link → camera_depth_optical_frame(相机在腕末端——眼在手)。 出处:EXP005_lab_guide.md 实验7 · transform_to_base_link() / tf_buffer.transform();实验6 步骤1:set_pick_target 消息必须写 frame_id:'base_link'。
map→odom→base_link→link1→link2→link3→link4→link5→tcp_link→camera_depth_optical_frame
红色高亮 = 当前查询路径 base_link ⇄ camera_depth_optical_frame(点进去的每一"跳"都被 tf_buffer.lookup_transform 自动拼接)

交互① · 点的搬运:拖滑块看两套读数怎么变

交互② · 变换矩阵:数值随滑块联动

P₀ = [ 0.000, 0.000, 0.500 ]ᵀ // 相机系读数
R = Rot_Y(0°) · R₀ ,  R₀ = X←Z, Y←−X, Z←−Y // REP-103 光学系
P_base = R · P₀ + t ,  t = [0.05, 0, 0.40]
Tcam→base =
怎么读:点列向量左乘矩阵 = "换语言"。γ=0、t=(0.05,0,0.40) 时输入讲义例子恰好得到 (0.55, 0, 0.40) ——这就是抓取链每 30ms 真实跑一遍的运算。把 γ 调离 0 再看读数:外参歪一点,抓取点就系统性偏移一截(对应讲义"手眼标定未接入则用 URDF 固定外参"的提醒)。
数学直觉:旋转矩阵每一列是新坐标系的一个轴在旧坐标系里的坐标;求逆变换在刚体情形 = 旋转转置 + 平移反号,所以 TF 查询 (A→B) 与 (B→A) 永远互逆,点换来换去总能量得回同毫米。
MODULE 2 · 正运动学

M2 · 正运动学 FK:从「舵机脉宽」到「指尖坐标」

教学目标:① 建立"关节角 → 末端坐标"是唯一确定的正向映射这一直觉;② 会用 2R 平面模型手算 TCP 并与代码互验;③ 把 PWM↔弧度换算 rad=(PWM−1500)×0.002356 变成肌肉记忆。

🎯 案例(讲义 · 实验5 步骤2「端到端工具脚本」+ 实验5 安全红线):上真机前先"纸上预演"——对 pwm_to_fk_demo.py 喂入五路脉宽 2150 1200 2200 2275 1500,不发任何指令,纯数学算出末端在哪,确认不撞桌再动臂。下面把这组真实案例值做成了默认滑块。 出处:EXP005_lab_guide.md 实验5 步骤2(pwm_to_fk_demo.py);换算式出自实验2 知识点 2.1.2 / 实验3 知识点 3.2.2。

交互 · 5 路 PWM 滑块实时正解(教学 2R+yaw 模型)

θi = (PWMi − 1500) × 0.002356  →  θ = […] rad
r = L1·cos θ₂ + L2·cos(θ₂+θ₃) ,   z = d + L1·sin θ₂ + L2·sin(θ₂+θ₃) ,   x = r·cos θ₁ , y = r·sin θ₁
L1=0.105 , L2=0.180 , d=0.085 (讲义 2R 简化口径,同 EXP-03 试算台)

读图:俯视看"扫到哪",侧视看"伸多高"

判达:虚线圈 = 参考球 rmax=0.4m(讲义·工作空间扫描口径,来自 demo_fk_ik 参考值)。指尖落在圈外,IK 大概率报 unreachable。
安全联动:任一路 PWM 变红 = 超出 500~2500 机械限位区间——这正是讲义第一条安全红线要求"发之前先心算对应角度"的原因:换算不过来,就上滑块先看。
为什么 FK 是"简单"的:它是纯代入——同一组角算一万次结果都一样(确定、无迭代、μs 级)。真正难的反过来:知道指尖在哪,关节该怎么弯?→ 进 M3。
MODULE 3 · 逆运动学

M3 · 逆运动学 IK:把「想放哪」反算成「怎么弯」

教学目标:① 走一遍 car1 三段式解析法(指向 → 2R 余弦定理 → pitch 补偿的前两段);② 看懂多解(肘上/肘下)与不可达的几何判据;③ 会做 IK→FK 闭环验证;④ 感受 DLS 数值法阻尼 λ 的作用。

🎯 案例(讲义 · 实验6 步骤1):固定抓取把目标直接发给臂:set_pick_target 写 position {x: 0.28, y: 0.0, z: 0.10}、orientation {x:0, y:1, z:0, w:0}(= pitch 180°,夹爪垂直朝下)。IK 必须在几十毫秒内把这一个点翻成 5 个关节角——下面红点就是它。 出处:EXP005_lab_guide.md 实验6 步骤1(ros2 topic pub 示例);三段式推导见实验4 步骤2(ik-analytical.js)。

交互① · 在工作平面里拖目标点(红点可拖!)

第一段 q₁ = atan2(y, x) [− asin(d/√(x²+y²))]
第二段 D² = ρ² + (z−d)² ;   cos q₃ = (D² − L1² − L2²) / (2·L1·L2) = —
   q₃ = ±acos(·)(肘姿选 ±),   q₂ = atan2(z−d, ρ) − atan2(L2·sin q₃, L1+L2·cos q₃)
(第三段 PHI=pitch−(q2+q3) 分给 q4/q5 —— 姿态解见 EXP-03 讲解,本台先攻位置)

交互② · DLS 数值法:阻尼 λ 起什么作用

参数调试指引:λ 拖小(<0.03)→ 步子迈太大,越过目标来回横跳;λ 拖大(>0.5)→ 每步挪一点点,稳但慢;讲义默认 λ=0.1,奇异(臂快伸直)附近靠它兜底不发散。这就是"解析法快而挑构型、数值法慢而皮实"的一体两面(讲义·实验4 4.2.2)。
闭环的含义:点"喂回 FK"后误差应显示 ≈0.00mm——数学上自洽。但"求解成功 ≠ 解准确":实臂还要过模型一致性、机械间隙、标定这几关(讲义·实验5 闭环实测的教训,故验收线单独列在实验报告里)。
MODULE 4 · 轨迹规划

M4 · 轨迹与分步移动:为什么不能「一条指令甩到位」

教学目标:① 理解轨迹 = 角度随时间的函数 q(t),插值决定"怎么走";② 看懂 car1 的分步策略 _move_stepwise(40%/60% 分时);③ 会用限位与 PWM 安全边界检查一条轨迹是否"能发"。

🎯 案例(讲义 · 实验6 知识点 6.1.3「分步移动」+ 实验3 动作回放):_move_stepwise()——当底座目标转角超过 0.05 rad 时,先只转 J1(占总时长 40%),转到位再动 J2–J5(60%);这样大扫动不会带着小臂横甩撞物。EXP-05 的 action_player 回放 YAML 也是同一思想:一帧一帧分时下发,而不是并发齐射。 出处:EXP005_lab_guide.md 实验6 知识点 6.1.3(arm_controller.py `_move_stepwise`);安全红线"目标位姿必须逐步逼近"见讲义实验1 注意事项。

交互 · q(t) 轨迹示波器(拖滑块改轨迹,按播放看小臂走)

同步动画:上视基座转角、侧视大臂俯仰

PWM 安全门:读数区实时按 PWM = θ/0.002356 + 1500 换算并检查 500~2500。把 J1 终点拖到 ±180°、打开限位再看曲线——超出 ±135°(J3 实际限位量级)的轨迹段会被钳位,这就是"软件软限位先替你挡一道"。
对比实验:取消"分步"→ 两条曲线同时起跑,基座带着大臂横甩(真实场景=撞桌上物);勾选分步→红线先走完蓝线才起步。平滑加减速关掉是直线斜坡,开则是 S 形缓入缓出——对应讲义"逐步逼近、小步试"的工程化表达。
MODULE 5 · THREE.JS 3D 抓取台

M5 · 六阶段抓取 · 3D 可视化:结构、姿态与"下探深度"的真身

教学目标:① 认识机械臂结构与每个关节的转轴方向(可开 TF 三色轴);② 按讲义六步走一遍完整 pick 序列;③ 把 gripper_depth / pre_grasp_height 从抽象参数变成看得见的两段高度差;④ 目标点滑到不可达区,亲眼看到 IK 拒绝作业。

🎯 案例(讲义 · 实验6 知识点 6.3.2 `_execute_pick()` 六步):开爪 → 移到 z+pre_grasp_height → 下探到 z−gripper_depth → 合爪 → 退回 z+pre_grasp_height → 移到 post_grasp_pose。目标点即案例值 (0.28, 0, 0.10)(pitch 180° 夹爪朝下),放置点 (0.059, 0.074, 0.219)。节点默认参数:gripper_depth=0.0(tcp 停在检测表面)、pre_grasp_height=0.03。 出处:EXP005_lab_guide.md 实验6 步骤1 命令示例、知识点 6.2/6.3;arm_controller.py 实际参数declare(工作区源码已核)。注意 lift_height 虽声明但源码未使用——抬升实由"退回 pre 高度"完成。
🏗 结构 · 姿态模式
✊ 六阶段抓取模式

结构模式 · 3 关节滑块直接摆姿态(更细的角进实验页)

看轴向:RGB 三色轴 = X 红 / Y 绿 / Z 蓝(REP-103)。转动 J1 时腕部坐标轴整体绕 Z 转——"TF 随动"就是 M1 里说的实时性;相机坐标轴插在腕上,所以臂一低头,相机看到的世界也跟着变(眼在手)。
MODULE 6 · 视觉定位

M6 · 视觉定位三件套:HSV 掩膜 → 深度反投影 → TF 进 base_link

教学目标:① 会调 HSV 阈值解释"检测不到/误检"两个经典故障;② 手算针孔反投影 X=(u−cx)·d/fx;③ 把三步串成 EXP-07 的完整定位链——抓取的"看见"就这三步。

🎯 案例(讲义 · 实验7 步骤2/3/4 + 步骤12 性能基线):目标认红色,两段并集阈值(红在 H 环两端):[0,100,80]~[15,255,255] ∪ [160,100,80]~[180,255,255];内参 fx=fy=614, cx=320, cy=240(HP60C,640×480);深度图 uint16 单位 mm,除以 1000 得米;OBSERVE 阶段须在 <1s 内完成"HSV+反投影+TF"三件套,整链成功率基线 >85%。 出处:EXP005_lab_guide.md 实验7 知识点 7.1.2/7.2(pixel_to_camera_frame)、步骤4(transform_to_base_link)、步骤12 性能基线表;阈值实参见工作区 visual_grasp.launch.py(已核)。

交互① · HSV 掩膜实验台(滑块动、掩膜立即变、质心跟着走)

RGB 场景(点击拾取像素 u,v)
inRange 掩膜(白=命中,十字=质心)

交互② · 深度反投影 → TF 到 base_link

K = [ fx 0 cx ; 0 fy cy ; 0 0 1 ] = [ 614 · · 320 ; · 614 · 240 ]
X = (u−cx)·d / fx , Y = (v−cy)·d / fy , Z = d
深度图 uint16 mm ÷1000 → m(HP60C,最低 0.1m,常有 0 空洞)
TF 桥接(光学系→base_link,γ=0 外参对齐讲义例子):
X_b = Z + 0.05 ,   Y_b = −X ,   Z_b = −Y + 0.40
故障演练:把 S 拉到 200 以上 → 红块变"褪色"部分被滤掉、质心跳动甚至消失(≈现场打强光/过曝);H 上限拉到 40+ → 橙色干扰块也被当目标(≈换了物体颜色没改参数)。讲义对策就两条:先宽后窄调六个阈值、换颜色光照改 4 个参数(7.6.2)。
为什么用 HSV 不用 RGB:V(明暗)与 H(色种)分离——关灯只降 V,红块的 H 纹丝不动,阈值不用跟着重调;这就是掩膜台拖 V 滑块时"只是整体变暗、红块始终被选中"的原理(讲义 7.1.1)。OBSERVE<1s 的时间预算里,这三步每步都必须是"一次遍历"级别的便宜操作。