Files
Technical-Report-LaTeX-Temp…/example/旧版个人example/main.tex
T
2026-07-24 12:01:53 +08:00

875 lines
35 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
\documentclass[12pt, a4paper]{article}
% ========== 基础包 ==========
\usepackage[UTF8]{ctex} % 中文支持
\usepackage[margin=2.5cm]{geometry} % 页边距
\usepackage{titlesec} % 章节标题格式
\usepackage{titletoc} % 目录格式
\usepackage{fancyhdr} % 页眉页脚
\usepackage{listings} % 代码块
\usepackage{xcolor} % 颜色
\usepackage{graphicx} % 图片
\usepackage{amsmath} % 数学公式
\usepackage{amssymb} % 数学符号
\usepackage{booktabs} % 表格
\usepackage{enumitem} % 列表格式
\usepackage{tcolorbox} % 彩色盒子(用于Q&A
\usepackage{fontawesome5} % 图标
\usepackage{setspace} % 行距
\usepackage{hyperref} % 超链接(放最后避免与其他包冲突)
\lstdefinestyle{bashstyle}{
backgroundcolor=\color{codebg},
basicstyle=\ttfamily\footnotesize,
breaklines=true,
frame=single,
rulecolor=\color{codeframe},
language=bash,
commentstyle=\color{codegreen},
keywordstyle=\color{blue},
}
% ========== 页面设置 ==========
\onehalfspacing % 1.5倍行距
% ========== 页眉页脚 ==========
\pagestyle{fancy}
\fancyhf{}
\fancyhead[L]{\small 机器人动作重定向文档}
\fancyhead[R]{\small \leftmark}
\fancyfoot[C]{\thepage}
\renewcommand{\headrulewidth}{0.4pt}
% ========== 超链接设置 ==========
\hypersetup{
colorlinks=true,
linkcolor=blue!70!black,
urlcolor=blue!70!black,
citecolor=green!60!black,
bookmarks=true,
bookmarksnumbered=true
}
% ========== 代码块设置 ==========
\definecolor{codebg}{RGB}{245, 245, 245}
\definecolor{codeframe}{RGB}{200, 200, 200}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\lstdefinestyle{pythonstyle}{
backgroundcolor=\color{codebg},
commentstyle=\color{codegreen},
keywordstyle=\color{blue}\bfseries,
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4,
frame=single,
rulecolor=\color{codeframe},
language=Python
}
\lstset{style=pythonstyle}
% ========== Q&A 盒子样式 ==========
\tcbuselibrary{skins, breakable}
\newtcolorbox{qabox}[2][]{
enhanced,
breakable,
colback=blue!5!white,
colframe=blue!60!black,
fonttitle=\bfseries,
title={Q: #2},
#1
}
\newtcolorbox{answerbox}[1][]{
enhanced,
breakable,
colback=green!5!white,
colframe=green!60!black,
leftrule=4pt,
#1
}
% ========== 文件说明盒子 ==========
\newtcolorbox{filebox}[2][]{
enhanced,
breakable,
colback=gray!10!white,
colframe=gray!60!black,
fonttitle=\bfseries\ttfamily,
title={\faFile\ #2},
#1
}
% ========== 章节标题格式 ==========
\titleformat{\section}
{\Large\bfseries\color{blue!70!black}}
{\thesection}{1em}{}
[\titlerule]
\titleformat{\subsection}
{\large\bfseries\color{blue!50!black}}
{\thesubsection}{1em}{}
\titleformat{\subsubsection}
{\normalsize\bfseries}
{\thesubsubsection}{1em}{}
% ========== 文档开始 ==========
\begin{document}
% ========== 封面 ==========
\begin{titlepage}
\centering
\vspace*{3cm}
{\Huge\bfseries 机器人动作重定向\\[0.5em]
\Large 技术文档}
\vspace{2cm}
\rule{\linewidth}{0.5mm}
\vspace{1cm}
{\large
\begin{tabular}{ll}
\textbf{项目名称:} & 人形机器人动作重定向系统 \\[0.5em]
\textbf{作者:} & 沈玉祥 \\[0.5em]
\textbf{日期:} & \today \\[0.5em]
\textbf{版本:} & v1.0 \\
\end{tabular}
}
\vspace{1cm}
\rule{\linewidth}{0.5mm}
\vfill
{\small 本文档记录了基于PINK差分IK的人形机器人动作重定向方法及实现细节}
\end{titlepage}
% ========== 目录 ==========
\tableofcontents
\newpage
% ============================================================
% 第一章:程序文件说明
% ============================================================
\section{程序文件说明}
\subsection{概述}
主要包括:人体动作到机器人动作的重定向方法、动作之间的平滑插值、视频提取到机器人动作的方法等内容。
% 在这里写一段整体项目结构的介绍
\subsection{核心文件}
% ---- 文件1 ----
\begin{filebox}{\texttt{ik\_redirection\_npy.py}}
\textbf{功能:} 将人体动作信息转化为机器人各关节角度(rad)信息。\\[0.5em]
\textbf{主要类/函数:}
\begin{itemize}
\item \texttt{H1Config} —— 机器人配置信息
\item \texttt{H1PinkSolver} —— 核心求解器
\item \texttt{process\_motion()} —— 主处理函数
\item \texttt{apply\_bone\_retargeting\_single()} —— 单帧骨骼重定向
\item \texttt{load\_data\_all\_npy()} —— 输入格式转换:$[N, 22, 3, T]$ -> 第 i 个 $[T, 22, 3]$
\end{itemize}
\vspace{0.5em}
\textbf{输入:} $[N, 22, 3]$ 的numpy数组,N为帧数,22为SMPL主要关节数,3为XYZ坐标
\textbf{输出:} 字典,主要为dof(关节角度序列,弧度,shape为 $[N, 28]$
\textbf{运行效率:}0.08s/196帧 $\approx$ 0.4ms/帧(单线程,CPU
\end{filebox}
\vspace{1em}
% ---- 文件2 ----
\begin{filebox}{\texttt{easy\_MotionInterpolator.py}}
\textbf{功能:} 实现机器人动作(关节角度dof)的插值、帧率重定向及平滑处理;可处理过渡的自碰撞问题。
\vspace{0.5em}
\textbf{主要配置项:}
\begin{itemize}
\item \texttt{MotionInterpolator} —— 核心插值类
\item \texttt{interpolate()} —— 对dof序列进行三次样条插值
\item \texttt{smooth()} —— 加权滑动滤波消除插值引入的抖动
\item \texttt{process()} —— 依次执行插值与平滑,返回处理后的dof序列
\end{itemize}
\vspace{0.5em}
\textbf{输入:} 低帧率的关节角度序列,shape为 $[N, 28]$
\textbf{输出:} 高帧率平滑后的关节角度序列,shape为 $[N', 28]$
\textbf{运行效率:}单次拼接(含碰撞检测)$\approx$
0.8ms(单线程,CPU
\end{filebox}
\vspace{1em}
\begin{filebox}{\texttt{vis.py}}
\textbf{功能:} 逐帧播放并可视化。
\vspace{0.5em}
\textbf{主要配置项:}
\begin{itemize}
\item \texttt{load\_motion\_data()} —— 加载动作数据
\item \texttt{build\_obstacle\_geoms()} —— 根据障碍物数据构建红色线框盒几何体
\item \texttt{main()} —— 逐帧绘制机器人关节角度与根节点状态
\end{itemize}
\vspace{0.5em}
\textbf{输入:} 包含dof序列、根节点位姿、可选的h1\_joint\_pos和smpl\_joints\_target
\textbf{输出:} Isaac Gym实时可视化窗口
\textbf{依赖:}Isaac Gym、PyTorch(支持GPU加速渲染)
\end{filebox}
% ---- 添加更多文件 ----
% \begin{filebox}{\texttt{文件名.py}}
% ...
% \end{filebox}
\subsection{服务器端文件}
\textbf{位置:} 服务器下的GVHMR目录下;conda activate gvhmr
\begin{filebox}{\texttt{GVHMR/run.py}}
\textbf{功能:} 把人体动作视频(mp4)转换成机器人各关节电机角度(rad)。
\vspace{0.5em}
\textbf{主要配置项:}
\begin{itemize}
\item \texttt{GVHMRSystem} —— 核心系统类
\item \texttt{\_\_init\_\_()} —— 冷启动初始化,加载GVHMR主模型、Tracker、VitPose、特征提取器、SMPL模型及H1PinkSolver
\item \texttt{process\_video()} —— 热运行入口,处理单个视频,依次执行预处理、GVHMR推理、H1 IK优化,返回关节角度结果
\item \texttt{\_run\_preprocess\_efficient()} —— SLAM后台线程并行,Tracker、VitPose、特征提取串行GPU推理
\item \texttt{\_run\_h1\_optimization()} —— 从GVHMR输出的SMPL参数提取关节坐标,调用H1PinkSolver完成重定向
\end{itemize}
\vspace{0.5em}
\textbf{输入:} mp4视频文件
\textbf{输出:} 包含dof关节角度序列(rad,shape为$[N,28]$)的字典
\textbf{使用方法:}
\begin{lstlisting}[style=bashstyle]
conda activate gvhmr
cd GVHMR/
python run.py --input (视频路径)
# 结果存储到 outputs 目录的 result.pkl 文件
\end{lstlisting}
\textbf{运行效率:}4.5s/8s4090GPU
\end{filebox}
\vspace{1em}
\begin{filebox}{\texttt{GVHMR/new-server.py}}
\textbf{功能:} 支持大模型工具调用的服务端。
\vspace{0.5em}
\textbf{主要配置项:}
\begin{itemize}
\item \texttt{\_run\_gvhmr()} —— 核心推理函数
\item \texttt{\_frames\_to\_tmp\_video()} —— 将帧列表写入临时视频文件
\item \texttt{\_process\_frames\_async()} —— 异步推理包装
\end{itemize}
\vspace{0.5em}
\textbf{主要接口:}
\begin{itemize}
\item \lstinline{GET /tools} —— 查询当前可用工具,大模型调用前先请求:
\begin{lstlisting}[style=bashstyle]
curl http://localhost:8003/tools\end{lstlisting}
\item \lstinline{POST /tool/call} —— 大模型调用工具的入口,传工具名和参数:
\begin{lstlisting}[style=bashstyle]
curl -X POST http://localhost:8003/tool/call \
-d '{"name": "process_video_to_robot_motion",
"parameters": {"video_path": "/data/demo.mp4"}}'\end{lstlisting}
\item \lstinline{POST /generate} —— 直接输入视频文件路径处理:
\begin{lstlisting}[style=bashstyle]
curl -X POST http://localhost:8003/generate \
-d '{"video_path": "/data/demo.mp4"}'\end{lstlisting}
\item \lstinline{WS /ws/stream} —— 接入实时相机推流,配合 camera\_client.py 使用:
\begin{lstlisting}[style=bashstyle]
python camera_client.py --camera 0\end{lstlisting}
\end{itemize}
\vspace{0.5em}
\textbf{输入:} 视频文件绝对路径,或通过 WebSocket 推送的 JPEG 编码帧字节流
\textbf{输出:} 字典,主要为 dof(关节角度序列,弧度,shape 为 $[N,28]$)、fps、duration\_sec
\end{filebox}
\vspace{1em}
\begin{filebox}{\texttt{GVHMR/camera\_client.py}}
\textbf{功能:} 获取本地摄像头,将视频帧实时推送到 new-server.py 进行推理,并接收返回的关节角度。\\[0.5em]
\textbf{主要函数:}
\begin{itemize}
\item \texttt{run\_camera\_stream()} —— 打开摄像头、推帧、接收结果
\item \texttt{\_handle\_result()} —— 处理每次推理返回的结果
\end{itemize}
\vspace{0.5em}
\textbf{使用方法:}
\begin{lstlisting}[style=bashstyle]
# 默认摄像头,默认参数
python camera_client.py
# 指定摄像头编号
python camera_client.py --camera 0 --fps 30 --chunk_sec 4
# 服务端不在同一台机器时指定地址
python camera_client.py --server 192.168.1.100:8003
# 固定机位相机
python camera_client.py --static_cam
\end{lstlisting}
\textbf{输入:} 本地摄像头实时画面(通过 OpenCV 读取)\\[0.5em]
\textbf{输出:} 每隔 \texttt{chunk\_sec} 秒打印一次推理结果,格式如下:
\begin{lstlisting}[style=bashstyle]
[推理结果] chunk_id=0 | 帧数=120 | 时长=4.0s | fps=90 | DOF shape=[360, 28]
\end{lstlisting}
\textbf{备注:} \texttt{\_handle\_result()} 函数内的注释处(第~117~行)可对接机器人控制器,将 DOF 数据发给 ROS 节点。
\end{filebox}
\vspace{1em}
\begin{filebox}{\texttt{GVHMR/test\_stream.py}}
\textbf{功能:} 用本地 mp4 视频文件模拟摄像头推流,测试 new-server.py 的视频流接口是否正常工作。\\[0.5em]
\textbf{主要函数:}
\begin{itemize}
\item \texttt{test\_stream()} —— 读取视频文件逐帧推送到服务端,并接收推理结果
\item \texttt{\_print\_result()} —— 打印每个 chunk 的推理结果
\end{itemize}
\vspace{0.5em}
\textbf{使用方法:}
\begin{lstlisting}[style=bashstyle]
# 基本用法(必须指定视频文件)
python test_stream.py --video /data/demo.mp4
# 指定参数
python test_stream.py --video /data/demo.mp4 --fps 30 --chunk_sec 4
# 服务端不在同一台机器时指定地址
python test_stream.py --video /data/demo.mp4 --server 192.168.1.100:8003
\end{lstlisting}
\textbf{输入:} 本地视频文件路径\\[0.5em]
\textbf{输出:} 每积累 \texttt{chunk\_sec} 秒的帧触发一次推理,打印结果:
\begin{lstlisting}[style=bashstyle]
[chunk 0] dof_shape=[360, 28], fps=90, duration=4.0s
[done] 共处理 2 个 chunk
\end{lstlisting}
\end{filebox}
\subsection{其他说明}
\begin{itemize}
\item \textbf{根节点固定:} 本模块的目标是从人体动作生成机器人电机角度序列,机器人waist点在世界坐标系中保持固定,相当于将机器人腰部悬挂于空间中。根节点的全局位移不参与重定向计算。
\item \textbf{输出崩溃排查:} 若输出的电机角度出现突变或崩溃,大概率是输入动作幅度过大,超出机器人关节限位范围,导致IK无法在单帧内追上目标。解决方法是适当降低大模型生成的动作幅度,或延长动作时长使运动更平缓。
\item \textbf{体型通用性:} 重定向模块对输入人体的臂长、身高、体型无要求,骨骼重定向阶段会自动将SMPL骨骼方向映射到机器人实际骨骼长度,不受输入人体比例影响。
\end{itemize}
% 在这里描述其他辅助脚本
\newpage
% ============================================================
% 第二章:重定向方法与原理
% ============================================================
\section{重定向方法与原理}
\subsection{常见的三种重定向方法}
常见的机器人动作重定向方法主要分为三类:梯度下降方法、神经网络模型+IK微调、纯IK逆解方法三种。
\begin{table}[h]
\centering
\caption{三种重定向方法对比}
\begin{tabular}{p{2.5cm}p{4cm}p{4cm}p{3cm}}
\toprule
\textbf{方法} & \textbf{优点} & \textbf{缺点} & \textbf{适用场合} \\
\midrule
梯度下降 &
动作质量高;批量处理性价比好 &
实时性差,处理数秒视频需2s以上;需预先标定缩放比例 &
离线批量处理 \\
\midrule
神经网络 + IK微调 &
推理速度快;兼顾数据驱动与几何约束 &
依赖大量高质量标注数据,准备成本高;对体型变化鲁棒性弱 &
数据充足的生产环境 \\
\midrule
纯IK逆解(本文) &
无需训练数据;实时求解;体型适应性好 &
需合理设计任务约束与权重 &
实时重定向 \\
\bottomrule
\end{tabular}
\end{table}
\subsection{整体流程}
本系统的重定向流程如下:
\begin{center}
\begin{tcolorbox}[width=0.85\textwidth, colback=blue!5, colframe=blue!40,
boxrule=0.5pt, arc=3pt]
\centering
\textbf{输入} SMPL关节坐标序列 $[N, J, 3]$ \\[4pt]
$\downarrow$ \\[2pt]
坐标系变换(quat旋转,对齐机器人坐标系)\\[4pt]
$\downarrow$ \\[2pt]
根节点归一化 $\rightarrow$ 比例缩放 $\rightarrow$ 平移至waist高度 \\[4pt]
$\downarrow$ \\[2pt]
Yaw朝向对齐(固定根节点朝向)\\[4pt]
$\downarrow$ \\[2pt]
Savitzky-Golay 平滑滤波 \\[4pt]
$\downarrow$ \\[2pt]
设定IK追踪任务与权重 \\[4pt]
$\downarrow$ \\[2pt]
逐帧骨骼重定向(动态Anchor + 骨骼长度替换)\\[4pt]
$\downarrow$ \\[2pt]
QP差分IK求解 \\[4pt]
$\downarrow$ \\[2pt]
加权滑动窗口平滑 $\rightarrow$ 脚踝pitch几何覆写 \\[4pt]
$\downarrow$ \\[2pt]
三次样条插值升采样至90fps \\[4pt]
$\downarrow$ \\[2pt]
\textbf{输出} 关节角度序列(rad$[N, 28]$
\end{tcolorbox}
\end{center}
% 描述整体 pipeline,可以用 itemize 列出步骤
\subsection{坐标系处理}
\subsubsection{SMPL坐标系与机器人坐标系的对齐}
SMPL以骨盆(pelvis)为根节点,机器人以waist为躯干中心,两者物理意义最接近,因此将pelvis对齐到机器人waist位置。具体分两步:以pelvis为原点中心化后乘以缩放系数,再平移至机器人waist高度:
\begin{lstlisting}[style=bashstyle]
# 中心化 + 缩放
smpl_joints = (smpl_raw_seq - smpl_raw_seq[:, 0:1, :]) * self.SMPL_SCALE
# 平移至机器人waist高度
waist_pos = self.configuration.get_transform_frame_to_world("waist").translation
smpl_joints += waist_pos.reshape(1, 1, 3)
\end{lstlisting}
% 描述 quat = [0.5, 0.5, 0.5, 0.5] 的作用
\subsubsection{朝向对齐预处理}
大模型输出的动作可能包含根节点移动(如走圈),直接重定向会导致机器人跟随转向。因此对每帧计算髋部左右向量的水平偏转角,绕Z轴旋转补偿,使全程朝向与第0帧保持一致:
\begin{lstlisting}[style=bashstyle]
# 计算每帧髋部向量的yaw角
hip_vecs = smpl_joints[:, idx_r] - smpl_joints[:, idx_l]
hip_vecs[:, 2] = 0 # 投影到水平面
yaw_ref = np.arctan2(hip_vecs[0, 1], hip_vecs[0, 0])
yaw_all = np.arctan2(hip_vecs[:, 1], hip_vecs[:, 0])
# 绕Z轴旋转补偿,固定朝向
rot_mats = sRot.from_euler('z', yaw_ref - yaw_all).as_matrix()
centered = smpl_joints - smpl_joints[:, 0:1, :]
smpl_joints = np.einsum('nij,nkj->nki', rot_mats, centered) + smpl_joints[:, 0:1, :]
\end{lstlisting}
% 描述固定根节点朝向的处理逻辑
\subsection{骨骼重定向}
\begin{figure}[h]
\centering
\begin{minipage}{0.45\textwidth}
\centering
\includegraphics[width=\textwidth]{before.png}
\caption{重定向前}
\end{minipage}
\hfill
\begin{minipage}{0.45\textwidth}
\centering
\includegraphics[width=\textwidth]{after.png}
\caption{重定向后}
\end{minipage}
\caption{重定向前后对比:绿色为H1机器人关节点,蓝色为SMPL目标关节点}
\label{fig:retarget_compare}
\end{figure}
\subsubsection{动态Anchor设计}
SMPL人体与机器人结构不同,肩部和髋部的关节位置无法直接对应。若直接以SMPL的肩膀点作为anchor,会有两个问题:一是两者骨骼比例不同导致位置偏差;二是人体上肢挥动时SMPL肩膀点存在上下位移,而机器人肩部三个电机的结构无法复现这种位移。
因此每帧从机器人当前正运动学结果中读取肩部、髋部、颈部的真实世界坐标作为anchor,再以SMPL骨骼的方向信息驱动末端位置:
\begin{lstlisting}[style=bashstyle]
# 每帧从机器人正运动学读取anchor点
anchors = {
'l_shoulder': self.configuration.get_transform_frame_to_world(
"left_upper_arm").translation.copy(),
'r_shoulder': self.configuration.get_transform_frame_to_world(
"right_upper_arm").translation.copy(),
'l_thigh': self.configuration.get_transform_frame_to_world(
"left_thigh").translation.copy(),
'r_thigh': self.configuration.get_transform_frame_to_world(
"right_thigh").translation.copy(),
'neck': self.configuration.get_transform_frame_to_world(
"neck_linkage").translation.copy(),
}
\end{lstlisting}
同时,利用SMPL的spine1、left\_collar、right\_collar三点构造正交基,得到躯干朝向,用于约束机器人胸部旋转,间接驱动上肢姿态跟随躯干运动:
\begin{lstlisting}[style=bashstyle]
# 三点构造胸部正交基
up = _normalize((p_l_collar + p_r_collar) / 2.0 - p_spine3)
right = _normalize(p_l_collar - p_r_collar)
forward = _normalize(np.cross(right, up))
right = _normalize(np.cross(up, forward))
chest_rot = np.column_stack([forward, right, up])
\end{lstlisting}
% 描述为什么要用动态anchor,以及如何实现
\subsubsection{链式骨骼长度替换}
SMPL与机器人的骨骼长度不同,直接使用SMPL关节坐标会导致末端位置偏差。解决方案是保留SMPL各骨骼段的方向向量,将长度替换为机器人实际物理尺寸,从anchor点出发链式计算各关节位置,使末端(手、脚)能够正确对齐:
\begin{lstlisting}[style=bashstyle]
# 机器人实际骨骼长度(米)
ROBOT_DIMS = {
'thigh': 0.0832, # 髋 -> 膝
'calf': 0.1105, # 膝 -> 踝
'upper_arm': 0.07579, # 肩 -> 肘
'forearm': 0.04739, # 肘 -> 腕
'neck2head': 0.022487329, # 颈部 -> 头
}
# 以anchor为父节点,沿SMPL方向链式扩展(以左腿为例)
nj[:, b['left_hip']] = anchors['l_thigh']
nj[:, b['left_knee']] = anchors['l_thigh'] \
+ _normalize(j[:, b['left_knee']] - j[:, b['left_hip']]) * d['thigh']
nj[:, b['left_ankle']] = nj[:, b['left_knee']] \
+ _normalize(j[:, b['left_ankle']] - j[:, b['left_knee']]) * d['calf']
\end{lstlisting}
左右臂同理,从肩部anchor出发,依次计算肘、腕位置。经过链式替换后,末端关节坐标既保留了SMPL动作的姿态方向,又符合机器人的实际骨骼比例。
% 描述 apply_bone_retargeting_single 的逻辑
\subsection{差分IK求解}
\subsubsection{PINK框架介绍}
PINKPython Inverse kiNematics)是基于Pinocchio的差分IK框架。Pinocchio负责机器人运动学/动力学计算,PINK在其基础上将IK问题构造为二次规划(QP)问题,通过定义多个带权重的\textbf{任务}Task)和\textbf{约束}Limit)来描述求解目标。
每个任务对应一个末端执行器的位置或旋转目标,权重决定各任务的优先级。求解器在满足关节限位约束的前提下,每帧求解一个关节速度增量,再积分更新机器人配置,从而逐帧追踪目标轨迹。本系统使用quadprog作为底层QP求解器,阻尼系数设为$10^{-3}$防止奇异。
% 简要介绍 PINK 和 Pinocchio
\subsubsection{任务设计与权重}
系统共定义以下IK任务,各任务的位置权重与旋转权重如表所示:
\begin{table}[h]
\centering
\caption{IK任务权重配置}
\begin{tabular}{llcc}
\toprule
\textbf{机器人Link} & \textbf{对应SMPL关节} & \textbf{位置权重} & \textbf{旋转权重} \\
\midrule
chest && 0.0 & 50.0 \\
left\_hand / right\_hand & left\_wrist / right\_wrist & 10.0 & 0.0 \\
left\_foot / right\_foot & left\_ankle / right\_ankle & 10.0 & 1.0 \\
left\_force\_arm / right\_force\_arm & left\_elbow / right\_elbow & 5.0 & 0.0 \\
left\_calf / right\_calf & left\_knee / right\_knee & 5.0 & 0.0 \\
head & head & 1.0 & 5.0 \\
\bottomrule
\end{tabular}
\end{table}
chest任务不追位置,只追旋转,用于约束躯干朝向防止后仰;手和脚作为末端执行器位置权重最高;肘和膝作为中间关节权重较低,主要用于引导肢体方向;头部以旋转约束为主。此外还加入了一个低权重(0.1)的PostureTask作为软约束,防止未被追踪的关节乱飘:
\begin{lstlisting}[style=bashstyle]
self.posture_task = pink.tasks.PostureTask(self.model)
self.posture_task.set_target(self.q_ref)
self.posture_task.cost = 0.1
\end{lstlisting}
% 描述各个 FrameTask 的权重设计思路
\subsubsection{躯干朝向约束}
IK求解时若不对躯干朝向加以约束,机器人上半身容易出现后仰或侧倾。为此单独设置一个chest的FrameTask,只追旋转不追位置(位置权重为0,旋转权重为50),每帧从SMPL的spine1、left\_collar、right\_collar三点构造正交基作为目标旋转矩阵(见2.4.1节),强制机器人胸部朝向与SMPL躯干保持一致。
由于位置不强制追踪,chest的世界坐标始终取机器人当前真实位置,避免与其他任务产生冲突:
\begin{lstlisting}[style=bashstyle]
chest_pos = self.configuration.get_transform_frame_to_world("chest").translation.copy()
self.chest_task.set_target(pin.SE3(chest_rot, chest_pos))
\end{lstlisting}
% 描述用 spine3/left_collar/right_collar 三点确定胸部朝向的方法
\subsubsection{头部朝向处理}
头部朝向通过两种方式处理。若上游提供了\lstinline{head_rot_mats}(局部旋转矩阵),则将其与当前帧的躯干旋转矩阵相乘得到头部世界朝向;若未提供则只追位置,旋转使用单位矩阵:
\begin{lstlisting}[style=bashstyle]
if smpl_name == "head" and head_rot_mats is not None:
# 世界朝向 = 躯干朝向 x 头部局部旋转
task.set_target(pin.SE3(chest_rot @ head_rot_mats[i], limited_targets[smpl_name]))
else:
task.set_target(pin.SE3(np.eye(3), limited_targets[smpl_name]))
\end{lstlisting}
由于\lstinline{head_rot_mats}是相对于父关节的局部旋转,对于包含大幅根节点移动的动作(如走圈),预处理阶段会自动检测并清除旋转向量的Yaw分量,避免头部跟随身体转向产生突变。
% 描述头部朝向的处理方法
\subsection{数据处理与平滑}
\subsubsection{插值扩帧}
IK求解在20fps上运行,完成后对关节角度序列做三次样条插值升采样至90fps。在关节空间插值比在笛卡尔空间插值更物理合理,且计算开销小:
\begin{lstlisting}[style=bashstyle]
interp = interp1d(t_orig, sim_dof, axis=0, kind='cubic')
sim_dof = interp(t_new)
\end{lstlisting}
% 描述从20fps插值到90fps的方法
\subsubsection{加权滑动滤波}
对IK输出的关节角度序列做线性加权滑动均值滤波(窗口7帧),越近的帧权重越高,有效抑制IK求解引入的高频抖动。该滤波方法在机器人动作重定向领域被广泛采用:
\begin{lstlisting}[style=bashstyle]
w = np.arange(1, window_size + 1, dtype=float)
w /= w.sum() # 线性权重归一化,越新的帧权重越大
\end{lstlisting}
% 描述 WeightedMovingFilter
\subsubsection{关节限位约束}
在IK求解时加入ConfigurationLimit约束,将28个关节的角度限制在物理允许范围内,防止求解结果超出机器人实际关节行程:
\begin{lstlisting}[style=bashstyle]
# 将限位写入Pinocchio模型
self.model.lowerPositionLimit[q_idx] = H1Config.JOINT_LIMITS[idx, 0]
self.model.upperPositionLimit[q_idx] = H1Config.JOINT_LIMITS[idx, 1]
self.config_limit = ConfigurationLimit(self.model)
# IK求解时传入约束
velocity = pink.solve_ik(
self.configuration, self.tasks, dt,
solver="quadprog", damping=1e-3,
limits=[self.config_limit],
)
\end{lstlisting}
% 描述 ConfigurationLimit 的使用
\subsubsection{脚踝角度计算}
机器人没有脚掌关节,脚踝pitch角度无法通过IK直接求解。因此从SMPL几何信息中提取每帧小腿方向向量与脚掌方向向量的夹角,以第0帧(站立姿态)为基准计算相对变化量,直接覆写到脚踝pitch电机:
\begin{lstlisting}[style=bashstyle]
# 计算小腿与脚掌方向向量的夹角
shin = _normalize(ankle - knee)
toes = _normalize(foot - ankle)
angle = np.arccos(np.clip((shin * toes).sum(axis=-1), -1.0, 1.0))
# 以第0帧为中性角基准,限制在关节限位内
pitch = angle - angle[0]
pitch = np.clip(pitch, lo, hi)
\end{lstlisting}
% 描述从小腿/脚掌方向向量计算脚踝pitch角的方法
\subsection{其他}
\subsubsection{遥操作与实时重定向的区别}
目前业内已有端到端的实时遥操作方案(如GR-1、AnyTeleop等),直接从摄像头到机器人控制信号,延迟极低。本系统与其定位不同,对比如下:
\begin{table}[h]
\centering
\caption{实时遥操作方案与本系统对比}
\begin{tabular}{p{3cm}p{5.5cm}p{5.5cm}}
\toprule
& \textbf{实时遥操作(如GMR} & \textbf{本系统(GVHMR + IK} \\
\midrule
\textbf{输入来源} & 实时摄像头 & 视频文件或大模型npy输出 \\
\textbf{实时性} & 极低延迟,在线处理 & 离线为主,单帧$\approx$0.4ms \\
\textbf{精度} & 人体捕捉精度较低,但足够还原动作 & GVHMR精度较高 \\
\textbf{机器人泛化性} & 换机器人需重新训练模型 & 只需修改URDF和骨骼参数,无需重训 \\
\textbf{适用场景} & 实时控制、遥操作 & 动作生成、离线批量处理 \\
\textbf{主要优势} & 延迟低、部署简单 & 通用性强,兼容多种输入源 \\
\bottomrule
\end{tabular}
\end{table}
本系统优先接入大模型生成的npy动作数据,以通用IK重定向为核心,机器人结构发生变化时只需更新URDF和骨骼长度参数即可适配,无需重新训练任何模型,维护成本低。
\subsubsection{局部四肢避障方法}
在动作模仿过程中,若摄像头检测到障碍物与机器人四肢存在碰撞风险,需要对目标关节点位进行实时修正。
\textbf{基本思路}:不修改IK框架,而是在骨骼重定向阶段对SMPL关节点位进行几何修正,将避障后的新点位作为IK追踪目标传入。
\textbf{几何避障原理}:以手臂为例,肩膀、肘部、手腕三点构成一个两边长度固定(大臂长、小臂长)的运动链。当大臂或小臂检测到与障碍物碰撞时,在保持骨骼长度不变的约束下,迭代计算绕障后的新关节位置,使整个手臂绕过障碍物的同时保持人体物理结构不变形。腿部同理,由髋、膝、踝三点构成固定长度运动链做同样处理。
\textbf{局限性}:该方法基于纯几何计算,时效性好,适用于简单的局部避障场景。对于需要腰部扭转、重心转移等全身协调配合的复杂避障情况,单纯修正四肢点位无法保证整体姿态合理性,需要引入更完整的全身运动规划方案。
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{arm_avoidance.png}
\caption{局部四肢避障几何方法示意图}
\label{fig:arm_avoidance}
\end{figure}
% 在这里写你对整个重定向方法的理解,遇到的问题和解决思路
\newpage
% ============================================================
% 第三章:问答记录
% ============================================================
\section{常见踩坑}
\begin{qabox}{quat旋转的作用是什么?}
\end{qabox}
\begin{answerbox}
大模型输出的SMPL关节坐标以Y轴朝上为标准坐标系,而本系统机器人重定向以Z轴朝上为基准。若不做坐标系变换,输入的SMPL人体在机器人坐标系下呈躺倒状态,无法正常重定向。
通过四元数 $q=[0.5, 0.5, 0.5, 0.5]$ 对应的旋转矩阵,将Y轴朝上变换为Z轴朝上:
\begin{lstlisting}[style=bashstyle]
rot = sRot.from_quat([0.5, 0.5, 0.5, 0.5]).as_matrix()
smpl_raw_seq = target_motion @ rot.T # [T, J, 3]
\end{lstlisting}
对于GVHMR视频提取的SMPL数据,输出已是Z轴朝上,无需做Y轴到Z轴的变换,只需绕Z轴旋转$-90°$调整朝向,对应quat参数为$[0, 0, -0.707, 0.707]$ (参考服务器端GVHMR目录下的逆解文件)。
\end{answerbox}
\vspace{1em}
% ---- 继续添加 Q&A ----
\subsection{骨骼重定向相关}
\begin{qabox}{若不将躯干与两臂任务解耦会有什么后果?}
\end{qabox}
\begin{answerbox}
若直接以SMPL肩膀点作为两臂IK追踪的anchor,由于机器人与SMPL骨骼比例不同,肩膀点无法完全对齐,导致以肩膀为起点的手肘、手腕等末端追踪点整体偏移,IK始终存在残差。
更严重的问题是,PINK本质上求解的是QP问题,当多个任务存在冲突时,求解器会在各任务间做权重妥协。若躯干与手臂任务耦合,某一侧手臂恰好能追上目标而另一侧追不上时,QP求解器为了整体最优会将误差集中到某个关节,导致该关节的轴向电机出现突变甚至疯转。
通过动态Anchor设计,每帧以机器人自身正运动学的肩膀位置为起点,将躯干运动与手臂末端追踪解耦,从根本上避免了上述问题。
\end{answerbox}
% Q&A
\subsection{IK求解相关}
\begin{qabox}{为什么不先插值到90fps再进行IK求解?}
\end{qabox}
\begin{answerbox}
IK求解的耗时与帧数成正比,若先插值到90fps再求解,计算量是当前方案的4.5倍。而先在20fps上完成IK求解,再对关节角度序列做三次样条插值升采样至90fps,精度损失极小,因为相邻帧间的关节角度变化平滑,插值能够很好地还原中间帧。
因此选择先IK后插值的方案,在几乎不影响动作质量的前提下将IK阶段耗时降低至原来的$\frac{1}{4.5}$,是性价比最高的做法。
\end{answerbox}
\begin{qabox}{为什么不在IK求解过程中加入速度控制?}
\end{qabox}
\begin{answerbox}
主要有三个原因:
第一,职责解耦。重定向模块只负责将人体动作映射为机器人关节角度,速度控制属于运动控制层的职责,两者混合会导致模块边界模糊,出现问题时难以定位。
第二,仿真与现实存在差距。PINK求解的是仿真环境下的机器人状态,加入速度控制后轨迹会产生延迟跟踪效果,而这个延迟在真实机器人上的表现与仿真并不一致,反而引入额外误差。
第三,需求已满足。本系统的目标是离线生成某段动作对应的机器人关节角度序列,并不需要在线实时跟踪,因此静态的角度序列输出已经完全满足需求,无需引入速度控制增加复杂度。
\end{answerbox}
% Q&A
% ============================================================
% 附录
% ============================================================
\newpage
\section{后续规划}
\subsection{目标}
\begin{itemize}
\item \textbf{训练机器人行走:} 利用重定向模块生成的高质量行走动作序列作为参考轨迹,通过强化学习训练机器人在仿真环境中实现稳定行走。
\item \textbf{去掉固定根节点限制:} 当前重定向模块输出的电机角度序列以固定waist为前提,无法直接在真实环境中执行带位移的动作。目标是结合强化学习,训练机器人在真实动力学约束下完成动作模仿,使其能够在现实环境中真正执行出对应动作。
\end{itemize}
\subsection{计划路线}
当前调研的技术路线如下:
\begin{center}
\begin{tcolorbox}[width=0.85\textwidth, colback=blue!5, colframe=blue!40,
boxrule=0.5pt, arc=3pt]
\centering
\textbf{输入} 大模型生成 / 视频提取 \\[4pt]
$\downarrow$ \\[2pt]
转换为SMPL关节坐标序列(.npy) \\[4pt]
$\downarrow$ \\[2pt]
动作重定向模块 $\rightarrow$ 输出电机角度序列(dof \\[4pt]
$\downarrow$ \\[2pt]
接入强化学习模块 \\[2pt]
(结合动力学信息对角度序列进行调整与优化) \\[4pt]
$\downarrow$ \\[2pt]
\textbf{输出} 可在真实环境执行的机器人动作
\end{tcolorbox}
\end{center}
\subsection{参考方法}
\begin{itemize}
\item \textbf{humanoid-gym}:基于PPO的H1行走训练,用于验证仿真环境与机器人配置。
\item \textbf{ExBody2}:基于AMP框架的动作模仿,以重定向模块输出的dof序列作为参考轨迹,对重定向误差容忍度较高。
\item \textbf{OmniH2O}:全身精确动作模仿备选方案,关节角度直接跟踪,还原度更高但对重定向精度要求更高。
\end{itemize}
\newpage
\appendix
\section{关键参数说明}
\begin{table}[h]
\centering
\caption{重定向关键参数}
\begin{tabular}{lll}
\toprule
\textbf{参数名} & \textbf{} & \textbf{说明} \\
\midrule
\texttt{scale} & 0.2466 & SMPL到机器人的缩放比例 \\
\texttt{aim\_fps} & 90 & 目标帧率 \\
\texttt{substeps} & 2 & IK每帧子步数 \\
\texttt{max\_step} & 0.01 & 目标点每步最大移动距离(m) \\
\texttt{damping} & 1e-3 & IK阻尼系数 \\
\bottomrule
\end{tabular}
\end{table}
\end{document}