
GMR核心思路提出两阶段的ik求解方法作重定向比之前更准。也是基于mink的两阶段求解优化此处两阶段有先后关系。第一阶段求解结果第二阶段使用继续想到那关于warmup整体链路SMPL-X / BVH / Xsens / FBX 人体数据→ 统一为 {人体骨骼名: (全局位置, 全局四元数)}→ 按人体身高、骨段比例、坐标系偏移校准→ 生成机器人各 link 的 SE(3) 目标→ mink MuJoCo 求解带关节/速度约束的 IK→ 输出机器人 qpos 根位置 根四元数 关节角对比方法PHC, ProtoMotions两阶段的意义以 SMPL-X → G1 为例第一阶段高权重跟踪骨盆、左右脚的位置并主要对腿、躯干、手臂做朝向匹配。第二阶段从第一阶段结果继续求解加入髋、膝、肩、肘、腕的位置匹配并提高脚朝向的权重。落地主要靠人体目标的高度对齐和离线后处理而非接触优化我们发现使用PHC对于某些序列这会产生严重30厘米或更高的漂浮。我们通过在重定向序列上运行运动学来解决这个问题在每一帧存储最小身体高度然后用平均最小身体高度抵消整个运动。其他方法生成的重定向不需要类似的后处理。人体到目标的校准每个「数据源 × 机器人」有一份配置例如 general_motion_retargeting/ik_configs/smplx_to_g1.json:1。它定义人体骨骼到机器人 link 的匹配位置与旋转的权重每个骨骼的局部位置/旋转补偿不同四肢的缩放系数两阶段 IK 的目标集。原仓库代码提供一个全局offset手动调整小软件导入的是BVH动捕采集的数据可以选择全身任意一个关节调整他相对父级x,y,z轴的旋转deghip是人体基frame他相对于mujoco世界坐标系的旋转全局轴转换已由代码自动完成。这个面板额外用于修正每个关节可能存在的固定局部旋转偏差。关于零位对齐总结不处理第一帧求解ik自己对齐。关于坐标标定旋转的思考之前实验经常问题是人带着传感器手臂朝前机器人手臂乱走。本质就是坐标系标定旋转的问题。人带着tracker各个关节位置指导旋转也知道只是local frame(除世界坐标系外各个关节)传感器的和机器人的对不齐。但是通过记录机器人和人的位置计算RᵀR 很不准实际local frame有不是规则的单位矩阵没法简单改顺序和机器人对齐子关节的旋转不会改变“子关节自身的位置” 但会影响它的子孙关节的位置和姿态。我的问题全局轴转换 →人体 FK → 每个关键段的 rot_offset/pos_offset。BVH 子关节位置本来就是由父关节旋转和 OFFSET 正向计算出来的这两个如何理解 我理解全局轴转换对齐后对于子关节除了hip外的local frame原始方向其实不重要关键是相对于父frame怎么移动的。比如原始下local相对父frame x移动现在我旋转了local frame其实不影响关键是旋转后孙frame要正确变换即可回答因此如果只关心关节点位置子关节 local frame 的朝向确实不重要。只要父关节姿态和 OFFSET 正确子关节位置就能正确计算。如果关心子关节自己的姿态local frame 就重要因为 global_rot_child 包含了它的局部旋转。子关节的旋转不会改变“子关节自身的位置”但会影响它的子孙关节的位置和姿态。你说的“旋转 local frame 后只要孙 frame 正确变换即可”本质上就是正确的坐标系换基。但换基时必须同时修改该关节的局部旋转父关节坐标系中的 OFFSET所有子孙关节的局部旋转和局部偏移。不能只把当前关节的 X/Y/Z 交换一下。对于坐标系换基如果父 frame 的固定变换是 A_parent当前子 frame 的固定变换是 A_child则局部旋转应变为R_local,new A_parentᵀ R_local,old A_child局部偏移应变为offset_new A_parentᵀ offset_old这样重新 FK 后所有关节点的世界位置保持不变只是每个 frame 的坐标表达方式改变。总结Local frame 不标准可以转因为位置不变当前关节定位就准只是要相应给后续自关节都转。这样可以把全身local坐标系转成统一朝向处理。整体求解流程以smpl-x数据为例SMPL-X 全局关节数据→ 转 numpy→ 人体尺度调整→ 姿态/位置补偿→ 地面偏移→ 设置 Mink FrameTask 目标→ table1 IK→ table2 IK→ 返回 MuJoCo qpos一、Mink 实际求解什么每个FrameTask约束一个机器人 body 的位姿mink.FrameTask(frame_nameframe_name,frame_typebody,position_costpos_weight,orientation_costrot_weight,lm_damping1,)对于第 i 个目标Mink 内部会计算机器人 body 当前位姿目标位姿位置误差旋转误差当前 body 对机器人速度的 Jacobian概念上每次求解的是一个速度型 QPmin_v Σᵢ ‖Wᵢ (Jᵢ v − eᵢ / Δt)‖² λ‖v‖²其中v机器人广义速度Jᵢ第 i 个 body 的位姿 Jacobianeᵢ当前 body 到目标 body 的 6D 位姿误差Wᵢ位置和旋转权重λ阻尼项ΔtMuJoCo timestep求出的速度通过self.configuration.integrate_inplace(vel,dt)积分成新的机器人状态。所以这不是一次性解析 IK而是线性化当前状态解一个带约束的速度优化问题积分重新计算误差继续迭代。三、两阶段 IK对应代码在 motion_retarget.py。对于smplx_to_h1.jsonuse_ik_match_table1:true,use_ik_match_table2:true因此每一帧会依次执行table1 → 更新 Configuration → table2 → 更新同一个 Configuration每个 table 的执行方式都是先求解一次计算新误差如果误差下降超过 0.001则继续最多额外迭代 10 次。因此一组 table 最多执行 11 次 IK两个 table 最多约 22 次求解。注意它判断的是curr_error-next_error0.001这是“误差下降量”阈值不是“绝对误差小于某个值”。如果第一次求解后误差只下降 0.0005就会直接停止即使误差仍然很大。五、人体缩放配置中所有人体关节的基础缩放大多是1.1human_scale_table:{pelvis:1.1,spine3:1.1,left_hip:1.1,right_hip:1.1,...}初始化时会计算scale_ratio actual_human_height / 1.8最终缩放值为effective_scale config_scale × scale_ratio例如人体高度正好是 1.8 m则effective_scale 1.1也就是说这个配置会把人体骨架整体放大约 10%。这通常是为了补偿SMPL-X 人体模型和 H1 的比例差异body 原点和真实关节中心的差异机器人机械结构与人体关节位置的差异。缩放实现不是简单地把所有世界坐标乘一个系数而是scaled_root root_pos × root_scale scaled_body scaled_root (body_pos − root_pos) × body_scale因此人体根节点先被缩放然后其他关节围绕人体根节点进行局部缩放。六、H1 的关节链H1 XML 的腿部结构是pelvis ├─ left_hip_yaw_link │ └─ left_hip_roll_link │ └─ left_hip_pitch_link │ └─ left_knee_link │ └─ left_ankle_link └─ right_hip_yaw_link └─ right_hip_roll_link └─ right_hip_pitch_link └─ right_knee_link └─ right_ankle_link躯干和手臂pelvis └─ torso_link ├─ left_shoulder_pitch_link │ └─ left_shoulder_roll_link │ └─ left_shoulder_yaw_link │ └─ left_elbow_link └─ right_shoulder_pitch_link └─ right_shoulder_roll_link └─ right_shoulder_yaw_link └─ right_elbow_linkH1 关节角的 XML 顺序是left_hip_yaw left_hip_roll left_hip_pitch left_knee left_ankle right_hip_yaw right_hip_roll right_hip_pitch right_knee right_ankle torso left_shoulder_pitch left_shoulder_roll left_shoulder_yaw left_elbow right_shoulder_pitch right_shoulder_roll right_shoulder_yaw right_elbow配置文件中的字典顺序不决定qpos顺序真正顺序来自 H1 XML。七、人体到 H1 body 的映射可以将两个 table 总结为H1 bodySMPL-X bodytable1 位置/旋转table2 位置/旋转pelvispelvis100 / 1010 / 5left/right_hip_roll_linkleft/right_hip0 / 1010 / 5left/right_knee_linkleft/right_knee0 / 1010 / 5left/right_ankle_linkleft/right_foot100 / 1050 / 10torso_linkspine30 / 1010 / 0left/right_shoulder_roll_linkleft/right_shoulder0 / 1050 / 5left/right_elbow_linkleft/right_elbow0 / 1010 / 5权重不是百分比而是 QP 的代价权重。例如left_ankle_link的位置权重为 100说明脚的位置非常重要left_hip_roll_link在 table1 中位置权重为 0只使用方向约束left_shoulder_roll_link在 table2 中位置权重为 50说明第二阶段会重点修正肩部空间位置torso_link在 table2 中旋转权重为 0因此腰部旋转主要由 table1 完成。位置误差通常以米为单位旋转误差以弧度为单位因此不能直接把100和10理解为简单的十倍关系。八、为什么目标 body 没有覆盖所有 H1 关节例如配置中没有直接约束left_hip_yaw_linkleft_hip_pitch_linkleft_shoulder_pitch_linkleft_shoulder_yaw_link这是因为 FrameTask 约束的是一个 body 的完整世界位姿而不是单个关节。例如left_shoulder_roll_link的位姿受到上游的shoulder_pitch shoulder_roll影响left_elbow_link的位姿又受到shoulder_pitch shoulder_roll shoulder_yaw elbow影响。因此通过肩部 body 和肘部 body 的位置/方向约束可以间接求出整条手臂的 4 个关节。同理脚踝、膝盖、髋部 body 的组合约束可以间接求出腿部 5 个关节。十、ground_height-0.05的实际效果这是该配置最容易误解的地方。初始化时self.groundik_config[ground_height]*np.array([0,0,1])因此ground [0, 0, −0.05]之后建立 offset 时pos_offset-self.ground所有原本为[0, 0, 0]的位置 offset 会变成[0, 0, 0.05]所以当前代码实际会对每个目标 body 添加一个“沿其局部 z 轴的 5 cm 偏移”然后再旋转到世界坐标。这不是把整个人体统一沿世界 z 轴移动 5 cm。十一、当前实现中一个重要的 table2 问题setup_retarget_configuration()确实建立了self.pos_offsets2 self.rot_offsets2但在update_targets()中始终只调用human_dataself.offset_human_data(human_data,self.pos_offsets1,self.rot_offsets1)随后 table2 使用的仍然是已经经过 table1 offset 处理过的人体数据。因此table2 的位置/旋转权重有效table2 的 robot body 映射有效table2 自己的pos_offset/rot_offset实际没有被应用。对于当前 H1 配置影响不算特别明显因为两组四元数大部分相同脚踝只是符号相反但如果以后单独调 table2 的坐标补偿这些修改不会生效。总结smplx_to_h1.json的设计思路是用 table1 先稳定 pelvis、脚踝和全身方向用 table2 再补偿髋、膝、肩和脚的位置通过人体缩放和固定四元数把 SMPL-X 坐标系转换到 H1 body 坐标系通过 H1 XML 的关节限制保证输出落在可行范围内。先优化 table1再单独优化 table2而不是table1 永远严格满足table2 只在 table1 的可行空间中优化