0. 简介

robot_lab是一个基于IsaacLab的机器人强化学习扩展库。它使您能够在一个独立的环境中进行开发,而无需依赖核心的Isaac Lab代码库。这次我们主要来看rewards.py以及mdp的其他文件,来梳理本身的强化学习数据内容。

1. robot_lab 项目结构与功能解析

根据提供的目录结构和代码片段,这是一个基于 Isaac Lab 强化学习框架的机器人控制项目,主要用于训练不同类型的机器人(四足、轮式、人形等)进行运动控制任务。

1.1 主要文件夹结构

1.1.1 robot_lab

主要源代码目录,包含以下结构:

  • robot_lab/ - 主要Python包

    • init.py - 包初始化文件,注册环境和UI扩展
    • ui_extension_example.py - UI扩展示例
    • assets/ - 资产配置
      • init.py - 定义数据路径等常量
      • unitree.py - Unitree系列机器人配置(A1、Go2、B2等)
      • fftai.py - FFTAI系列机器人配置
    • tasks/ - 任务实现
      • locomotion/velocity/ - 基于速度的运动控制任务
        • mdp/ - MDP(马尔可夫决策过程)组件
          • rewards.py - 奖励函数定义
          • commands.py - 命令生成器
          • events.py - 事件处理
          • observations.py - 观察空间定义
          • curriculums.py - 课程学习组件
        • config/ - 不同机器人配置
          • quadruped/ - 四足机器人配置
          • wheeled/ - 轮式机器人配置
          • humanoid/ - 人形机器人配置
    • third_party/ - 第三方工具和库
      • amp_utils/ - Adversarial Motion Priors工具
      • rsl_rl_amp/ - RSL RL AMP扩展
  • config/ - 扩展配置

    • extension.toml - 扩展元数据配置
  • data/ - 机器人数据

    • Robots/ - 机器人模型
      • Unitree/ - Unitree公司机器人
        • A1/, B2/, Go2/, B2W/, Go2W/ - 不同型号
      • FFTAI/ - FFTAI公司机器人

1.1.2 scripts

训练和工具脚本:

  • rsl_rl/ - RSL强化学习框架脚本

    • cli_args.py - 命令行参数处理
    • rsl_rl_utils.py - 工具函数
    • amp/ - AMP相关脚本
      • play.py - 播放训练模型
      • replay_amp_data.py - 重放AMP数据
      • train.py - 训练AMP模型
    • base/ - 基础RL训练脚本
      • play.py - 播放训练模型
      • train.py - 训练基础模型
  • tools/ - 工具脚本

    • clean_trash.py - 清理无用日志
    • convert_urdf.py - URDF转USD
    • list_envs.py - 列出可用环境
    • train_batch.py - 批量训练脚本

1.2 核心模块详解

1.2.1 MDP组件 (tasks/locomotion/velocity/mdp/)

这是项目的核心部分,定义了强化学习环境的关键组件:

rewards.py - 奖励函数

提供各种奖励函数,例如:

  • track_lin_vel_xy_exp - 奖励跟踪线性速度命令
  • track_ang_vel_z_exp - 奖励跟踪角速度命令
  • feet_air_time - 奖励足部在空中的时间
  • feet_slide - 惩罚足部滑动
  • joint_pos_penalty - 惩罚关节偏离默认位置
  • flat_orientation_l2 - 奖励保持平坦姿态
  • GaitReward - 步态奖励类

每个奖励函数针对机器人运动的特定方面,例如姿态稳定性、命令跟踪精度、能量效率等。

commands.py - 命令生成
  • UniformThresholdVelocityCommand - 从均匀分布生成带阈值的速度命令
  • DiscreteCommandController - 离散命令控制器

这些组件负责生成机器人控制命令,例如目标速度、方向等。

observations.py - 观察空间

定义观察空间函数:

  • joint_pos_rel_without_wheel - 获取非轮式关节相对位置
  • phase - 计算相位信息

这些函数提供强化学习策略所需的状态信息。

events.py - 事件处理

提供事件处理函数:

  • randomize_rigid_body_inertia - 随机化刚体惯性
  • randomize_com_positions - 随机化质心位置

这些函数用于环境域随机化,增强策略的鲁棒性。

curriculums.py - 课程学习
  • command_levels_vel - 基于表现逐步增加命令复杂度
  • terrain_levels_vel - 基于表现逐步增加地形复杂度

2. Unitree B2W 轮式reward和强化学习梳理

2.1 框架概述

这里我们以Unitree B2W 轮式机器人为基础代码,解释如何实现一个基于 Isaac Lab 和强化学习的 Unitree B2W 轮腿式机器人控制系统。通过分析提供的代码文件,可以看到系统采用了模块化的设计,从环境配置到任务定义,再到训练算法,形成了一个完整的强化学习流程。

2.2 文件结构与关系

提供的代码包含以下几个密切相关的文件:

  1. velocity_env_cfg.py: 基础环境配置文件,定义了通用的运动速度控制环境
  2. rough_env_cfg.py: B2W机器人在粗糙地形上的特定配置
  3. flat_env_cfg.py: B2W机器人在平坦地形上的简化配置
  4. rsl_rl_ppo_cfg.py: PPO强化学习算法的配置
  5. init.py: 环境注册文件,连接环境配置和学习算法

这些文件构成了继承关系树:

LocomotionVelocityRoughEnvCfg (velocity_env_cfg.py)
  └── UnitreeB2WRoughEnvCfg (rough_env_cfg.py)
       └── UnitreeB2WFlatEnvCfg (flat_env_cfg.py)

2.3 核心配置解析

2.3.1 基础环境框架 (velocity_env_cfg.py)

这是整个框架的基础,定义了运动控制环境的核心组件:

@configclass
class LocomotionVelocityRoughEnvCfg(ManagerBasedRLEnvCfg):
    # 场景设置
    scene: MySceneCfg = MySceneCfg(num_envs=4096, env_spacing=2.5)
    # 基本设置
    observations: ObservationsCfg = ObservationsCfg()
    actions: ActionsCfg = ActionsCfg()
    commands: CommandsCfg = CommandsCfg()
    # MDP 设置
    rewards: RewardsCfg = RewardsCfg()
    terminations: TerminationsCfg = TerminationsCfg()
    events: EventCfg = EventCfg()
    curriculum: CurriculumCfg = CurriculumCfg()

核心组件包括:

  • Scene: 定义物理场景,包括地形、机器人和传感器
  • Observations: 定义机器人状态的观测空间
  • Actions: 定义控制动作空间
  • Commands: 定义速度指令生成规则
  • Rewards: 定义奖励函数
  • Terminations: 定义任务终止条件
  • Events: 定义随机化事件
  • Curriculum: 定义课程学习规则

这个基类实现了强化学习中的马尔可夫决策过程(MDP)框架,为各种机器人提供了通用的运动控制环境。这里面是所有的环境和MDP数据

##
# Scene definition
##
@configclass
class MySceneCfg(InteractiveSceneCfg):
    """配置用于具有腿部机器人的地形场景。"""

    # 地面地形
    terrain = TerrainImporterCfg(
        prim_path="/World/ground",  # 地形的原始路径
        terrain_type="generator",  # 地形类型为生成器
        terrain_generator=ROUGH_TERRAINS_CFG,  # 使用的地形生成器配置
        max_init_terrain_level=5,  # 最大初始地形级别
        collision_group=-1,  # 碰撞组
        physics_material=sim_utils.RigidBodyMaterialCfg(  # 物理材料配置
            friction_combine_mode="multiply",  # 摩擦合成模式
            restitution_combine_mode="multiply",  # 反弹合成模式
            static_friction=1.0,  # 静态摩擦系数
            dynamic_friction=1.0,  # 动态摩擦系数
        ),
        visual_material=sim_utils.MdlFileCfg(  # 视觉材料配置
            mdl_path=f"{ISAACLAB_NUCLEUS_DIR}/Materials/TilesMarbleSpiderWhiteBrickBondHoned/TilesMarbleSpiderWhiteBrickBondHoned.mdl",  # 材料模型路径
            project_uvw=True,  # 是否投影UV坐标
            texture_scale=(0.25, 0.25),  # 纹理缩放
        ),
        debug_vis=False,  # 调试可视化开关
    )
    # 机器人
    robot: ArticulationCfg = MISSING  # 机器人配置,缺失时为MISSING
    # 传感器
    height_scanner = RayCasterCfg(  # 高度扫描仪配置
        prim_path="{ENV_REGEX_NS}/Robot/base",  # 原始路径
        offset=RayCasterCfg.OffsetCfg(pos=(0.0, 0.0, 20.0)),  # 偏移配置
        attach_yaw_only=True,  # 仅附加偏航
        pattern_cfg=patterns.GridPatternCfg(resolution=0.1, size=[1.6, 1.0]),  # 网格模式配置
        debug_vis=False,  # 调试可视化开关
        mesh_prim_paths=["/World/ground"],  # 网格原始路径
    )
    height_scanner_base = RayCasterCfg(  # 基础高度扫描仪配置
        prim_path="{ENV_REGEX_NS}/Robot/base",  # 原始路径
        offset=RayCasterCfg.OffsetCfg(pos=(0.0, 0.0, 20.0)),  # 偏移配置
        attach_yaw_only=True,  # 仅附加偏航
        pattern_cfg=patterns.GridPatternCfg(resolution=0.05, size=(0.1, 0.1)),  # 网格模式配置
        debug_vis=False,  # 调试可视化开关
        mesh_prim_paths=["/World/ground"],  # 网格原始路径
    )
    contact_forces = ContactSensorCfg(  # 接触传感器配置
        prim_path="{ENV_REGEX_NS}/Robot/.*",  # 原始路径
        history_length=3,  # 历史长度
        track_air_time=True  # 跟踪空中时间
    )
    # 灯光
    sky_light = AssetBaseCfg(  # 天空光源配置
        prim_path="/World/skyLight",  # 原始路径
        spawn=sim_utils.DomeLightCfg(  # 圆顶光源配置
            intensity=750.0,  # 光源强度
            texture_file=f"{ISAAC_NUCLEUS_DIR}/Materials/Textures/Skies/PolyHaven/kloofendal_43d_clear_puresky_4k.hdr",  # 纹理文件路径
        ),
    )


##
# MDP 设置
##


@configclass
class CommandsCfg:
    """MDP 的命令规范。"""

    base_velocity = mdp.UniformThresholdVelocityCommandCfg(  # 基础速度命令配置
        asset_name="robot",  # 资产名称
        resampling_time_range=(10.0, 10.0),  # 重采样时间范围
        rel_standing_envs=0.02,  # 相对静止环境
        rel_heading_envs=1.0,  # 相对朝向环境
        heading_command=True,  # 启用朝向命令
        heading_control_stiffness=0.5,  # 朝向控制刚度
        debug_vis=True,  # 调试可视化开关
        ranges=mdp.UniformThresholdVelocityCommandCfg.Ranges(  # 速度范围配置
            lin_vel_x=(-1.0, 1.0), lin_vel_y=(-1.0, 1.0), ang_vel_z=(-1.0, 1.0), heading=(-math.pi, math.pi)
        ),
    )


@configclass
class ActionsCfg:
    """MDP 的动作规范。"""

    joint_pos = mdp.JointPositionActionCfg(  # 关节位置动作配置
        asset_name="robot",  # 资产名称
        joint_names=[".*"],  # 关节名称
        scale=0.5,  # 缩放因子
        use_default_offset=True,  # 使用默认偏移
        clip=None,  # 剪辑设置
        preserve_order=True  # 保持顺序
    )


@configclass
class ObservationsCfg:
    """MDP 的观察规范。"""

    @configclass
    class PolicyCfg(ObsGroup):
        """策略组的观察。"""

        # 观察项(保持顺序)
        base_lin_vel = ObsTerm(  # 基础线速度观察项
            func=mdp.base_lin_vel,  # 观察函数
            noise=Unoise(n_min=-0.1, n_max=0.1),  # 噪声配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        base_ang_vel = ObsTerm(  # 基础角速度观察项
            func=mdp.base_ang_vel,  # 观察函数
            noise=Unoise(n_min=-0.2, n_max=0.2),  # 噪声配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        projected_gravity = ObsTerm(  # 投影重力观察项
            func=mdp.projected_gravity,  # 观察函数
            noise=Unoise(n_min=-0.05, n_max=0.05),  # 噪声配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        velocity_commands = ObsTerm(  # 速度命令观察项
            func=mdp.generated_commands,  # 观察函数
            params={"command_name": "base_velocity"},  # 参数配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        joint_pos = ObsTerm(  # 关节位置观察项
            func=mdp.joint_pos_rel,  # 观察函数
            params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=True)},  # 参数配置
            noise=Unoise(n_min=-0.01, n_max=0.01),  # 噪声配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        joint_vel = ObsTerm(  # 关节速度观察项
            func=mdp.joint_vel_rel,  # 观察函数
            params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=True)},  # 参数配置
            noise=Unoise(n_min=-1.5, n_max=1.5),  # 噪声配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        actions = ObsTerm(  # 动作观察项
            func=mdp.last_action,  # 观察函数
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        height_scan = ObsTerm(  # 高度扫描观察项
            func=mdp.height_scan,  # 观察函数
            params={"sensor_cfg": SceneEntityCfg("height_scanner")},  # 参数配置
            noise=Unoise(n_min=-0.1, n_max=0.1),  # 噪声配置
            clip=(-1.0, 1.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )

        def __post_init__(self):
            self.enable_corruption = True  # 启用数据损坏
            self.concatenate_terms = True  # 连接观察项

    @configclass
    class CriticCfg(ObsGroup):
        """评论组的观察。"""

        # 观察项(保持顺序)
        base_lin_vel = ObsTerm(  # 基础线速度观察项
            func=mdp.base_lin_vel,  # 观察函数
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        base_ang_vel = ObsTerm(  # 基础角速度观察项
            func=mdp.base_ang_vel,  # 观察函数
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        projected_gravity = ObsTerm(  # 投影重力观察项
            func=mdp.projected_gravity,  # 观察函数
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        velocity_commands = ObsTerm(  # 速度命令观察项
            func=mdp.generated_commands,  # 观察函数
            params={"command_name": "base_velocity"},  # 参数配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        joint_pos = ObsTerm(  # 关节位置观察项
            func=mdp.joint_pos_rel,  # 观察函数
            params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=True)},  # 参数配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        joint_vel = ObsTerm(  # 关节速度观察项
            func=mdp.joint_vel_rel,  # 观察函数
            params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*", preserve_order=True)},  # 参数配置
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        actions = ObsTerm(  # 动作观察项
            func=mdp.last_action,  # 观察函数
            clip=(-100.0, 100.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )
        height_scan = ObsTerm(  # 高度扫描观察项
            func=mdp.height_scan,  # 观察函数
            params={"sensor_cfg": SceneEntityCfg("height_scanner")},  # 参数配置
            clip=(-1.0, 1.0),  # 剪辑范围
            scale=1.0,  # 缩放因子
        )

        def __post_init__(self):
            self.enable_corruption = False  # 禁用数据损坏
            self.concatenate_terms = True  # 连接观察项

    # 观察组
    policy: PolicyCfg = PolicyCfg()  # 策略观察组
    critic: CriticCfg = CriticCfg()  # 评论观察组
@configclass
class EventCfg:
    """事件的配置。"""

    # 启动时的事件配置
    randomize_rigid_body_material = EventTerm(  # 随机化刚体材料
        func=mdp.randomize_rigid_body_material,  # 调用的函数
        mode="startup",  # 事件模式为启动
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=".*"),  # 资产配置,选择所有身体部件
            "static_friction_range": (0.3, 1.0),  # 静态摩擦范围
            "dynamic_friction_range": (0.3, 0.8),  # 动态摩擦范围
            "restitution_range": (0.0, 0.0),  # 反弹范围
            "num_buckets": 64,  # 桶的数量
        },
    )

    randomize_rigid_body_mass = EventTerm(  # 随机化刚体质量
        func=mdp.randomize_rigid_body_mass,  # 调用的函数
        mode="startup",  # 事件模式为启动
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置,选择所有身体部件
            "mass_distribution_params": (-3.0, 3.0),  # 质量分布参数
            "operation": "add",  # 操作类型为加法
        },
    )

    randomize_rigid_body_inertia = EventTerm(  # 随机化刚体惯性
        func=mdp.randomize_rigid_body_inertia,  # 调用的函数
        mode="startup",  # 事件模式为启动
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=".*"),  # 资产配置,选择所有身体部件
            "inertia_distribution_params": (0.5, 1.5),  # 惯性分布参数
            "operation": "scale",  # 操作类型为缩放
        },
    )

    randomize_com_positions = EventTerm(  # 随机化质心位置
        func=mdp.randomize_com_positions,  # 调用的函数
        mode="startup",  # 事件模式为启动
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置,选择所有身体部件
            "com_distribution_params": (-0.1, 0.1),  # 质心分布参数
            "operation": "add",  # 操作类型为加法
        },
    )

    # 重置时的事件配置
    randomize_apply_external_force_torque = EventTerm(  # 随机化施加外部力和扭矩
        func=mdp.apply_external_force_torque,  # 调用的函数
        mode="reset",  # 事件模式为重置
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置,选择所有身体部件
            "force_range": (-10.0, 10.0),  # 力的范围
            "torque_range": (-10.0, 10.0),  # 扭矩的范围
        },
    )

    randomize_reset_joints = EventTerm(  # 随机化重置关节
        # func=mdp.reset_joints_by_scale,  # 通过缩放重置关节(已注释)
        func=mdp.reset_joints_by_offset,  # 通过偏移重置关节
        mode="reset",  # 事件模式为重置
        params={  # 参数配置
            "position_range": (-0.2, 0.2),  # 位置范围
            "velocity_range": (-2.5, 2.5),  # 速度范围
        },
    )

    randomize_actuator_gains = EventTerm(  # 随机化执行器增益
        func=mdp.randomize_actuator_gains,  # 调用的函数
        mode="reset",  # 事件模式为重置
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", joint_names=".*"),  # 资产配置,选择所有关节
            "stiffness_distribution_params": (0.5, 2.0),  # 刚度分布参数
            "damping_distribution_params": (0.5, 2.0),  # 阻尼分布参数
            "operation": "scale",  # 操作类型为缩放
            "distribution": "log_uniform",  # 分布类型为对数均匀分布
        },
    )

    # randomize_joint_limits = EventTerm(  # 随机化关节限制(已注释)
    #     func=mdp.randomize_joint_parameters,
    #     mode="reset",
    #     params={
    #         "asset_cfg": SceneEntityCfg("robot", joint_names=".*"),
    #         "lower_limit_distribution_params": (0.00, 0.01),
    #         "upper_limit_distribution_params": (0.00, 0.01),
    #         "operation": "add",
    #         "distribution": "gaussian",
    #     },
    # )

    randomize_reset_base = EventTerm(  # 随机化重置基座状态
        func=mdp.reset_root_state_uniform,  # 调用的函数
        mode="reset",  # 事件模式为重置
        params={  # 参数配置
            "pose_range": {"x": (-0.5, 0.5), "y": (-0.5, 0.5), "yaw": (-3.14, 3.14)},  # 姿态范围
            "velocity_range": {  # 速度范围
                "x": (-0.5, 0.5),
                "y": (-0.5, 0.5),
                "z": (-0.5, 0.5),
                "roll": (-0.5, 0.5),
                "pitch": (-0.5, 0.5),
                "yaw": (-0.5, 0.5),
            },
        },
    )

    # 间隔事件配置
    randomize_push_robot = EventTerm(  # 随机化推动机器人
        func=mdp.push_by_setting_velocity,  # 调用的函数
        mode="interval",  # 事件模式为间隔
        interval_range_s=(10.0, 15.0),  # 间隔时间范围
        params={"velocity_range": {"x": (-0.5, 0.5), "y": (-0.5, 0.5)}},  # 速度范围参数
    )

@configclass
class RewardsCfg:
    """MDP 的奖励项配置。"""

    # 一般奖励项
    is_terminated = RewTerm(func=mdp.is_terminated, weight=0.0)  # 终止状态的奖励项

    # 根部惩罚
    lin_vel_z_l2 = RewTerm(func=mdp.lin_vel_z_l2, weight=0.0)  # Z轴线速度的L2惩罚
    ang_vel_xy_l2 = RewTerm(func=mdp.ang_vel_xy_l2, weight=0.0)  # XY平面角速度的L2惩罚
    flat_orientation_l2 = RewTerm(func=mdp.flat_orientation_l2, weight=0.0)  # 平坦方向的L2惩罚
    base_height_l2 = RewTerm(  # 基座高度的L2惩罚
        func=mdp.base_height_l2,
        weight=0.0,
        params={  # 参数配置
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置,选择所有身体部件
            "sensor_cfg": SceneEntityCfg("height_scanner_base"),  # 传感器配置
            "target_height": 0.0,  # 目标高度
        },
    )
    body_lin_acc_l2 = RewTerm(  # 身体线加速度的L2惩罚
        func=mdp.body_lin_acc_l2,
        weight=0.0,
        params={"asset_cfg": SceneEntityCfg("robot", body_names="")},  # 资产配置
    )

    # 关节惩罚
    joint_torques_l2 = RewTerm(  # 关节扭矩的L2惩罚
        func=mdp.joint_torques_l2, weight=0.0, params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*")}
    )
    joint_vel_l2 = RewTerm(  # 关节速度的L2惩罚
        func=mdp.joint_vel_l2, weight=0.0, params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*")}
    )
    joint_acc_l2 = RewTerm(  # 关节加速度的L2惩罚
        func=mdp.joint_acc_l2, weight=0.0, params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*")}
    )

    def create_joint_deviation_l1_rewterm(self, attr_name, weight, joint_names_pattern):
        """创建关节偏差的L1奖励项。"""
        rew_term = RewTerm(
            func=mdp.joint_deviation_l1,  # 调用的函数
            weight=weight,  # 权重
            params={"asset_cfg": SceneEntityCfg("robot", joint_names=joint_names_pattern)},  # 参数配置
        )
        setattr(self, attr_name, rew_term)  # 将奖励项设置为属性

    joint_pos_limits = RewTerm(  # 关节位置限制的惩罚
        func=mdp.joint_pos_limits, weight=0.0, params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*")}
    )
    joint_vel_limits = RewTerm(  # 关节速度限制的惩罚
        func=mdp.joint_vel_limits,
        weight=0.0,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*"), "soft_ratio": 1.0},
    )
    joint_power = RewTerm(  # 关节功率的惩罚
        func=mdp.joint_power,
        weight=0.0,
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=".*"),
        },
    )

    stand_still_without_cmd = RewTerm(  # 无命令时保持静止的惩罚
        func=mdp.stand_still_without_cmd,
        weight=0.0,
        params={
            "command_name": "base_velocity",  # 命令名称
            "command_threshold": 0.1,  # 命令阈值
            "asset_cfg": SceneEntityCfg("robot", joint_names=".*"),  # 资产配置
        },
    )

    joint_pos_penalty = RewTerm(  # 关节位置惩罚
        func=mdp.joint_pos_penalty,
        weight=0.0,
        params={
            "command_name": "base_velocity",  # 命令名称
            "asset_cfg": SceneEntityCfg("robot", joint_names=".*"),  # 资产配置
            "stand_still_scale": 5.0,  # 保持静止的缩放因子
            "velocity_threshold": 0.5,  # 速度阈值
            "command_threshold": 0.1,  # 命令阈值
        },
    )

    wheel_vel_penalty = RewTerm(  # 轮子速度惩罚
        func=mdp.wheel_vel_penalty,
        weight=0.0,
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=""),  # 资产配置
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
            "command_name": "base_velocity",  # 命令名称
            "velocity_threshold": 0.5,  # 速度阈值
            "command_threshold": 0.1,  # 命令阈值
        },
    )

    joint_mirror = RewTerm(  # 关节镜像惩罚
        func=mdp.joint_mirror,
        weight=0.0,
        params={
            "asset_cfg": SceneEntityCfg("robot"),  # 资产配置
            "mirror_joints": [["FR.*", "RL.*"], ["FL.*", "RR.*"]],  # 镜像关节配置
        },
    )

    # 动作惩罚
    applied_torque_limits = RewTerm(  # 应用扭矩限制的惩罚
        func=mdp.applied_torque_limits,
        weight=0.0,
        params={"asset_cfg": SceneEntityCfg("robot", joint_names=".*")},
    )
    action_rate_l2 = RewTerm(func=mdp.action_rate_l2, weight=0.0)  # 动作速率的L2惩罚
    # smoothness_1 = RewTerm(func=mdp.smoothness_1, weight=0.0)  # 与action_rate_l2相同(已注释)
    # smoothness_2 = RewTerm(func=mdp.smoothness_2, weight=0.0)  # 目前不可用(已注释)

    # 接触传感器
    undesired_contacts = RewTerm(  # 不希望的接触惩罚
        func=mdp.undesired_contacts,
        weight=0.0,
        params={
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
            "threshold": 1.0,  # 阈值
        },
    )
    contact_forces = RewTerm(  # 接触力的惩罚
        func=mdp.contact_forces,
        weight=0.0,
        params={"sensor_cfg": SceneEntityCfg("contact_forces", body_names=""), "threshold": 100.0},
    )

    # 速度跟踪奖励
    track_lin_vel_xy_exp = RewTerm(  # XY平面线速度期望的跟踪奖励
        func=mdp.track_lin_vel_xy_exp, weight=0.0, params={"command_name": "base_velocity", "std": math.sqrt(0.25)}
    )
    track_ang_vel_z_exp = RewTerm(  # Z轴角速度期望的跟踪奖励
        func=mdp.track_ang_vel_z_exp, weight=0.0, params={"command_name": "base_velocity", "std": math.sqrt(0.25)}
    )

    # 其他奖励项
    feet_air_time = RewTerm(  # 脚在空中的时间奖励
        func=mdp.feet_air_time,
        weight=0.0,
        params={
            "command_name": "base_velocity",  # 命令名称
            "mode_time": 0.3,  # 模式时间
            "velocity_threshold": 0.5,  # 速度阈值
            "command_threshold": 0.1,  # 命令阈值
            "asset_cfg": SceneEntityCfg("robot"),  # 资产配置
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
        },
    )

    feet_gait = RewTerm(  # 脚步奖励
        func=mdp.GaitReward,
        weight=0.0,
        params={
            "std": math.sqrt(0.5),  # 标准差
            "command_name": "base_velocity",  # 命令名称
            "max_err": 0.2,  # 最大误差
            "velocity_threshold": 0.5,  # 速度阈值
            "command_threshold": 0.1,  # 命令阈值
            "synced_feet_pair_names": (("", ""), ("", "")),  # 同步脚对名称
            "asset_cfg": SceneEntityCfg("robot"),  # 资产配置
            "sensor_cfg": SceneEntityCfg("contact_forces"),  # 传感器配置
        },
    )

    feet_contact = RewTerm(  # 脚接触奖励
        func=mdp.feet_contact,
        weight=0.0,
        params={
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
            "command_name": "base_velocity",  # 命令名称
            "expect_contact_num": 2,  # 期望接触数量
        },
    )

    feet_contact_without_cmd = RewTerm(  # 无命令时脚接触奖励
        func=mdp.feet_contact_without_cmd,
        weight=0.0,
        params={
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
            "command_name": "base_velocity",  # 命令名称
        },
    )

    feet_stumble = RewTerm(  # 脚绊倒惩罚
        func=mdp.feet_stumble,
        weight=0.0,
        params={
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
        },
    )

    feet_slide = RewTerm(  # 脚滑动惩罚
        func=mdp.feet_slide,
        weight=0.0,
        params={
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=""),  # 传感器配置
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置
        },
    )

    feet_height = RewTerm(  # 脚高度奖励
        func=mdp.feet_height,
        weight=0.0,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置
            "tanh_mult": 2.0,  # tanh缩放因子
            "target_height": 0.05,  # 目标高度
            "command_name": "base_velocity",  # 命令名称
        },
    )

    feet_height_body = RewTerm(  # 身体高度奖励
        func=mdp.feet_height_body,
        weight=0.0,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置
            "tanh_mult": 2.0,  # tanh缩放因子
            "target_height": -0.3,  # 目标高度
            "command_name": "base_velocity",  # 命令名称
        },
    )

    feet_distance_y_exp = RewTerm(  # Y轴脚距离期望奖励
        func=mdp.feet_distance_y_exp,
        weight=0.0,
        params={
            "std": math.sqrt(0.25),  # 标准差
            "asset_cfg": SceneEntityCfg("robot", body_names=""),  # 资产配置
            "stance_width": float,  # 站立宽度
        },
    )

    # feet_distance_xy_exp = RewTerm(  # XY轴脚距离期望奖励(已注释)
    #     func=mdp.feet_distance_xy_exp,
    #     weight=0.0,
    #     params={
    #         "std": math.sqrt(0.25),
    #         "asset_cfg": SceneEntityCfg("robot", body_names=""),
    #         "stance_length": float,
    #         "stance_width": float,
    #     },
    # )

    upward = RewTerm(  # 向上的奖励
        func=mdp.upward,
        weight=0.0,
        params={
            "std": math.sqrt(0.5),  # 标准差
        },
    )
@configclass
class TerminationsCfg:
    """MDP 的终止条件配置。"""

    # MDP 终止条件
    time_out = DoneTerm(func=mdp.time_out, time_out=True)  # 超时终止条件

    # 命令重采样
    terrain_out_of_bounds = DoneTerm(  # 地形超出边界的终止条件
        func=mdp.terrain_out_of_bounds,  # 调用的函数
        params={"asset_cfg": SceneEntityCfg("robot"), "distance_buffer": 3.0},  # 参数配置
        time_out=True,  # 设置为超时终止
    )

    # 接触传感器
    illegal_contact = DoneTerm(  # 非法接触的终止条件
        func=mdp.illegal_contact,  # 调用的函数
        params={"sensor_cfg": SceneEntityCfg("contact_forces", body_names=""), "threshold": 1.0},  # 参数配置
    )


@configclass
class CurriculumCfg:
    """MDP 的课程配置。"""

    terrain_levels = CurrTerm(func=mdp.terrain_levels_vel)  # 地形级别的课程项

    command_levels = CurrTerm(  # 命令级别的课程项
        func=mdp.command_levels_vel,  # 调用的函数
        params={  # 参数配置
            "reward_term_name": "track_lin_vel_xy_exp",  # 奖励项名称
            "max_curriculum": 1.5,  # 最大课程值
        },
    )


##
# Environment configuration
##

@configclass
class LocomotionVelocityRoughEnvCfg(ManagerBasedRLEnvCfg):
    """用于运动速度跟踪环境的配置。"""

    # 场景设置
    scene: MySceneCfg = MySceneCfg(num_envs=4096, env_spacing=2.5)  # 创建场景配置,环境数量为4096,环境间距为2.5
    # 基本设置
    observations: ObservationsCfg = ObservationsCfg()  # 观察配置
    actions: ActionsCfg = ActionsCfg()  # 动作配置
    commands: CommandsCfg = CommandsCfg()  # 命令配置
    # MDP 设置
    rewards: RewardsCfg = RewardsCfg()  # 奖励配置
    terminations: TerminationsCfg = TerminationsCfg()  # 终止条件配置
    events: EventCfg = EventCfg()  # 事件配置
    curriculum: CurriculumCfg = CurriculumCfg()  # 课程配置

    def __post_init__(self):
        """后初始化设置。"""
        # 一般设置
        self.decimation = 4  # 降采样因子
        self.episode_length_s = 20.0  # 每集持续时间为20秒
        # 模拟设置
        self.sim.dt = 0.005  # 模拟时间步长
        self.sim.render_interval = self.decimation  # 渲染间隔
        self.sim.physics_material = self.scene.terrain.physics_material  # 设置物理材料
        self.sim.physx.gpu_max_rigid_patch_count = 10 * 2**15  # GPU最大刚体补丁数量

        # 更新传感器更新周期
        # 所有传感器的更新基于最小更新周期(物理更新周期)
        if self.scene.height_scanner is not None:
            self.scene.height_scanner.update_period = self.decimation * self.sim.dt  # 高度扫描仪更新周期
        if self.scene.contact_forces is not None:
            self.scene.contact_forces.update_period = self.sim.dt  # 接触力传感器更新周期

        # 检查地形级别课程是否启用 - 如果启用,则为地形生成器启用课程
        # 这将生成难度逐渐增加的地形,有助于训练
        if getattr(self.curriculum, "terrain_levels", None) is not None:
            if self.scene.terrain.terrain_generator is not None:
                self.scene.terrain.terrain_generator.curriculum = True  # 启用课程
        else:
            if self.scene.terrain.terrain_generator is not None:
                self.scene.terrain.terrain_generator.curriculum = False  # 禁用课程

    def disable_zero_weight_rewards(self):
        """如果奖励的权重为0,则将奖励设置为None。"""
        for attr in dir(self.rewards):  # 遍历奖励属性
            if not attr.startswith("__"):  # 排除特殊属性
                reward_attr = getattr(self.rewards, attr)  # 获取奖励属性
                if not callable(reward_attr) and reward_attr.weight == 0:  # 如果不是可调用且权重为0
                    setattr(self.rewards, attr, None)  # 将奖励设置为None


def create_obsgroup_class(class_name, terms, enable_corruption=False, concatenate_terms=True):
    """
    根据给定的配置项动态创建并注册一个 ObsGroup 类。

    :param class_name: 配置类的名称。
    :param terms: 配置项,一个字典,其中键是项名称,值是项内容。
    :param enable_corruption: 是否启用观察组的损坏。默认为 False。
    :param concatenate_terms: 是否连接观察项。默认为 True。
    :return: 动态创建的类。
    """
    # 动态确定模块名称
    module_name = inspect.getmodule(inspect.currentframe()).__name__

    # 定义后初始化函数
    def post_init_wrapper(self):
        setattr(self, "enable_corruption", enable_corruption)  # 设置启用损坏
        setattr(self, "concatenate_terms", concatenate_terms)  # 设置连接观察项

    # 动态创建类,使用 ObsGroup 作为基类
    terms["__post_init__"] = post_init_wrapper  # 添加后初始化函数
    dynamic_class = configclass(type(class_name, (ObsGroup,), terms))  # 创建动态类

    # 自定义序列化和反序列化
    def __getstate__(self):
        state = self.__dict__.copy()  # 复制当前状态
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)  # 更新状态

    # 将自定义序列化方法添加到类中
    dynamic_class.__getstate__ = __getstate__
    dynamic_class.__setstate__ = __setstate__

    # 将类放入全局命名空间以便访问
    globals()[class_name] = dynamic_class

    # 在模块字典中注册动态类
    if module_name in sys.modules:
        sys.modules[module_name].__dict__[class_name] = dynamic_class
    else:
        raise ImportError(f"模块 {module_name} 未找到。")

    # 返回类以供外部实例化
    return dynamic_class

2.3.2 B2W 粗糙地形配置 (rough_env_cfg.py)

这个配置继承基础环境,并进行了针对Unitree B2W特性的调整:

@configclass
class UnitreeB2WRoughEnvCfg(LocomotionVelocityRoughEnvCfg):
    actions: UnitreeB2WActionsCfg = UnitreeB2WActionsCfg()
    rewards: UnitreeB2WRewardsCfg = UnitreeB2WRewardsCfg()
    
    base_link_name = "base_link"
    foot_link_name = ".*_foot"
    wheel_joint_name = ".*_foot_joint"

关键定制包括:

  • 混合动作空间: 同时支持关节位置控制和轮子速度控制
  • B2W特定奖励: 调整了奖励权重以适应轮腿机器人特性
  • 轮腿结构适配: 通过区分腿部关节和车轮关节的处理方式

…详情请参照古月居

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐