最近一直在折腾一个挺有意思的方向:如何让灵巧手像人手一样自然地去抓取、拿捏、转动东西。单独说起来,灵巧手这些年已经不算冷门,真正让我重新提起兴趣的,是“ego”这个前缀。把第一人称视角引入到整个控制链里之后,问题会变得非常不一样。
我做的这个项目叫 EgoScale,核心就一句话:从第一人称视频中提取人手动作,再 retargeting 到绳驱灵巧手上执行。这里没有用传统的动作捕捉服、数据手套,也没有上庞大的端到端模型,而是走了一条看着朴素,但实际调试起来坑不少的路子——先做几何重建,再做尺度映射,最后交给欠驱动灵巧手去执行。
如果你手头也在做第一人称视觉、灵巧手操作、模仿学习相关的事情,或者你只是想找一个“从零开始把人类手部动作搬到机械手上”的完整工程参考,这篇文章应该能帮你省掉好几周的试错时间。我会把 EgoScale 里最关键的几个环节拆开讲清楚,尤其是我自己在 scale 这一步上踩过的那些坑。
1. 先看懂问题:ego、retargeting、灵巧手到底怎么咬合在一起
1.1 第一人称视角给整个任务带来了什么变化
很多人第一次听到 ego,第一反应是“以自我为中心”,但在机器人视觉里,它的含义要具体得多。换句话说,ego 指的是 egocentric vision,第一人称视觉。摄像头装在人的头上、眼镜上或者胸口,看到的画面就是人自己看到的画面,而不是站在旁边第三个人看到的画面。
这个差别看起来只是相机位置不同,实际影响非常大。第三人称视角下,相机稳定、观察范围大、人手相对完整,大多数做法都能跑得动。可一旦换成第一人称,手部会离镜头很近,运动范围大,自遮挡频繁,而且相机本身也在动,画面里的背景每帧都在变。你再也不能指望靠一个固定外参,把像素坐标简单转换成机器人坐标系里的三维位置。
但第一人称也有它不可替代的价值。人在操作东西的时候,真正依赖的就是自己看到的画面,视角天然和手部任务绑定。漫游式远程操作、AR 辅助示教、模仿学习的数据采集,这些东西用第一人称采集反而更自然。EgoScale 选了这个视角,也是在试图站在“数据最原始、最不依赖外部设备”的角度去解决控制问题。
1.2 retargeting 到底在重定向什么
retargeting 这个词,在动画领域早就存在,意思是把一段动作从一个骨骼结构搬到另一个骨骼结构上。人和机器人的骨骼尺寸不一样,关节自由度不一定对得上,所以不能直接复制角度,而要做一层映射。
在 EgoScale 里,retargeting 的对象是人手到灵巧手。人手本身就非常复杂,20 多个自由度,每个手指的骨骼长度、关节活动范围都不同。灵巧手也分好几种,有的全驱动,有的欠驱动,有的还引入了腱绳传动。不管下游手型多复杂,retargeting 要做的事情都逃不出这一步:从视频流里恢复人手的三维姿态,把它转换成一组合法、平滑、能执行的机器人关节指令。
这里面有两层设计选择。第一层,是直接映射关节角度,还是先映射指尖位置再反解机器人运动学。第二层,是纯几何规则映射,还是让网络去学一个端到端的映射关系。我最后选了“几何先验为主、动态尺度估计为辅”,原因很现实:端到端训练需要大量配对数据,而且一旦换了灵巧手型号,模型基本要重训。几何方法至少还能在换手之后,只改一层 URDF 参数就能继续调试。
1.3 为什么执着于灵巧手,而不是二指夹爪
如果只是做抓取,二指夹爪已经能覆盖不少场景,很多产线上也是这么用的。但夹爪的问题在于,抓取形态太受限了。对于球形物体、圆柱形物体、薄片、软性物体,夹爪要么需要频繁换治具,要么根本无能为力。灵巧手能做的操作类型丰富得多,而且更接近人的行为范式,这对未来复杂操作、人机协作来说意义更大。
当然,灵巧手的代价也很明显。自由度多,控制复杂,结构复杂,价格还不便宜。为了在成本和可靠性之间找平衡,我特意选了绳驱灵巧手。绳驱结构可以把电机放到手臂靠近腕部甚至前臂的位置,末端手部本身很轻,撞击时不会损坏电机。更重要的是,腱绳具备一定的柔性,天然对冲击有缓冲作用。这个特性在后面做 retargeting 的时候反而成了一个大变量,绳子会拉长、会滑动、会滞后,这些都必须在映射环节里提前考虑。
2. 整体方案设计与核心原理拆解
2.1 EgoScale 的总体流程:从一帧像素到一组关节角
整个系统的数据流大致可以分成五段。
第一段是图像采集,用头戴式相机或者胸挂式相机,以 30 帧以上的频率录制第一人称 RGB 视频。第二段是手部姿态估计,从图像里检测手部关键点,并恢复三维关键点坐标。第三段是尺度估计,也就是 EgoScale 的核心,要算出当前人手相对于机器人灵巧手的尺寸映射关系。第四段是 retargeting,把三维人手关键点映射为机器人关节角。第五段是底层控制,把目标关节角平滑处理后发给灵巧手执行。
这个流程看着直白,但每一步都有隐藏问题。比如姿态估计这步,虽然当前有很多现成方案,但第一人称视频里手部频繁遮挡,加上鱼眼、广角带来的畸变,关键点置信度波动会非常大。尺度估计更麻烦,因为单目相机无法直接给出真实物理尺寸,你需要额外的假设或者近似手法才能把像素距离换算成毫米。
为了跑通整个流程,我没有直接把所有模块一次性全做掉,而是先拆解成独立的小任务,逐个验证,最后再串联。这也是我给你的第一个建议:如果总流程第一次跑不通,你可以先离线处理录好的视频,把姿态估计、尺度、映射、仿真这些都验证完,再上真机。
2.2 Scale 是真正的重头戏:人手和机械手尺度不一致的数学本质
为什么项目叫 EgoScale,就是因为整个方案的成败都压在 scale 上。人的手和机器人灵巧手的物理尺寸差距很大,有的灵巧手只有人手一半大,有的是人手的一倍左右。简单粗暴地把人手关节角度直接复制过去是行不通的,因为骨骼长度不同,同样的关节角,指尖落点会差出一大截。
如果我们用几何的方式去思考这个问题,可以建立一个非常直观的模型。假设人手食指的长度是 L_h,灵巧手食指的长度是 L_r,那么理想的尺度缩放因子就是 s = L_r / L_h。对于末端位置映射,我们希望达到的效果是 P_robot = s * P_human,其中 P 表示指尖或者某个参考关键点的三维坐标。
但事情没这么简单,因为关节角是旋转量,不是平移量。如果直接在关节空间做缩放,会出现一个物理上很诡异的现象:你不改变角度,只改变骨骼长度,那指尖的位置变化其实是非线性的。这时候就有两条路线:
第一条是关节角映射,把人的每个关节角乘以一个系数,再截断到机械手限位。这个办法实现简单,但无法保证指尖位置对应关系,而且欠驱动手指一旦耦合,角度甚至无法直接独立控制。
第二条是末端位置映射,先把人手关键点转换成一组合法的指尖位置,然后利用机器人的正运动学和逆运动学,求解出对应的关节角。这条路线更符合“从一个机械臂换到另一个机械臂”的直觉,但在解逆解的时候需要非常注意奇异位形和关节限位。
EgoScale 同时也设置了一个动态尺度估计模块。因为人手在画面里的尺度不是一成不变的,手离相机近的时候,像素距离会放大,离远的时候又缩小。静态标定只解决“人手和机械手尺寸”的固定差异,动态尺度则解决“每一帧图像里的尺度漂移”。实际操作里,我会先用一次手部尺度静态标定,拿到一个基准 s,然后在每一帧根据手掌宽度、手指长度这些几何量做局部修正,最后再用低通滤波平滑,防止尺度参数在帧与帧之间跳变。
2.3 绳驱灵巧手的加入把问题又抬高了一截
用绳驱灵巧手,意味着关节角不是由电机直接驱动的,而是由腱绳拉动滑轮或者关节结构产生的。绳驱最大的特点在于电机可以放在远端,手部结构紧凑。但这也带来了一个问题:关节角与电机转角之间不再是固定的比例关系,中间隔着一根绳子,绳子的张紧程度、摩擦力、蜿蜒路径都会影响实际关节角度。
在 retargeting 的语境下,这相当于又多了一层运动学映射。你算出来的目标关节角,首先要转换到执行器的线长变化量,再转换成电机转角,最后通过底层 PID 或者力矩控制去执行。这个过程里最常见的现象就是“回程差”:绳子正转时和反转时不一定是同一个刚度状态,反向运动响应会慢一拍。
为了抵消绳驱的影响,我在设计 retargeting 策略时做了两个调整。一个是把目标值从“绝对位置”改成“相对位置”,每一帧只发送一个增量,让底层控制器顺着绳子的自然方向运动,减少频繁反向带来的空回。另一个是引入了最小张力预设,确保绳子始终处于张紧状态,避免松弛后出现突然的跳动。
2.4 为什么没有直接上端到端网络
开始做 EgoScale 之前,我被问过最多的问题就是:“现在都端到端了,你怎么还在做几何映射?”这个问题我很理解,端到端在视觉、语言领域确实是主流,但落到灵巧手操作上,我需要谨慎。
端到端路线真正卡人的不是模型本身,而是数据。要训练一个“第一人称视频到机器人关节角”的模型,你得同时采集大量 RGB 视频和真实的机器人执行数据。机器人执行数据的采集本身就慢,灵巧手自由度又多,标注成本更高。换成仿真环境来生成数据,又要面对 sim-to-real 的迁移问题,尤其是接触、摩擦、腱绳特性,迁移过去很容易失真。
几何 retargeting 至少在可控性上让人踏实。每个中间环节都有明确物理意义,出了问题能定位到具体模块。EgoScale 真正把几何使用时,也不需要训练一个完全从零的模型,现成的姿态估计网络已经足够强大,我只需要做一层轻量映射。这相当于把“智能”和“几何确定性”做了分工:视觉理解交给神经网络,动作映射交给几何约束。
3. 实操复现:从数据采集到灵巧手执行
3.1 数据采集装备与标定细节
想跑通 EgoScale,第一步不是写代码,而是把数据采集做好。我用的是一台头戴式广角相机,尽量固定在前额位置,稍微向下倾斜 15 到 20 度,确保手部在画面中央偏下。广角很重要,因为第一人称下手部离相机很近,如果视角不够宽,手稍微一抬就出画了,后面再好的算法也白搭。
帧率也需要注意。灵巧手的动作速度比人的手臂慢,但手部抓取瞬间的运动其实是很快的,至少 30 帧拍着才够用。如果预算允许,上 60 帧更好,后续做关键点追踪时会有明显优势。
相机拿到手后,第一件事是标定内参。我用的方法就是常规的棋盘格标定,OpenCV 里 calibrateCamera 就能搞定。这一步千万别省,因为手部离镜头很近,径向畸变和切向畸变会直接影响三维关键点的几何重建精度。我当时偷懒跳过了一次,结果 retargeting 出来的指尖轨迹明显扭曲,重新标定后才恢复正常。
然后就是手部尺度标定。在项目的第一版里,我让用户先把手平放在一张 A4 纸上,张开五指,相机记录几秒钟的数据。程序会自动测量中指到掌根的长度、手掌宽度,把这组数值存成一个标定文件。任何后续的 retargeting 都会直接读取这个文件,作为静态尺度的基准。
3.2 手部关键点估计与 retargeting 工程实现
手部关键点估计,我没有自己造轮子,直接用了现成的 MediaPipe Hands 作为主检测器。它能检测 21 个语义关键点,包括每个指节的关节和指尖,输出结果也带置信度,方便后续做遮挡处理。
但对于第一人称强遮挡场景,单靠 MediaPipe 还不够稳定。我加了一个简单策略:当某一帧的置信度低于阈值时,不直接采用当前检测,而是用上一帧结果做外推,同时把期望关节角保持在前一个安全状态。这个思路非常土,但对灵巧手的安全性很关键。
下面是一段简化的 retargeting 核心逻辑,我做成了伪代码,方便你理解整个流程:
import numpy as np # 静态标定数据 calib = load_calibration("hand_scale.json") s_offset = calib["scale_factor"] # 人手到机械手的基准缩放 # 每帧处理管线 for frame in video_stream: result = hand_landmarker.process(frame) if result.confidence < 0.5: continue # 跳过低置信度帧,保留上一状态 landmarks = extract_3d_landmarks(result) # 21个手部关键点 # 1. 动态尺度估计 hand_width = norm(landmarks["palm_base_l"] - landmarks["palm_base_r"]) s_dynamic = hand_width / calib["palm_width_pixel"] scale = s_offset * smooth(s_dynamic) # 低通平滑 # 2. 末端位置映射 target_tip_positions = {} for finger in ["thumb", "index", "middle", "ring", "pinky"]: tip = landmarks[f"{finger}_tip"] base = landmarks[f"{finger}_mcp"] target_tip_positions[finger] = base + scale * (tip - base) # 3. 逆运动学求解 joint_angles = ik_solve(target_tip_positions, robot_hand_urdf) # 4. 安全约束 joint_angles = np.clip(joint_angles, lower_limits, upper_limits) joint_angles = lowpass_filter(joint_angles, alpha=0.3) send_to_hand(joint_angles)这套逻辑里,最容易被忽略的是第 1 步里的 smooth 函数。动态尺度在每一帧都可能变化,如果不做平滑,scale 一会儿是 0.8,一会儿是 0.9,最终端关节角就会抖动得很明显。我采用的是一个简单的一阶低通,效果够用,如果你想要更稳,可以上卡尔曼滤波。
3.3 关键参数调节:缩放因子、关节限位与滤波
参数调节是整个项目里最磨人的环节,但也最看不到捷径。我给你列一下我最终调稳的一组参数,作为起点参考。
首先看缩放因子。静态缩放的初值可以从 URDF 里的骨骼长度直接算出来。比如人的食指近端指骨长度一般为 4 到 5 厘米,机器人手的对应段是 2.5 厘米,那么 scaling 就在 0.5 到 0.6 左右。实际用的时候,我会把静态缩放放到 0.55,然后在这个基础上让动态尺度参数在一个小范围内浮动,比如上下浮动 20%,不要太多。
关节限位这块,我的原则是宁紧勿松。灵巧手每个关节都有硬件限位,但实际跑 retargeting 时,不可能每次都精确卡在限位上,因为绳驱系统存在惯性,限位撞久了容易把绳子拉松。我在软件层把所有关节限位都向内缩了 5 到 8 度,虽然牺牲了一点动作范围,但可靠性提升非常明显。
滤波参数我分成两层。第一层是对关键点坐标做轻量平滑,alpha 取 0.4;第二层是对关节角做平滑,alpha 取 0.3。这个顺序不要反,如果先对关节角平滑,你会发现关键点抖动引起的突变还是会被 IK 放大,只不过看起来慢了一点。
延迟方面,我的实测端到端延迟大约在 80 到 120 毫秒之间,主要来自视觉检测和 IK 求解。对于灵巧手的示教任务,这个延迟还能接受,但如果要用来做实时远程操作,建议把视觉检测模型换成 TensorRT 或者 ONNX Runtime 的加速版本,延迟能压到 50 毫秒以内。
3.4 选型对比:不同灵巧手在 EgoScale 里的表现
| 手型 | 驱动方式 | 自由度 | retargeting 复杂度 | 稳定性 | 适用场景 |
|---|---|---|---|---|---|
| 全驱动手 | 电机直驱 | 16-20 | 中 | 高 | 高精度控制 |
| 腱绳手 | 腱绳+电机 | 12-16 | 高 | 中 | 低自重、近端电机 |
| 欠驱动手 | 腱绳+弹性件 | 8-12 | 高 | 中低 | 抓取自适应 |
| 气动手 | 气囊/气动肌腱 | 8-12 | 中 | 中 | 柔软交互 |
如果你刚接触这个方向,我不是很建议一上来就挑战欠驱动气动手,尺度标定和状态观测会让人崩溃。先拿一个自由度不多不少、带位置反馈的腱绳手跑通 EgoScale 的完整链路,之后再去挑战更复杂的执行器,体验会顺畅很多。
4. 常见问题与排查技巧实录
4.1 尺度漂移:灵巧手突然像“巨人手”或者“婴儿手”
这是 EgoScale 里最典型的问题。第一人称画面里,手离相机远近变化很频繁,如果尺度参数只依赖像素宽度,就会出现这种情况:手靠近镜头,像素宽度大,scale 变小,灵巧手动作幅度变小;手远离镜头,scale 变大,动作幅度变大。结果就是操作反馈极不稳定。
解决思路是这样:不能只靠单帧的像素宽度做绝对尺度,要结合手部三维姿态的深度信息。如果姿态估计模型输出了手掌的深度值,那么你可以先把手关键点的三维坐标转到相机坐标系下,再做深度归一化。这样即使手前后移动,尺度参数也会稳定很多。实在没有深度输出,也可以用一段时间窗口内的中位数来代替瞬时值,效果也算说得过去。
另外一个很实用的技巧是:在启动流程前,先让人手在画面里保持一个标准姿势三秒钟,程序自动计算一次基准尺度。运行过程中只在基准尺度周围小范围浮动,这样即便中途手突然凑近镜头,尺度也不会飞掉。
4.2 手部遮挡:抓东西时手被东西挡了一大半
第一人称下,手部遮挡是家常便饭。抓杯子的时候,手指被杯壁挡了,很多关键点直接消失或者置信度暴跌。一开始我以为只能靠更牛的视觉模型解决,后来发现工程上也有很多空子可以钻。
最简单的方法是置信度门控加动量保持。检测置信度低于阈值时,当前目标关节角不更新,同时开始计时。如果遮挡时间小于 0.3 秒,恢复检测后直接衔接;如果超过 0.5 秒,就强制让手回到一个安全张开位,避免在盲区里乱动。这个策略在抓取实验里救了我很多次。
对于部分手指被遮挡,还可以用“兄弟手指先验”。比如食指和中指在很多抓取动作里姿态接近,食指被挡时,可以用中指关键点做插值。这不严谨,但在紧急情况下非常管用。
4.3 绳驱迟滞:手指动作总感觉“慢半拍”
绳驱灵巧手天然存在回程差和弹性滞后。我实测下来,反向运动时大约有 30 到 50 毫秒的反应延迟,正转时也跟着有轻微滞后。如果 retargeting 每一帧都发绝对位置,绳子会不断在松紧之间切换,最终表现为关节抖动和噪声。
我后期把控制器从位置模式换成了位置加张力混合模式。具体做法是:每次到达目标位置后,不是立即停下来,而是让电机继续输出一个很小的力矩把绳子拉紧,保证绳子始终在张紧状态。这个保持张力大概只占最大力矩的 5% 到 10%,但去掉的抖动非常明显。
如果你没有张力反馈,也可以用增量式控制代替绝对位置。同样是每帧发送目标,但底层把目标量解释为“相对上一次位置的增量”,这样绳子的运动方向不会频繁反向,回程差的影响会降到最低。
4.4 关节角跳变:IK 解出来一个完全不合理的姿势
关节角跳变一般有两个来源。第一个是视觉关键点的突然跳动,比如手指检测从左手瞬间跳到右手,或者关键点索引匹配错误。第二个是 IK 的多解问题,灵巧手自由度多,同一个指尖位置可能对应多种关节组合,解算器可能在两个解之间来回切换。
针对视觉关键点跳动,可以在送入 IK 前增加一个数值变化率上限,任何关键点移动超过单帧距离阈值就视为异常,直接用上一帧值代替。针对 IK 多解,我给每个关节设置了优先级的偏好角度,比如手指自然弯曲是一个默认弯曲量,解算时优先逼近这个偏好,避免诡异姿势。
经验是,限制关节角速度比限制关节角位置更有效。只限制位置上限,关节很容易在限位上磨来磨去;限制速度上限之后,整个手运动的节奏感会好很多,给视觉和底层控制器都预留了缓冲时间。
4.5 常见问题速查表
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| 手部动作整体过大 | 静态缩放因子偏大 | 重新标定,缩小缩放因子 |
| 手部动作忽大忽小 | 尺度参数跳变 | 加入深度归一化和低通滤波 |
| 手指卡在限位不回 | 关节限位过紧 | 软件限位向内缩 5-8 度 |
| 反向运动抖动 | 绳驱回程差 | 改用增量控制,预设张力 |
| 抓取位置总偏 | 相机外参未标定 | 重新标定相机相对头部的位姿 |
| 视觉延迟过高 | 模型推理太慢 | 替换为 TensorRT 加速 |
| 关节角突然离谱 | IK 多解切换 | 加关节偏好角度和速度限制 |
结语:一点个人体会
做了 EgoScale 这一轮,最大的感受是:灵巧手的上限,很多时候不是被硬件决定的,而是被“映射”这层软件设计决定的。第一人称视觉天然适合操作任务,retargeting 是连接视觉和执行的桥梁,而 scale 问题则是这座桥梁最关键的支撑结构。如果没有人手与机械手之间的尺度理解,再好的姿态估计网络也无法让灵巧手做出自然动作。
如果再让我重做一遍,我会在项目最开始就引入动态尺度估计,而不是先用静态缩放跑通,再回头补。虽然静态方案能快速打通全流程,但在真实操作场景里的表现确实差得比较多。另外一定要给自己留一个仿真环境,哪怕是简单的 MuJoCo 模型,也能帮你快速排查 retargeting 和 IK 层面的问题,而不用每次都在真机上试错。
最后分享一个小技巧:在调试 canon 流程时,建议把每一帧的视觉关键点、尺度参数和目标关节角都同步录制下来,后续出问题时能直接回放比对。数据一旦积累起来,你不仅能优化 EgoScale 本身,甚至能为后续尝试端到端模型打下底子。这个项目我还会继续往下走,下一步准备把动态尺度估计换成一个轻量回归网络,看看能不能把手部操作的连贯性再往上推一档。