多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

NMR 神经动作重定向实战:在昇腾引擎上将 SMPL-X 动作迁移到 Unitree G1 人形机器人

NMR 神经动作重定向实战:在昇腾引擎上将 SMPL-X 动作迁移到 Unitree G1 人形机器人 NMR 神经动作重定向实战在昇腾引擎上将 SMPL-X 动作迁移到 Unitree G1 人形机器人【免费下载链接】cann-recipes-embodied-ai本项目针对具身智能业务中的典型模型、加速算法提供基于CANN平台的优化样例项目地址: https://gitcode.com/cann/cann-recipes-embodied-ai本指南围绕仓库 locomotion/NMR 展开系统讲解如何基于 LLaMA Transformer 架构将人类 SMPL-X 动作序列重定向为宇树 Unitree G1 人形机器人的全身控制指令并面向昇腾引擎完成从环境配置、数据准备、模型训练、指标评估到端到端推理的完整闭环。阅读本文后你将掌握RetargetTransformerPredMotionNoSMPLVQ模型的数据流与损失设计、configs/retarget_fwd.py中每个关键配置项的作用以及可直接复制的训练、评估与推理命令。许可证与使用边界locomotion/NMR目录包含非商业和受限许可的第三方代码、依赖、模型与资产因此该目录整体不是 Apache-2.0-only 项目。除非文件头另有说明本项目自研代码与 NMR 修改部分按 Apache-2.0 发布文件级版权声明、SPDX 标识、第三方许可证以及 THIRD_PARTY_LICENSES.md 中列出的条款优先于目录级默认许可证详见 LICENSE。特别地smplx以及运行所需的 SMPL-X 模型文件受 SMPL-X / SMPLify-X 官方许可约束通常仅限非商业科研用途smplx不作为默认依赖自动安装本仓库不分发SMPLX_NEUTRAL.npz相关模型、数据与软件不得被默认理解为可自由再分发任何商业使用都需要使用者自行向权利方另行取得授权。使用者需要自行确认并遵守相关第三方代码、模型、数据和资产的许可条件非商业或受限组件不得被理解为由本仓库授予商业使用或自由再分发权利。方法架构基于 LLaMA Transformer 的 SMPL-X → G1 重定向数据流总览NMR 采用条件式conditionalTransformer 而非自回归 token 生成范式其核心模型为RetargetTransformerPredMotionNoSMPLVQ注册于 src/models/transformers/retarget_model.py整体数据流为条件输入SMPL-X 运动序列经VQVAE的 encoder仅使用运动编码器不生成 token降维编码再过一层motion_encoderMLP得到条件嵌入(B, T/4, 512)Transformer 主干条件嵌入直接送入 LLaMA 风格的LlamaHfFwdTransformer不预测 token直接输出 feature上采样与投影输出特征经 2 次 2× 最近邻上采样恢复到原始时序分辨率再由线性投影层映射为 217 维 G1 运动向量损失Smooth L1分段 Huber损失仅在有有效运动帧的位置计算。该设计将重定向问题建模为以 SMPL-X 运动为条件、回归 G1 运动向量的序列到序列任务避免了对运动离散 token 的依赖。源码级模型拆解在 retarget_model.py 中模型由以下子模块构成子模块类型说明transformerLlamaHfFwd由MODELS.build(transformer_cfg)构建的 LLaMA 主干smplx_vqvaeVQVAE仅 encoderSMPL-X 运动编码器EncoderAttn时序下采样 2 倍motion_encoderLinear(512, n_embd) → SiLU → Linear(n_embd, n_embd)将编码器输出对齐到 Transformer 隐层维度upsampleUpsample ×2 Conv1d×2将T/4恢复为TprojectorLinear(n_embd, 217)输出 217 维 G1 运动特征其中projector的输出维度 217 是针对宇树 G1 机器人运动特征维度的硬编码源码注释明确说明见 retarget_model.py如需适配其他机器人须修改此处。VQVAE的封装在 src/models/tokenizers/vqvae.py它只保留encoderpreprocess负责把(B, T, Jx3)转置为(B, Jx3, T)forward被显式禁用raise NotImplementedError。配置中对应的编码器超参见 configs/retarget_fwd.pysmplx_vqvae_cfg dict( encoder_cfgdict( activationrelu, depth3, # 编码器深度 dilation_growth_rate3, # 空洞卷积膨胀率增长 down_t2, # 时序下采样次数 input_emb_width140, # 输入特征宽度22 关节 SMPL-X 运动特征 normNone, output_emb_width512, # 编码输出宽度 stride_t2, # 时序步长 typeEncoderAttn, width512, ), typeVQVAE, )输入宽度 140 与推理脚本中构造的 SMPL-X 运动特征维度一致2 维根 XZ 速度 6 维 6D 旋转 22×3 关节位置 22×3 关节速度 140见 tools/inference.py。Transformer 主干LlamaHfFwd 与 LengthCausalSelfAttentionLlamaHfFwdsrc/models/transformers/llama_fwd.py继承自LlamaHfArsrc/models/transformers/llama_ar.py基于 nanoGPT 实现关键差异是在初始化时删除词嵌入表wte并将sample方法显式置为不可用——因为本模型不做自回归 token 采样而是直接消费条件特征、回归连续运动向量。其前向流程为直接以条件嵌入(B, T, C)作为输入无需 token 查表逐层经过BlockRMSNorm →LengthCausalSelfAttention→ 残差 → RMSNorm → MLP → 残差末端ln_f归一化后经lm_head输出(B, T, vocab_size)此处vocab_size被配置为n_embd不预测 token直接出 feature见 configs/retarget_fwd.py。注意力层LengthCausalSelfAttentionllama_ar.py实现了三类关键机制RoPE 旋转位置编码build_rope_cache/apply_rope使用复数形式的旋转位置编码base10000见 llama_ar.py并缓存rope_cache避免重复计算长度因果掩码attn_mask torch.tril(...)保证序列内部因果性条件掩码扩展text_mask y_mask.unsqueeze(2) * y_mask.unsqueeze(1)使填充位置互相不可见且只允许条件特征位置参与注意力避免 padding 污染KV 缓存支持传入past_key_values与position_ids为未来推理/采样预留了接口。MLP 采用 SwiGLU 变体F.silu(c_fc1(x)) * c_fc2(x)隐藏维按 256 对齐归一化使用 RMSNorm详见 llama_ar.py。损失函数与掩码forward_lossretarget_model.py的实现细节条件嵌入经 Transformer 得到特征后upsample将其 4 倍恢复到时序分辨率对 mask 同步做 2× 最近邻插值得到new_mask计算F.smooth_l1_loss(pred_motion, gt_motion, reductionnone)即 Smooth L1 / Huber 损失按new_mask[..., None]屏蔽填充帧后求均值。这保证只有真实运动帧参与梯度计算。训练模式的输入组织见forwardretarget_model.pysmplx_motion先经preprocess → encoder → motion_encoder得到(B, T/4, C)条件嵌入masks由motion_length // 2构造。依赖与数据准备1. GMR 子模块GMRGeneral Motion Retargeting作为工具依赖需在项目tools目录下克隆# 在项目根目录执行 cd tools git clone https://github.com/YanjieZe/GMR.git执行完成后目录结构应为NMR/ └── tools/ └── GMR/ ├── README.md ├── assets/ ├── gmr/ └── ...下载完成后可用以下命令快速校验来源正确性ls tools/GMR git -C tools/GMR remote -v若第二条命令能看到https://github.com/YanjieZe/GMR.git说明来源正确。2. 统计与辅助数据文件以下文件是项目方为复现实验提前训练的辅助文件或统计文件并非第三方源码、第三方模型文件或 SMPL-X 官方模型参数。若使用者基于其他数据重新训练可自行重新生成对应文件。按如下方式放置文件目标路径betas.npycheckpoints/humanoid_model/g1/betas.npygmr_mean.npydata/gmr_data/gmr_mean.npygmr_std.npydata/gmr_data/gmr_std.npysmplx_mean.npydata/smplx_data/smplx_mean.npysmplx_std.npydata/smplx_data/smplx_std.npy这些路径与 configs/retarget_fwd.py 中的SMPLX_MEAN_PATH、SMPLX_STD_PATH、G1_MEAN_PATH、G1_STD_PATH一一对应供训练时对 SMPL-X / G1 运动特征做标准化。3. 三维网格模型与机器人资产human_model/humanoid_model资产准备可参考 PHUMA 仓库的asset目录作为组织方式参考。注意请勿将 PHUMA 的asset/human_model理解为 SMPL-X 模型文件的再分发来源其中涉及 SMPL-X 的模型文件应由用户自行从 SMPL-X 官方渠道获取。humanoid_model相关 G1 文件按 PHUMA 仓库说明自行获取或生成并放置到本项目的checkpoints/humanoid_model/目录下。SMPLX_NEUTRAL.npz需用户自行从 SMPL-X 官方下载页获取并遵守其许可条款。登录后选择SMPL-X 2020The neutral SMPL-X model with the FLAME 2020 expression blendshapes对应的下载项若下载得到的文件名为SMPLX_NEUTRAL_2020.npz可将其重命名为SMPLX_NEUTRAL.npz。g1_custom_collision_with_fixed_hand.urdf请从 YanjieZe/TWIST 仓库的对应文件获取。文件目标路径SMPLX_NEUTRAL.npzcheckpoints/human_model/SMPLX_NEUTRAL.npzg1_custom_collision_with_fixed_hand.urdfsrc/metrics/assets/g1/g1_custom_collision_with_fixed_hand.urdf放置完成后checkpoints/目录结构应与仓库约定一致NMR/ └── checkpoints/ ├── human_model/ └── humanoid_model/SMPL-X / SMPLify-X 相关软件、模型和数据通常按非商业科研用途授权。本仓库不分发SMPLX_NEUTRAL.npz也不授予任何超出 SMPL-X 官方许可的权利商业用途需要用户自行联系权利方取得单独授权。昇腾运行环境配置本项目不限定具体云平台支持在可用昇腾训练环境中运行含云上与本地集群。支持的产品型号Atlas A3 系列产品。必要版本约束项版本框架PyTorch 2.7.1CANN8.3.rc1Python3.11系统架构aarch64推荐环境标识示例Ascend-Powered-Engine/pytorch_2.7.1-cann_8.3.rc1-py_3.11-euler_2.10.11-aarch64-snt9b。Python 基础依赖可通过以下命令安装pip install -e .依赖清单见 setup.py核心包括torch2.0、mmengine0.10.0、numpy1.23,2.0、scipy、joblib、pinpinocchio、imageio[ffmpeg]、opencv-python、matplotlib、tqdm。由于smplx受 SMPL-X / SMPLify-X 官方许可约束通常仅限非商业科研用途不作为默认依赖自动安装。如需运行涉及 SMPL-X 模型加载或处理的功能请先确认已阅读并同意其官方许可条款再手动安装pip install smplx部署步骤准备运行目录并上传项目代码目录名建议保持为NMR。按上文预训练模型文件章节放置必要权重与模型文件并将 configs/retarget_fwd.py 中的TRAIN_SPLIT_FILE和TEST_SPLIT_FILE路径修改为训练集和测试集对应的数据路径。配置训练入口为tools/dist_train.py并设置输出环境变量train_out_path指向预计输出目录该变量会被 tools/dist_train.sh 读取并作为工作目录。在 configs/retarget_fwd.py 中进行对应修改optim_wrapper段中的lr为学习率train_cfg中的max_epochs控制训练周期。提交训练作业并在平台日志中观察启动与收敛状态。后训练迁移训练代码修改将 configs/retarget_fwd.py 中TRAIN_SPLIT_FILE和TEST_SPLIT_FILE后的文件路径改为训练和测试使用的数据。将 configs/retarget_fwd.py 中LOAD_FROM修改为当前训练产出的 checkpoint 路径若不进行后训练可保持为None。在 configs/retarget_fwd.py 进行对应修改第 144 行optim_wrapper.optimizer.lr为学习率第 152 行train_cfg的max_epochs控制训练周期README 中记载的 154-175 行区间对应当前文件中的优化器/调度器/训练循环/钩子配置区域行号以当前仓库 configs/retarget_fwd.py 为准。配置文件详解configs/retarget_fwd.py主训练配置 configs/retarget_fwd.py 继承自 configs/default_runtime.py基础运行时设置含RANDOM_SEED3407固定随机种子、env_cfg中mp_start_methodspawn与dist_cfg.backendnccl、TensorBoard 可视化等。模型规模与 Transformer 超参MODEL_SIZE 70M llama_configs { 70M: dict(n_layer8, n_head8, n_embd512), 111M: dict(n_layer12, n_head12, n_embd768), 343M: dict(n_layer24, n_head16, n_embd1024), 775M: dict(n_layer36, n_head20, n_embd1280), }配置通过MODEL_SIZE一键切换 4 档模型容量70M / 111M / 343M / 775M对应n_layer层数、n_head注意力头数、n_embd隐层维度。Transformer 配置中block_size1024最大序列长度vocab_sizen_embd不预测 token、直接输出 feature见 configs/retarget_fwd.py。CODE_DIM512与NB_CODE1920为运动 tokenizer 相关常量本实现仅使用其编码器部分。数据集与数据加载器三个数据加载器train / val / test均使用RetargetDataset与retarget_collate_fn实现在 src/datasets/retarget_dataset.py配置项trainval / test含义batch_size2432每卡批大小num_workers84数据加载进程数persistent_workersTrueTrue复用 workersamplerDefaultSampler(shuffleTrue)DefaultSampler(shuffleFalse)采样器collate_fnretarget_collate_fn同左变长序列 padding 并返回均值/方差unit_length44采样长度按 4 对齐window_size-1-1大于 0 时固定采样长度-1 表示自适应min_motion_length6060最短样本长度帧小于此值被过滤max_motion_length300300最长采样长度帧均值/方差路径4 个 npy同左SMPL-X 与 G1 特征的标准化统计量RetargetDataset的数据流见 retarget_dataset.pyjoblib.load(split_file)读取样本{smplx_motion, g1_motion, source_path}字典或三元组形式按min_motion_length过滤_filter_by_min_length过滤后为空会抛出ValueError加载 4 个 npy 统计文件对g1_motion与smplx_motion做(x - mean) / std标准化确定采样长度window_size 0时固定为window_size否则为(time_steps // unit_length) * unit_length并受max_motion_length截断随机裁剪等长片段返回dict供retarget_collate_fn打包。优化器、学习率调度与训练循环optim_wrapper dict( typeAmpOptimWrapper, dtypefloat32, optimizerdict(typeAdamW, lr2e-5, betas(0.9, 0.99), weight_decay0.0), ) param_scheduler [ dict(typeLinearLR, by_epochFalse, start_factor1e-5, begin0, end200), dict(typeCosineAnnealingLR, by_epochTrue, T_max50, eta_min1e-5), ] train_cfg dict(typeEpochBasedTrainLoop, max_epochs50, val_interval5) default_hooks dict( checkpointdict(typeCheckpointHook, by_epochTrue, max_keep_ckpts2, interval50), loggerdict(typeLoggerHook, interval5), )要点说明AMPAmpOptimWrapper启用自动混合精度训练训练入口--amp参数也可动态开启见 tools/train.py学习率调度先线性 warmup200 步从 1e-5 起步再余弦退火50 个 epoch最低 1e-5训练周期max_epochs50每 5 个 epoch 验证一次val_interval5checkpoint按 epoch 保存最多保留 2 个max_keep_ckpts2。说明README 记载的默认训练参数Batch size 64、Epochs 1000、lr2e-4、线性 warmup 500 步 → MultiStep 衰减可视为历史/建议值当前仓库 configs/retarget_fwd.py 的实际默认值为上表所示batch 24/32、epochs 50、lr 2e-5、warmup 200 → CosineAnnealing正式实验请以仓库配置为准并按需调整。训练昇腾引擎分布式训练项目默认提供分布式训练脚本 tools/dist_train.sh可直接在昇腾引擎上运行。脚本的关键逻辑源码可验证自动定位 conda 环境PyTorch-2.7.1的 Python 解释器自动安装依赖并写入tools/pip-constraints-train.txtnumpy1.23,2.0、scipy1.15.3约束自动识别可见卡数优先从ASCEND_RT_VISIBLE_DEVICES读取其次ASCEND_VISIBLE_DEVICES通过逗号数量推导GPUSdist_train.sh外部启动器感知若外部分布式启动器已注入RANK、LOCAL_RANK、WORLD_SIZE则复用外部环境直接调用tools/train.py --launcher pytorch否则自动使用torch.distributed.run拉起多卡训练dist_train.sh工作目录默认work_dirs/retarget_pred_token_l_4_seq可用环境变量train_out_path覆盖。tools/dist_train.pytools/dist_train.py仅是调用dist_train.sh的包装入口subprocess.run([sh_path, ...])。如果需要指定单机多卡可先设置可见设备export ASCEND_RT_VISIBLE_DEVICES0,1,2,3,4,5,6多卡启动时还可通过MASTER_ADDR、PORT、ASCEND_RT_VISIBLE_DEVICES调整通信地址与可见设备NNODES、NODE_RANK用于多机扩展。训练 Transformer# 昇腾引擎分布式训练默认自动识别可见卡 bash tools/dist_train.sh # 单卡训练调试 PYTHONPATH. python tools/train.py configs/retarget_fwd.py \ --work-dir work_dirs/debug # 恢复训练--resume auto 自动从 work_dir 中最新的 checkpoint 恢复 PYTHONPATH. python tools/train.py configs/retarget_fwd.py \ --work-dir work_dirs/xxx --resume auto # 覆盖配置参数例如把训练周期改为 500 epoch PYTHONPATH. python tools/train.py configs/retarget_fwd.py \ --cfg-options train_cfg.max_epochs500单卡训练入口 tools/train.py 基于 mmengineRunner构建--work-dir输出目录优先级为 CLI 配置文件 ./work_dirs/config名--resume auto自动从工作目录最新 checkpoint 恢复设置cfg.resumeTrue, cfg.load_fromNone--resume path指定 checkpoint 路径--cfg-options以keyvalue形式覆盖任意配置列表值如key[a,b]脚本内置了 PyTorch 2.6weights_only兼容补丁tools/train.py确保 mmengine checkpoint 可正常加载。数据长度约束重要根据 src/datasets/retarget_dataset.py 的实现RetargetDataset在window_size 0时会固定采样长度为window_size若样本时间长度T window_size会在数据加载阶段直接抛出ValueError不是自动补齐建议保证数据集中每条样本满足T window_size或将window_size设为不大于最短样本长度当前实现会在数据集初始化阶段按min_motion_length过滤样本仅保留T min_motion_length的序列_filter_by_min_length若过滤后无有效样本会抛出ValueError提示检查min_motion_length或数据源。评估单卡测试/评估命令PYTHONPATH. python tools/nmr_inference.py config_path checkpoint_path评估入口 tools/nmr_inference.py 基于 mmengineRunner.test()额外支持--work-dir、--out导出预测结果 pickle、--cfg-options等参数。评估器由 configs/retarget_fwd.py 指定为HumanoidReconsMetric实现在 src/metrics/humanoid_metric.py 起内部借助 pinocchio 正向运动学将 G1 DOF 还原为关节轨迹再与真值比对。评估指标指标说明MPJPE平均关节位置误差mmPA-MPJPEProcrustes 对齐后的 MPJPEACCEL平均关节加速度误差DOF_ERRDOF 角度误差从源码结构看humanoid_metric.py加速度类指标基于二阶差分np.diff(q, n2) * fps**2并定义了 29 关节的joint_mappingSMPL 关节序号 → G1 关节序号用于真值 SMPL 与 G1 预测之间的关节对应。端到端推理在昇腾引擎上将 SMPL-X 动作文件转换为 G1 机器人控制指令使用 tools/inference.py# 单文件推理 PYTHONPATH. python tools/inference.py config checkpoint \ --src input.npz --output-dir output/ # 批量推理目录下所有 npz/pkl 文件递归遍历 PYTHONPATH. python tools/inference.py config checkpoint \ --src input_dir/ --output-dir output/ # 禁用低通滤波 PYTHONPATH. python tools/inference.py config checkpoint \ --src input.npz --no-filter命令行参数源码定义见 inference.py参数默认值说明config-mmengine 配置文件路径checkpoint-模型权重路径--src必填输入文件NPZ/PKL或目录--output-dirinference_output输出目录--no-filter-禁用低通滤波--smplx-model-pathcheckpoints/human_model/SMPLX_NEUTRAL.npzSMPL-X 模型文件--betas-pathcheckpoints/humanoid_model/g1/betas.npyG1 betas 文件输入格式NPZ含global_orient(T,3)、body_pose(T,63)、transl(T,3)PKL含fullpose(T,72)、trans(T,3)SMPL 格式取前 21 个 body 关节丢弃手部关节自动从 120 FPS 降采样到 30 FPS每 4 帧取 1并完成 Z-up → Y-up 坐标系转换绕 X 轴旋转 -90°详见 inference.py。输出格式PKL字段形状含义dof(T, 29)关节角度DOFroot_trans(T, 3)根节点位置root_rot_quat(T, 4)根节点旋转wxyz 四元数推理流水线源码级预处理preprocess_smplinference.py调用smplx模型前向得到 22 个关键关节位置结合 root XZ 速度、6D 旋转、关节位置/速度构造 140 维运动特征朝向规范化_infer_chunkinference.py提取首帧 yaw将运动旋转到规范朝向yaw0再送入模型输出后再旋转回原始朝向DOF 不受旋转影响标准化与推理(x - smplx_mean) / smplx_std后送入模型modepredict输出反标准化pred * g1_std g1_mean后处理postprocess_g1inference.py根位移由累积根速度重建并做 Y-up 坐标还原旋转由 6D 表示转四元数DOF 取最后 29 维默认启用 4 阶 Butterworth 低通滤波截止频率 5 Hz按 30 FPS 归一化四元数滤波后重新归一化。长序列分段推理长序列自动按4 秒分段推理并线性混合拼接源码常量见 inference.py每段 120 帧CHUNK_FRAMES 30 FPS × 4 s对齐到 4 的倍数相邻段重叠 32 帧约 1 秒OVERLAP_FRAMES滑动步长 88 帧重叠区域按线性权重torch.linspace(0, 1, overlap)融合保证拼接处平滑_blend_overlapping_chunksinference.py序列长度按 4 取整(total_frames // 4) * 4过短序列 4 帧直接跳过。推理脚本会逐文件打印预处理/推理/后处理耗时并在--output-dir下按输入相对路径生成同名.pkl。项目结构索引NMR/ ├── configs/ # 训练配置 │ ├── default_runtime.py # 基础运行时设置 │ └── retarget_fwd.py # 主训练配置Transformer ├── src/ # 核心代码 │ ├── models/ │ │ ├── tokenizers/ # 运动 Tokenizer仅使用其中的运动编码器 │ │ └── transformers/ # LLaMA Transformer 重定向模型 │ ├── datasets/ # 数据加载RetargetDataset 等 │ ├── metrics/ # 评估指标HumanoidReconsMetric 等 │ └── utils/ # 旋转转换等工具 ├── tools/ # 脚本 │ ├── train.py # 训练入口 │ ├── nmr_inference.py # 测试/评估入口 │ ├── dist_train.sh # 分布式训练启动 │ ├── dist_train.py # 分布式启动器用于启用 .sh 文件 │ ├── inference.py # 端到端推理 │ └── GMR/ # General Motion Retargeting 子模块 └── work_dirs/ # 训练输出权重、日志关键文件导读模型定义src/models/transformers/retarget_model.py主模型、src/models/transformers/llama_ar.pyLLaMA 主干、src/models/transformers/llama_fwd.py无 token 前向、src/models/tokenizers/vqvae.py运动编码器数据管线src/datasets/retarget_dataset.py训练/评估/推理入口tools/train.py、tools/dist_train.sh、tools/dist_train.py、tools/nmr_inference.py、tools/inference.py评估实现src/metrics/humanoid_metric.py环境与依赖setup.py、configs/default_runtime.py。Citation若在研究中引用本工作可使用以下文献条目article{zhao2026maketrackingeasy, title {Make Tracking Easy: Neural Motion Retargeting for Humanoid Whole-body Control}, author {Zhao, Qingrui and Yang, Kaiyue and Wang, Xiyu and Zhao, Shiqi and Lu, Yi and Zhang, Xinfang and Yin, Wei and Shen, Qiu and Long, Xiao-Xiao and Cao, Xun}, journal {arXiv preprint arXiv:2603.22201}, year {2026}, eprint {2603.22201}, archivePrefix {arXiv}, url {https://arxiv.org/abs/2603.22201} }【免费下载链接】cann-recipes-embodied-ai本项目针对具身智能业务中的典型模型、加速算法提供基于CANN平台的优化样例项目地址: https://gitcode.com/cann/cann-recipes-embodied-ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表