开源代码结构框架

Bi-DexHands/
├── assets/                 # [关键] 存放机器人和物体的模型文件 (URDF, MJCF, Meshes)
├── bidexhands/
│   ├── algorithms/         # 存放RL/MARL算法的实现 (如 ma_ppo, happo 等)
│   ├── tasks/              # [核心] 环境定义的源代码
│   │   ├── shadow_hand_over.py  # 具体任务:例如双手交接
│   │   ├── shadow_hand_catch.py # 具体任务:例如接球
│   │   ├── base/           # 基础类,定义了通用的仿真接口
│   │   └── ...
│   └── utils/              # 工具脚本 (解析配置、处理数据等)
├── cfg/                    # [关键] 配置文件目录
│   ├── config.yaml         # 总入口配置
│   ├── task/               # 环境的具体参数 (重力、物理参数、奖励权重)
│   │   ├── ShadowHandOver.yaml
│   │   └── ...
│   └── train/              # 训练算法的超参数 (学习率、Batch size等)
│       ├── ShadowHandOverPPO.yaml
│       └── ...
├── train.py                # 训练入口脚本
└── ...

任务注册

代码路径配置

1.复制task下的任务文件shalow_hand_over.py,改成自己的名称l21_hand_over.py

然后把类改成自己的名字

class L21HandOver(BaseTask)

2.复制cfg文件夹下的.yaml文件,改成自己的yaml文件

# 1. 这里添加任务名称,必须与 --task 参数一致
name: L21HandOver

env: 
  # 2. 修改环境名称,用于日志记录
  env_name: "l21_hand_over"

3.去任务表里面添加自己的任务名

在Util文件夹下的config.py中添加"L21HandOver"任务(找到自己任务应该对应的位置)

def retrieve_cfg(args, use_rlg_config=False):

    if args.task in ["ShadowHandOver", "L21HandOver","ShadowHandCatchUnderarm", "ShadowHandTwoCatchUnderarm", "ShadowHandCatchAbreast", "ShadowHandReOrientation",
                     "ShadowHandCatchOver2Underarm", "ShadowHandBottleCap", "ShadowHandDoorCloseInward", "ShadowHandDoorCloseOutward",
                    "ShadowHandDoorOpenInward", "ShadowHandDoorOpenOutward", "ShadowHandKettle", "ShadowHandPen", "ShadowHandSwitch",
                    "ShadowHandPushBlock", "ShadowHandSwingCup", "ShadowHandGraspAndPlace", "ShadowHandScissors", "AllegroHandOver", "AllegroHandCatchUnderarm"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/config.yaml".format(args.algo), "cfg/{}.yaml".format(args.task)

    elif args.task in ["ShadowHandLiftUnderarm"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/lift_config.yaml".format(args.algo), "cfg/{}.yaml".format(args.task)

    elif args.task in ["ShadowHandBlockStack"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/stack_block_config.yaml".format(args.algo), "cfg/{}.yaml".format(args.task)

    elif args.task in ["ShadowHand", "ShadowHandReOrientation"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/config.yaml".format(args.algo), "cfg/{}.yaml".format(args.task)

    elif args.task in ["ShadowHandMetaMT4", "ShadowHandMetaMT1"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/config.yaml".format(args.algo), "cfg/meta_env_cfg/{}.yaml".format(args.task)

    elif args.task in ["ShadowHandMetaMT1"]:
        return os.path.join(args.logdir, "{}/{}/{}".format(args.task, args.algo, args.algo)), "cfg/{}/config.yaml".format(args.algo), "cfg/meta_env_cfg/{}.yaml".format(args.task)

    else:
        warn_task_name()

4.在Util文件夹下的config.py中添加

# 1. 在文件顶部的 import 区域,添加这一行:
from bidexhands.tasks.l21_hand_over import L21HandOver 

5.运行训练代码

python train.py --task=L21HandOver --algo=ppo

如果代码可以正常跑通,代表任务注册成功

添加自己的模型

在asset文件夹下,添加自己的模型文件

将URDF文件导入进去,找到 _create_envs 函数。我们需要把 ShadowHand 的 MJCF 文件替换成你的 URDF 文件。

asset_root = "../../assets"
        # 这里填入你的URDF相对路径
        shadow_hand_asset_file = "urdf/linkhand/L21right/linkerhand_l21_right.urdf"
        # 暂时两个手都用同一个模型(之后你可以做一个左手的URDF或者在代码里旋转这个手)
        shadow_hand_another_asset_file = "urdf/linkhand/L21right/linkerhand_l21_right.urdf"

L21灵巧手16个自由度,修改奖励函数的指尖名称:

# 根据URDF文件结构提取的末端 Link 名称
        # 注意:除大拇指外,其他手指在 URDF 中只定义到了 index_middle 这一级
        self.fingertips = [
            "index_middle",   # 食指末端
            "middle_middle",  # 中指末端
            "ring_middle",    # 无名指末端
            "pinky_middle",   # 小指末端
            "thumb_distal"    # 大拇指末端
        ]
 # 如果第二只手用的是同一个 URDF,名字是一样的
        self.a_fingertips = self.fingertips

修改手掌中心:

# 用于计算手和物体的整体距离
        self.hand_center = ["hand_base_link"]

手的模型显示如下:

URDF 中定义的 type="revolute"(可旋转)关节如下:

    Index (食指): index_mcp_roll, index_mcp_pitch, index_pip (3个)

    Middle (中指): middle_mcp_roll, middle_mcp_pitch, middle_pip (3个)

    Ring (无名指): ring_mcp_roll, ring_mcp_pitch, ring_pip (3个)

    Pinky (小指): pinky_mcp_roll, pinky_mcp_pitch, pinky_pip (3个)

    Thumb (大拇指): thumb_cmc_roll, thumb_cmc_yaw, thumb_cmc_pitch, thumb_mcp, thumb_ip (5个)

这里的URDF文件中写着有17个可旋转关节,我查看了具体的参数,也是16个,代表着16个自由度,有一个关节不可动。

修改yaml文件

修改路径

 asset:
    assetRoot: "../assets"
    # [修改] 保持与 Python 代码一致,指向你的 L21 URDF
    # 虽然 Python 代码会覆盖这个值,但改了更清晰
    assetFileName: "urdf/linkhand/L21right/linkerhand_l21_right.urdf"

还有修改关节限位等等,之后再根据任务进行调整

运行代码

python train.py --task=L21HandOver --algo=ppo

看到手已经可以成功导入到里面了,说明移植基本成功了

接下来就是修改环境和修改奖励函数了

修改isaccgym环境

调整手的位置和角度

找到 _create_envs 函数中定义 shadow_hand_start_pose 的部分

# 设置初始位姿
shadow_hand_start_pose = gymapi.Transform()
shadow_hand_start_pose.p = gymapi.Vec3(*get_axis_params(0.5, self.up_axis_idx))

shadow_another_hand_start_pose = gymapi.Transform()
shadow_another_hand_start_pose.p = gymapi.Vec3(0, -1, 0.5)
shadow_another_hand_start_pose.r = gymapi.Quat().from_euler_zyx(0, 0, 3.1415)

修改为

# 设置手和物体的初始位置和姿态
shadow_hand_start_pose = gymapi.Transform()
# [修改位置] x, y, z (单位: 米)
# 例如:放在原点上方 0.5 米处
shadow_hand_start_pose.p = gymapi.Vec3(0.0, 0.0, 0.5) 

# [修改角度] 欧拉角 (弧度制)。 
# 0 表示不转。1.57 ≈ 90度,3.14 ≈ 180度。
# 例如:绕 Z 轴旋转 90 度,让手掌换个朝向
shadow_hand_start_pose.r = gymapi.Quat.from_euler_zyx(0.0, 0.0, 1.57)
shadow_another_hand_start_pose = gymapi.Transform()
# [修改位置] 比如拉近一点,设为 -0.6
shadow_another_hand_start_pose.p = gymapi.Vec3(0.0, -0.6, 0.5) 
# [修改角度] 保持面对面 (绕 Z 轴 180 度)
shadow_another_hand_start_pose.r = gymapi.Quat.from_euler_zyx(0.0, 0.0, 3.14159)
修改物体位置

找到下面代码段

object_start_pose = gymapi.Transform()
object_start_pose.p = gymapi.Vec3()
object_start_pose.p.x = shadow_hand_start_pose.p.x
# 关键参数在这里!
pose_dy, pose_dz = -0.39, 0.04 

object_start_pose.p.y = shadow_hand_start_pose.p.y + pose_dy
object_start_pose.p.z = shadow_hand_start_pose.p.z + pose_dz

改成以下

# 修改偏移量
pose_dx = 0.0   # 新增 X 轴偏移
pose_dy = -0.05 # Y 轴偏移 (根据你的手掌中心调整,原代码 -0.39 可能是针对 ShadowHand 的手腕长度)
pose_dz = 0.15  # Z 轴高度 (给它一点高度让它落下)

object_start_pose.p.x = shadow_hand_start_pose.p.x + pose_dx
object_start_pose.p.y = shadow_hand_start_pose.p.y + pose_dy
object_start_pose.p.z = shadow_hand_start_pose.p.z + pose_dz

# [修改物体角度] 如果你想让物体斜着生成
object_start_pose.r = gymapi.Quat.from_euler_zyx(0.0, 0.5, 0.0)
还要记得修改目标位置(后面更新)

然后看着页面调整

看着是不是和官网的历程很像了

奖励函数修改(目前还不太知道要怎么改,学学PPO之后再改)

之后继续更新

开源网址:https://github.com/PKU-MARL/DexterousHands

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐