前言

本文一开始是属于此文《NaVILA——可语音交互的用于四足和人形导航与避障的VLA模型:在VLM的导航规划下,执行基于视觉的运动策略》的

但为避免原文篇幅过长,故「把原理 + 部署 ——详见此文 」,与「源码剖析」的部分 各自独立开来

故,后者《源码剖析》的部分成此文

第一部分 NaVILA VLA部分的源码

// 待更

第二部分 NaVILA/legged-loco中isaaclab_exts/模块的解析:侧重H1人形机器人配置

整体代码库主要分为以下几个部分:

  1. isaaclab_exts - Isaac Lab的扩展,包含机器人配置和控制逻辑
  2.  rsl_rl - 强化学习框架实现
  3. scripts - 训练、演示和使用的脚本
  4. src - 资源文件,包含演示GIF等

其中的isaaclab_exts/这个目录是Isaac Lab的扩展模块,结构如下:

isaaclab_exts/
    omni.isaac.leggedloco/ - 主要扩展模块
        setup.py - 安装脚本
        config/
            extension.toml - 扩展配置文件
        omni/
            isaac/
                leggedloco/
                    config/ - 不同机器人的配置
                        g1/ - G1机器人配置
                        go1/ - Go1机器人配置
                        go2/ - Go2机器人配置
                        h1/ - H1机器人配置
                    leggedloco/ - 核心功能实现
                        mdp/ - MDP(马尔可夫决策过程)相关实现
                            actions/ - 动作空间定义
                            commands/ - 命令生成器
                            rewards/ - 奖励函数
                    utils/ - 工具函数

`omni/isaac/leggedloco/config`模块是整个系统的核心配置部分,它定义了:

  • 四种机器人模型(Go1、Go2、G1、H1)的物理参数和控制特性
  • 两种环境模式(基础和视觉增强)的完整配置

G1, Go1, Go2, H1的每个机器人子目录中都遵循一致的配置模式,主要包含两类配置文件:

  1. 基础配置 (`*_low_base_cfg.py`) - 包含基础运动控制,不使用视觉信息
  2. 视觉配置 (`*_low_vision_cfg.py`) - 在基础配置上增加了视觉感知能力
  • 强化学习训练参数**,包括奖励函数、观察空间、域随机化策略等

2.1 Go1和GO2的机器人配置/omni/isaac/leggedloco/config/go1/

Go1是Unitree公司的四足机器人。它有两个主要配置文件:

2.1.1 go1_low_base_cfg.py

这个文件定义了Go1机器人的基础配置:

  1. 导入必要的依赖和基础类
  2. 定义`Go1RoughPPORunnerCfg`训练配置类
  3. 定义`UNITREE_GO1_CFG`机器人物理配置
  4. 定义`Go1_BASE_TERRAINS_CFG`地形生成配置
  5. 定义`Go1SceneCfg`场景配置类
  6. 定义`CustomGo1RewardsCfg`奖励函数配置
  7. 定义`ObservationsCfg`观察空间配置
  8. 定义`EventCfg`事件和域随机化配置
  9. 定义`Go1BaseRoughEnvCfg`完整环境配置类
  10. 定义`Go1BaseRoughEnvCfg_PLAY`用于演示的环境配置类

2.1.2 go1_low_vision_cfg.py

在基础配置的基础上,增加了视觉能力:

  1. 导入基础配置
  2. 定义`Go1VisionRoughPPORunnerCfg`支持视觉的训练配置
  3. 扩展`Go1SceneCfg`,添加深度传感器等视觉组件
  4. 配置视觉观察空间
  5. 定义`Go1VisionRoughEnvCfg`完整的视觉环境配置
  6. 定义`Go1VisionRoughEnvCfg_PLAY`用于演示的视觉环境配置

2.2 Go2 机器人配置 (/omni/isaac/leggedloco/config/go2/)

Go2是Unitree公司的另一种四足机器人,配置结构类似于Go1:

2.2.1 go2_low_base_cfg.py

  1. 定义`Go2RoughPPORunnerCfg`训练配置
  2. 定义`UNITREE_GO2_CFG`机器人物理配置
  3. 定义`Go2_BASE_TERRAINS_CFG`地形配置
  4. 定义`Go2SceneCfg`场景配置
  5. 定义`CustomGo2RewardsCfg`奖励函数
  6. 定义观察空间、事件和完整环境配置

2.2.2 go2_low_vision_cfg.py

  1. 继承基础配置
  2. 定义`Go2VisionRoughPPORunnerCfg`
  3. 定义`Go2_Vision_TERRAINS_CFG`视觉地形配置
  4. 扩展场景配置,添加视觉传感器
  5. 定义`Go2VisionRoughEnvCfg`视觉环境配置

2.3 G1 人形机器人配置 (/omni/isaac/leggedloco/config/g1/)

G1是一种人形机器人,配置结构与四足机器人类似:

2.3.1 g1_low_base_cfg.py

  1. 定义`G1RoughPPORunnerCfg`训练配置
  2. 定义`ROUGH_TERRAINS_CFG`地形配置
  3. 定义`G1SceneCfg`场景配置
  4. 定义`RewardsCfg2DoF`针对2自由度关节的奖励
  5. 定义`CustomG1Rewards`自定义奖励
  6. 定义`EventCfg`事件和域随机化
  7. 定义`G1BaseRoughEnvCfg`环境配置

2.3.2 g1_low_vision_cfg.py

  1. 导入基础配置
  2. 定义`G1VisionRoughPPORunnerCfg`视觉训练配置
  3. 定义`G1_NO_ARMS_CFG`无臂版G1机器人配置
  4. 定义视觉地形和场景配置
  5. 定义`G1VisionRoughEnvCfg`视觉环境配置

2.4 H1人形机器人配置/omni/isaac/leggedloco/config/h1/

H1是另一种人形机器人,配置结构与G1类似,主要包含h1_low_base_cfg.py、h1_low_vision_cfg.py,而它两的主要区别在此

h1_low_base_cfg.py h1_low_vision_cfg.py
地形类型和复杂度 基础配置只有两种简单地形:
- 50%平坦地形
- 50%轻微随机粗糙地形,噪声范围较小
视觉配置采用了更加复杂多样的地形环境:
- 包括7种不同类型的地形(金字塔阶梯、倒金字塔阶梯、箱子地形、随机粗糙地形、两种斜坡地形、离散障碍物)
- 每种地形都配置有平坦区域采样点,方便机器人适应不同地形间的过渡
- 障碍物高度最高达1.5米,明显增加了难度
感知能力与观察空间 基础配置:
- 策略网络观察中没有地形感知能力
- 只依赖基本的本体感受信息(关节位置、速度、重力等
视觉配置的核心特点是:
- 策略网络中加入了`height_scan`地形扫描观察,没有噪声干扰
- 通过射线投射技术实现地形感知,使机器人能够"看到"前方地形
- 提供了保留但被注释掉的激光雷达和深度相机配置,为未来拓展做准备
- 使用天空光照(sky_light)而非单一定向光源,更加真实
重置和初始化策略 视觉配置使用了地形感知的重置策略:
- `reset_root_state_from_terrain`替换了`reset_root_state_uniform`
- 这允许机器人根据地形情况进行更合理的初始化
奖励权重调整 视觉配置中:
- 平坦姿态惩罚权重(`flat_orientation_l2.weight`)从-5.0减轻到-1.0
- 这使机器人在复杂地形上有更大的姿态调整灵活性
训练与评估设置 基础配置在评估时使用了更简单的环境设置 视觉配置更适合在复杂地形上进行评估,因为它保留了多样化的地形类型
本质区别 本质区别在于,h1_low_vision_cfg.py通过环境感知增强了H1机器人的感知能力,使其能够"看到"并适应更加复杂多变的地形,而不仅仅依赖于本体感受和经验。这种感知型策略使得机器人能够预见并规划如何应对前方的地形挑战,而基础配置中机器人则更多依赖于反应式控制策略,缺乏对环境的前瞻性认知

2.4.1 h1_low_base_cfg.py:涉及PPO、地形、场景、观察空间(比如线速度/角速度等)、动作空间、奖励等一系列配置

2.4.1.1 导入部分

这部分导入了

  1. Isaac Lab的各种模拟工具、资产配置、管理器
  2. 传感器、地形生成器和噪音模型等组件
  3. 自定义的MDP(马尔可夫决策过程)组件
  4. 预定义的H1机器人模型配置(`H1_MINIMAL_CFG`)
2.4.1.2 PPO 训练配置

首先是 `H1RoughPPORunnerCfg` 类,配置了 PPO(近端策略优化)训练算法的参数:

  1. 每个环境的步数设为 32
  2. 最大迭代次数 50000,每 500 次迭代保存一次模型
  3. 使用三层神经网络架构作为 Actor 和 Critic,尺寸分别为 [512, 256, 128]
  4. 激活函数采用 ELU,初始噪声标准差为 1.0
  5. PPO 超参数配置:学习率 0.001,衰减因子 γ=0.99,λ=0.95,以及熵系数 0.005
2.4.1.3 地形配置

`BASE_TERRAIN_CFG` 定义了两种地形类型的混合

  1. 50% 的平坦地形(flat)
  2. 50% 的随机粗糙地形(random_rough),噪声范围为 0.02 到 0.10
2.4.1.4 场景配置

`BaseSceneCfg` 描述了仿真环境中的各种元素:

  1. 地形配置,包含物理材质(摩擦系数为 1.0)和视觉材质
  2. 机器人模型及其位置
  3. 接触力传感器,用于检测机器人与地面的接触
  4. 高度扫描器,使用射线投射技术检测地形高度
  5. 光照设置
2.4.1.5 观察空间配置

`ObservationsCfg` 定义了三组观察空间:

  1. `PolicyCfg`:策略网络的输入,包含基本动作信息和噪声,如:
       - 线速度、角速度、重力投影
       - 速度指令
       - 关节位置、速度
       - 上一步动作
  2. `ProprioCfg`:类似于 PolicyCfg,但噪声参数有所不同
  3. CriticObsCfg`:评论家网络的输入,与策略网络类似,但增加了地形高度扫描,没有噪声干扰
2.4.1.6 动作空间与事件配置
  1. `ActionsCfg` 定义了动作空间,使用关节位置控制
  2. `EventCfg` 配置了训练过程中的各种随机化事件,包括:
      - 物理材质随机化
      - 重置机器人位置、姿态和关节状态等
2.4.1.7 奖励与指令配置
  1. `CustomH1Rewards` 在基础奖励上增加了对脚部绊倒的惩罚
  2. `CommandsCfg` 定义了速度指令的范围和重采样时间
  3. `TerminationsCfg` 设置了终止条件,如时间结束或机器人躯干接触地面
2.4.1.8 主环境配置

`H1BaseRoughEnvCfg` 整合了上述所有配置,并在 `__post_init__` 中进行了额外设置:

  1. 仿真步长为 0.005 秒,每集长度 20 秒
  2. 优化了摩擦和弹性参数
  3. 调整了奖励权重,例如平坦姿态奖励权重为 -5.0
2.4.1.9 游戏测试配置

`H1BaseRoughEnvCfg_PLAY` 继承自主环境配置,但做了以下调整:

  1. 减少环境数量从 4096 到 40,以减轻计算负担
  2. 延长每集时间到 40 秒
  3. 固定机器人速度指令(前向速度固定为 0.5)
  4. 禁用了观察噪声和随机推力

这个配置主要用于在训练后对模型进行评估和演示

2.4.2 h1_low_vision_cfg.py:基础感知之上增加视觉感知导航

这段代码定义了一个面向H1四足机器人的强化学习环境配置,特别针对复杂地形中的视觉感知导航任务。与基础配置相比,该配置加入了更丰富的地形类型和环境感知能力。

2.4.2.1 强化学习算法配置

`H1VisionRoughPPORunnerCfg`类继承自`H1RoughPPORunnerCfg`,保留了原有的PPO算法超参数设置,如学习率、衰减因子、网络架构等,但将实验名称更改为"h1_vision_rough",表明这是一个基于视觉的复杂地形导航实验

2.4.2.2 复杂地形配置

`ROUGH_TERRAINS_CFG`定义了七种不同类型的地形,每种占据不同比例:

  1. 金字塔阶梯(20%):阶梯高度在0.05到0.3米之间
  2. 倒金字塔阶梯(20%):与上面相反的阶梯结构
  3. 随机箱体地形(20%):随机分布的箱形障碍物
  4. 随机粗糙地形(20%):带有0.02-0.10米噪声的不平整表面
  5. 金字塔斜坡(10%):斜率在0-0.4之间的斜坡地形
  6. 倒金字塔斜坡(10%):与上面相反的斜坡结构
  7. 离散障碍物(20%):固定高度为1.5米的障碍物

同时,为每种地形添加了平坦区域采样点,方便机器人学习从平地过渡到复杂地形。

2.4.2.3 场景与传感器配置

`TrainSceneCfg`类配置了训练场景,包括:

  1. 地形导入器,使用上述复杂地形生成器
    class TrainSceneCfg(InteractiveSceneCfg):  # 定义训练场景配置,继承自交互场景配置
        """带有腿式机器人的地形场景配置"""
    
        # 地面地形
        terrain = TerrainImporterCfg(               # 地形导入器配置
            prim_path="/World/ground",              # 地形基元路径
            terrain_type="generator",               # 使用生成器类型
            terrain_generator=ROUGH_TERRAINS_CFG,   # 使用上面定义的粗糙地形生成器
            max_init_terrain_level=5,               # 可以尝试设为9
            collision_group=-1,                     # 碰撞组设为-1
            physics_material=sim_utils.RigidBodyMaterialCfg(      # 物理材质配置
                friction_combine_mode="multiply",          # 摩擦力组合模式为乘法
                restitution_combine_mode="multiply",       # 弹性组合模式为乘法
                static_friction=1.0,                       # 静摩擦系数1.0
                dynamic_friction=1.0,                      # 动摩擦系数1.0
            ),
    
            # 视觉材质配置
            visual_material=sim_utils.MdlFileCfg(  
                # 材质文件路径
                mdl_path=f"{ISAACLAB_NUCLEUS_DIR}/Materials/TilesMarbleSpiderWhiteBrickBondHoned/TilesMarbleSpiderWhiteBrickBondHoned.mdl",  
    
                # 投影UV坐标
                project_uvw=True,  
                # 纹理缩放比例
                texture_scale=(0.25, 0.25),  
            ),
    
             # 不显示调试可视化
            debug_vis=False, 
        )
  2. 机器人模型配置
        # 机器人
        # 使用H1最小配置,设置基元路径
        robot = H1_MINIMAL_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")  
  3. 接触力传感器,用于检测机器人与地面的接触
        # 传感器
        # 接触力传感器配置
        contact_forces = ContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, track_air_time=True, debug_vis=False)  
  4. 高度扫描器,使用射线投射技术检测前方地形高度
        # 高度扫描器配置
        height_scanner = RayCasterCfg(  
            # 基元路径
            prim_path="{ENV_REGEX_NS}/Robot/base",  
    
            # 设置偏移位置,向上20米
            offset=RayCasterCfg.OffsetCfg(pos=(0.0, 0.0, 20.0)),  
    
             # 仅附加偏航角
            attach_yaw_only=True, 
    
            # 网格模式,分辨率0.1,大小1.6x1.0
            pattern_cfg=patterns.GridPatternCfg(resolution=0.1, size=[1.6, 1.0]),  
    
            # 不显示调试可视化
            debug_vis=False,  
    
            # 扫描的网格基元路径
            mesh_prim_paths=["/World/ground"],  
        )
    
        # 修改扫描器基元路径为机器人躯干
        height_scanner.prim_path = "{ENV_REGEX_NS}/Robot/torso_link"  
    
        # 相机
        # 激光雷达传感器为空
        lidar_sensor = None  
    
        # 深度传感器为空
        depth_sensor = None
  5. 天空光照效果
        # 灯光
        # 天空光配置
        sky_light = AssetBaseCfg(  
            # 基元路径
            prim_path="/World/skyLight",
    
            # 使用半球光  
            spawn=sim_utils.DomeLightCfg(  
                # 光强度750.0
                intensity=750.0,  
    
                # 天空纹理文件
                texture_file=f"{ISAAC_NUCLEUS_DIR}/Materials/Textures/Skies/PolyHaven/kloofendal_43d_clear_puresky_4k.hdr",  
            ),
        )

值得注意的是,代码中保留了深度传感器和激光雷达的接口,但当前设置为`None`,这表明未来可能会整合这些传感器。

2.4.2.4 观察空间配置

`ObservationsCfg`类定义了三种观察类型:

  1.  `PolicyCfg`:策略网络使用的观察,包括:
    基本动态信息:线速度、角速度、重力投影
        # 配置类装饰器
        # 策略观察组配置,用于策略网络
        class PolicyCfg(ObsGroup):  
            """策略组的观察"""
    
            # 观察项(保持顺序)
            # 基座线速度
            base_lin_vel = ObsTerm(func=mdp.base_lin_vel)      
        
            # 基座角速度      
            base_ang_vel = ObsTerm(func=mdp.base_ang_vel)            
    
            # 投影重力
            projected_gravity = ObsTerm(func=mdp.projected_gravity)
    关节状态:位置、速度
             # 速度命令
            velocity_commands = ObsTerm(func=mdp.generated_commands, params={"command_name": "base_velocity"})              
    
            # 关节位置        
            joint_pos = ObsTerm(func=mdp.joint_pos_rel)      
    
            # 关节速度
            joint_vel = ObsTerm(func=mdp.joint_vel_rel) 
    历史动作
            # 上一步动作
            actions = ObsTerm(func=mdp.last_action)     
    地形高度扫描:这是与基础配置的主要区别
            # 高度扫描结果
            height_scan = ObsTerm(              
                # 高度扫描函数
                func=mdp.height_scan,           
    
                 # 使用高度扫描器
                params={"sensor_cfg": SceneEntityCfg("height_scanner")}, 
    
                 # 限制值范围在-1到1之间
                clip=(-1.0, 1.0), 
  2. `ProprioCfg`:本体感受观察,包含噪声干扰
        # 配置类装饰器
        # 本体感受观察组配置
        class ProprioCfg(ObsGroup):   
            """本体感受组的观察"""
    
            # 观察项
            # 基座线速度,带噪声
            base_lin_vel = ObsTerm(func=mdp.base_lin_vel, noise=Unoise(n_min=-0.2, n_max=0.2))          
    
             # 基座角速度,带噪声
            base_ang_vel = ObsTerm(func=mdp.base_ang_vel, noise=Unoise(n_min=-0.2, n_max=0.2))         
    
             # 投影重力,带噪声
            projected_gravity = ObsTerm(     
                func=mdp.projected_gravity,
                noise=Unoise(n_min=-0.05, n_max=0.05),
            )
            
            # 速度命令
            velocity_commands = ObsTerm(func=mdp.generated_commands, params={"command_name": "base_velocity"})    
    
            # 关节位置,带噪声  
            joint_pos = ObsTerm(func=mdp.joint_pos_rel, noise=Unoise(n_min=-0.01, n_max=0.01))      
    
            # 关节速度,带噪声
            joint_vel = ObsTerm(func=mdp.joint_vel_rel, noise=Unoise(n_min=-1.5, n_max=1.5))       
    
            # 上一步动作
            actions = ObsTerm(func=mdp.last_action)    
    
            # 后初始化方法
            def __post_init__(self): 
                 # 拼接所有观察项 
                self.concatenate_terms = True      
  3. `CriticObsCfg`:评论家网络的观察,类似于策略网络但包含噪声设置
        # 评论家观察组配置
        class CriticObsCfg(ObsGroup):  
            # 观察项(保持顺序)
            # 基座线速度,带噪声
            base_lin_vel = ObsTerm(func=mdp.base_lin_vel, noise=Unoise(n_min=-0.1, n_max=0.1))      
    
            # 基座角速度,带噪声
            base_ang_vel = ObsTerm(func=mdp.base_ang_vel, noise=Unoise(n_min=-0.2, n_max=0.2))      
    
            # 投影重力,带噪声
            projected_gravity = ObsTerm(  
                func=mdp.projected_gravity,
                noise=Unoise(n_min=-0.05, n_max=0.05),
            )
            
             # 速度命令
            velocity_commands = ObsTerm(func=mdp.generated_commands, params={"command_name": "base_velocity"}) 
    
            # 关节位置,带噪声
            joint_pos = ObsTerm(func=mdp.joint_pos_rel, noise=Unoise(n_min=-0.01, n_max=0.01))  
    
            # 关节速度,带噪声
            joint_vel = ObsTerm(func=mdp.joint_vel_rel, noise=Unoise(n_min=-1.5, n_max=1.5))  
    
            # 上一步动作
            actions = ObsTerm(func=mdp.last_action)  
    
            # 高度扫描结果
            height_scan = ObsTerm(  
                func=mdp.height_scan,
                params={"sensor_cfg": SceneEntityCfg("height_scanner")},
                clip=(-1.0, 1.0),
            )
2.4.2.5 奖励与终止条件

`CustomH1Rewards`类在基础奖励上增加了对脚部绊倒的惩罚,但环境配置中实际使用的是标准`H1Rewards`

终止条件包括时间到期和躯干接触地面

# 自定义H1奖励,继承自H1Rewards
class CustomH1Rewards(H1Rewards):  
    # 添加脚部绊倒惩罚
    feet_stumble = RewTerm(  
        # 脚部绊倒函数
        func=mdp.feet_stumble,  

        # 权重-0.5(惩罚)
        weight=-0.5,  
        params={ 
            # 使用踝关节的接触力传感器
            "sensor_cfg": SceneEntityCfg("contact_forces", body_names=".*ankle_link"),  
        },
    )
2.4.2.6 训练与测试环境配置

主环境配置`H1VisionRoughEnvCfg`设定了:

  1. 4096个并行环境用于训练
  2. 每集20秒的仿真时间
  3. 物理参数如摩擦系数、弹性系数等
  4. 奖励权重调整,如平坦姿态奖励权重为-1.0
  5. 命令范围:前向速度在0到1之间,角速度在-1到1之间

`H1VisionRoughEnvCfg_PLAY`配置专为测试设计:

  1. 只使用40个环境
  2. 延长每集时间到40秒
  3. 减少地形复杂度以节省内存
  4. 固定前向速度为0.5,取消转向
  5. 禁用观察噪声和随机外力

整体看来,这个配置与基础配置相比,最大的不同在于添加了更为多样化的复杂地形以及地形感知能力,使H1机器人能够通过高度扫描获取前方地形信息,从而学习如何应对各种复杂地形的导航策略

第三部分 核心MDP组件omni/isaac/leggedloco/leggedloco/mdp

mdp目录包含了马尔可夫决策过程(MDP)的关键组件,这是强化学习环境的核心部分

3.1 actions/

这个目录包含定义机器人动作空间的类,例如:

  1. `navigation_actions.py` - 导航动作
  2. `vlm_navigation_actions_gpt.py` - 基于GPT的视觉-语言模型导航动作
  3. `vlm_navigation_actions.py` - 视觉-语言模型导航动作

3.1.1 navigation_actions.py:高层导航、底层控制

这段代码实现了一个分层控制系统,用于机器人导航任务。`NavigationAction` 类是一个高层控制器,负责将导航指令转换为低层运动控制动作,形成了一个典型的分层机器人控制架构

  • 高层导航控制:接收导航指令(如前进、转向等),频率为10Hz
  • 低层运动控制:执行实际的关节控制,频率为50Hz「由于物理引擎运行在200Hz,因此采用了4:1的抽取比例」

这种分层架构允许导航策略专注于路径规划和障碍物避开,而将复杂的运动动力学控制委托给预训练的低层策略

首先,初始化过程中,系统加载一个预训练的低层控制策略:

  1. 首先检查配置中指定的策略文件是否存在
  2. 使用`torch.jit.load`加载策略模型,并将其冻结为评估模式
  3. 准备所需的动作缓冲区,包括原始导航指令、处理后的导航指令和低层动作

低层策略接收来自环境的观察("low_level_policy"组),并生成关节控制命令

        # 加载策略
        # 加载TorchScript策略模型到指定设备
        self.low_level_policy = torch.jit.load(file_bytes, map_location=self.device)  

        # 冻结策略模型并设为评估模式
        self.low_level_policy = torch.jit.freeze(self.low_level_policy.eval())  

这些命令随后通过`low_level_action_term`类处理并应用于机器人

        # 准备关节位置动作
        # 创建低层动作处理对象
        self.low_level_action_term: ActionTerm = self.cfg.low_level_action.class_type(cfg.low_level_action, env)  

其次,系统运行时遵循以下流程

  1. `process_actions`方法以10Hz的频率处理高层导航动作,将它们转换为速度指令(前进速度、横向速度和角速度)
        # 处理动作方法
        def process_actions(self, actions):  
            """处理高层导航动作。该函数以10Hz的频率被调用"""
    
            # 存储低层导航动作
            self._raw_navigation_velocity_actions[:] = actions  
    
            # 将动作重塑为3D导航命令
            self._processed_navigation_velocity_actions[:] = actions.clone().view(self.num_envs, 3) 
  2. `apply_actions`方法以200Hz的频率运行,但每4次迭代(即50Hz)才执行一次低层控制计算:
       - 更新命令管理器
       - 获取环境观察
       - 通过低层策略生成关节控制命令
       - 处理并应用这些命令
        def apply_actions(self):  # 应用动作方法
            """将低层动作应用到物理引擎的模拟器。此函数以200Hz的模拟频率被调用。由于低层运动控制以50Hz运行,我们需要抽取动作。"""
    
            # 如果计数器是抽取因子的整数倍
            if self._counter % self.cfg.low_level_decimation == 0:  
                self._counter = 0  # 重置计数器
            #     # -- 更新命令
                self._env.command_manager.compute(dt=self._low_level_step_dt)  # 更新命令管理器
                # 从低层策略获取低层动作
                # 使用低层策略处理观察获得动作
                self._low_level_actions[:] = self.low_level_policy(
                    self._env.observation_manager.compute_group(group_name="low_level_policy")  
                )
    
                # 处理低层动作
                self.low_level_action_term.process_actions(self._low_level_actions)  
    
            # 应用低层动作到物理引擎
            self.low_level_action_term.apply_actions()  
    
             # 增加计数器
            self._counter += 1

这种抽取方法(每4次物理更新执行1次控制计算)能够减少计算负担,同时保持合理的控制频率

`NavigationActionCfg` 类定义了控制器的配置参数,包括:

  1. 低层抽取系数(默认为4,使低层控制频率为50Hz)
  2. 低层策略文件路径
  3. 低层动作配置
  4. 路径长度(51个点)
  5. 图像尺寸(暂未使用,代码中有相关注释掉的深度图像处理部分)

值得注意的是,代码中保留了一些已注释的图像处理功能,包括深度CNN和图像调整,表明该系统可能原本设计支持或未来计划支持视觉引导导航

3.1.2 vlm_navigation_actions_gpt.py:高层规划决策、低层运动执行

这段代码实现了一个用于机器人导航的分层控制架构。该系统的设计目的是通过视觉语言模型(VLM)和GPT技术来增强机器人的导航能力,建立了从高级导航指令到低级运动控制的映射关系

`VLMActionsGPT`类采用了典型的分层控制设计:

  • 高层导航控制:负责处理速度指令(前进速度、横向速度和旋转速度),以10Hz的频率运行
  • 低层运动控制:负责执行实际的关节控制,以50Hz的频率运行(由于物理引擎运行在200Hz,采用4:1抽取比例)

剩下的类似上节..

3.1.3 vlm_navigation_actions.py

`VLMActions`类设计了一个典型的两层控制系统:

  • 高层导航控制:以10Hz的频率运行,处理三维导航指令(前进速度、横向速度和旋转速度)
  • 低层运动执行:以50Hz的频率运行(通过4:1的抽取比例从200Hz的物理更新中获得),负责实际的关节控制

这种分层设计思路将复杂的导航问题分解为"决策"和"执行"两个相对独立的子问题,使系统更容易开发和维护。高层专注于"往哪里去"的问题,而低层专注于"如何到达"的问题

首先,系统在初始化阶段执行以下关键步骤:

  1. 验证低层策略文件是否存在,若不存在则抛出`FileNotFoundError`异常
            # 初始化图像计数器为0
            self.image_count = 0  
    
            # 检查策略文件是否存在
            if not check_file_path(self.cfg.low_level_policy_file): 
                # 抛出文件未找到异常
                raise FileNotFoundError(f"Policy file '{self.cfg.low_level_policy_file}' does not exist.")  
    
            # 读取低层策略文件内容为字节
            file_bytes = read_file(self.cfg.low_level_policy_file)  
  2. 使用`torch.jit.load`加载预训练的低层策略模型,并通过`freeze`和`eval`方法优化其性能
            # 加载策略
            # 使用JIT加载低层策略模型到当前设备
            self.low_level_policy = torch.jit.load(file_bytes, map_location=self.device)  
    
            # 冻结策略模型并设置为评估模式以提高性能
            self.low_level_policy = torch.jit.freeze(self.low_level_policy.eval())  
  3. 初始化低层动作执行器,用于将策略输出转换为物理控制指令
            # 准备关节位置动作
            # 创建低层动作项实例
            self.low_level_action_term: ActionTerm = self.cfg.low_level_action.class_type(cfg.low_level_action, env)  
  4. 创建三种动作缓冲区:原始导航动作、处理后的命令速度动作和低层控制动作
            # 准备缓冲区
            # 设置动作维度为3,包含前向速度、横向速度和旋转角速度 
            self._action_dim = (
                3
            )  
    
            # 创建原始导航速度动作张量
            self._raw_navigation_velocity_actions = torch.zeros(self.num_envs, self._action_dim, device=self.device)  
    
            # 创建处理后的命令速度动作张量
            self._processed_command_velocity_actions = torch.zeros(
                (self.num_envs, 3), device=self.device
            )

值得注意的是,系统预留了图像处理相关的代码(尽管被注释掉了),包括深度图像处理和图像大小调整,这表明该系统可能计划或曾经支持视觉输入处理

其次,动作处理分为两个主要阶段:

  1. 高层动作处理(`process_actions`方法):
       - 接收并存储导航指令
       - 将指令重塑为标准三维向量形式(vx, vy, omega)
       - 这些指令的范围约束在:vx[-0.5,1.0]、vy[-0.5,0.5]、omega[-1.0,1.0]
        # 处理动作方法
        def process_actions(self, actions):  
            # 处理高层导航动作,此函数以10Hz的频率被调用
    
            # 将输入动作存储到原始导航速度动作缓冲区
            self._raw_navigation_velocity_actions[:] = actions  
    
            # 重塑为3D路径
            # 克隆动作并重塑为(num_envs, 3)的形状存入处理后的命令速度动作缓冲区
            self._processed_command_velocity_actions[:] = actions.clone().view(self.num_envs, 3)  
  2. 低层动作执行(`apply_actions`方法):
       - 采用计数器进行频率控制,每4个物理时间步执行一次控制计算
       - 更新环境的命令系统
       - 通过低层策略处理环境观察,生成关节控制命令
       - 应用这些命令到物理引擎
        def apply_actions(self):  # 应用动作方法
            # 将低层动作应用到物理引擎,此函数以200Hz的仿真频率被调用,由于低层运动控制以50Hz运行,我们需要降采样动作
    
            # 如果计数器是降采样因子的整数倍
            if self._counter % self.cfg.low_level_decimation == 0:  
                self._counter = 0          # 重置计数器为0
    
                # 使用低层步长时间间隔计算命令管理器
                self._env.command_manager.compute(dt=self._low_level_step_dt)  
    
                # 使用观察管理器计算低层策略所需的观察组
                 # 将低层策略的输出存储到低层动作缓冲区
                self._low_level_actions[:] = self.low_level_policy(
                    self._env.observation_manager.compute_group(group_name="low_level_policy")  
                ) 
    
                # 处理低层动作
                # 使用低层动作项处理低层动作
                self.low_level_action_term.process_actions(self._low_level_actions) 
    
            # 使用低层动作项应用动作
            self.low_level_action_term.apply_actions()  
    
            # 计数器加1
            self._counter += 1

整个过程形成了一个闭环:高层导航指令→低层运动策略→关节控制命令→物理执行→环境反馈→低层运动策略...

最后,`VLMActionsCfg`类定义了系统的配置参数:

  1. 低层控制频率抽取系数(默认为4)
  2. 低层动作配置(由使用者提供)
  3. 低层策略文件路径(必须提供)

系统设计允许通过配置文件调整这些参数,而无需修改代码,增强了系统的灵活性。值得注意的是,配置中还保留了一些被注释掉的参数(如路径长度和图像尺寸),这可能是未来功能的占位符

3.2 commands/

命令生成器相关文件,例如:

  1. `goal_command_generator_cfg.py` - 目标命令生成器配置
  2. `goal_command_generator.py` - 目标命令生成器实现

3.3 rewards/

奖励函数相关的实现,用于在强化学习过程中对机器人行为进行评价。从配置文件中可以看到各种奖励项,例如:

 # 终止惩罚
termination_penalty = RewTerm(func=mdp.is_terminated, weight=-200.0) 
track_lin_vel_xy_exp = RewTerm(  # 跟踪线性速度奖励
    func=mdp.track_lin_vel_xy_yaw_frame_exp,
    weight=1.0,
    params={"command_name": "base_velocity", "std": 0.5},
)

其他文件:
- `curriculums.py` - 课程学习相关,用于逐渐增加训练难度
- `events.py` - 事件处理
- `observations.py` - 观察空间定义

3.3.1 rewards/objnav_rewards.py:机器人目标导航中的奖励设计

首先,代码首先定义了最基础的奖励信号:

  1. `is_alive` 函数为未终止的环境提供正向奖励,鼓励机器人继续运行
    # 定义is_alive函数,判断是否存活
    def is_alive(env: ManagerBasedRLEnv) -> torch.Tensor:  
        # 为存活提供奖励
        # 返回未终止环境的浮点张量,代表存活奖励
        return (~env.termination_manager.terminated).float()  
  2. `is_terminated` 函数对非超时导致的终止行为施加惩罚
    # 定义is_terminated函数,判断是否终止
    def is_terminated(env: ManagerBasedRLEnv) -> torch.Tensor:  
        # 对非超时导致的终止进行惩罚
        # 返回已终止环境的浮点张量,代表终止惩罚
        return env.termination_manager.terminated.float()
  3. `is_terminated_term` 类更加细致地处理不同原因的终止事件,允许选择性地惩罚特定终止条件

这种设计允许对机器人的存活时间进行精细控制,区分"正常结束"和"意外失败"两种情况

其次,代码中大量的奖励函数专注于维持机器人的稳定性和自然姿态:

  1. `lin_vel_z_l2` 和 `ang_vel_xy_l2` 惩罚不必要的垂直运动和倾斜旋转
  2. `flat_orientation_l2` 鼓励机器人保持水平姿态
    # 定义平面方向的L2惩罚函数
    def flat_orientation_l2(env: ManagerBasedRLEnv, asset_cfg: SceneEntityCfg = SceneEntityCfg("robot")) -> torch.Tensor:  
        # 使用L2核惩罚非平面基座方向
        # 这通过惩罚投影重力向量的XY分量来计算
    
        # 从环境场景中获取指定名称的资产对象
        asset: RigidObject = env.scene[asset_cfg.name]  
    
        # 返回投影重力向量XY分量的平方和
        return torch.sum(torch.square(asset.data.projected_gravity_b[:, :2]), dim=1)  
  3. `base_height_l2` 引导机器人维持适当的高度

这些奖励函数在向前移动的同时,抑制了机器人的额外摇摆和不稳定动作,使其行走更加自然流畅

接着,一系列关节相关的奖励函数控制着机器人的精细动作:

  1. `joint_torques_l2` 和 `power_penalty` 惩罚过大的关节力矩和能量消耗
    # 定义关节扭矩的L2惩罚函数
    def joint_torques_l2(env: ManagerBasedRLEnv, asset_cfg: SceneEntityCfg = SceneEntityCfg("robot")) -> torch.Tensor:  
        # 使用L2核惩罚施加在关节上的扭矩
        # 注意:只有在asset_cfg.joint_ids中配置的关节的扭矩才会计入L2范数
    
        # 从环境场景中获取指定名称的资产对象
        asset: Articulation = env.scene[asset_cfg.name]  
    
        # 返回指定关节ID上施加的扭矩平方和
        return torch.sum(torch.square(asset.data.applied_torque[:, asset_cfg.joint_ids]), dim=1)  
    
  2. `joint_vel_l1/l2` 和 `joint_acc_l2` 鼓励平滑的关节运动
  3. `joint_deviation_l1` 和 `joint_pos_limits` 引导关节保持在合理的活动范围内

这些函数共同作用,使机器人的动作更加自然、高效,避免了抽搐或过度疲劳的运动模式

再之后,代码的核心部分是导航相关的目标导向奖励:

  1. `track_lin_vel_xy_exp` 和 `track_ang_vel_z_exp` 奖励机器人精确跟踪速度命令
  2. `goal_distance` 根据机器人到目标的距离提供奖励
    def goal_distance(  # 定义目标距离奖励函数
    
        # 参数:环境、命令名称、资产配置
        env: ManagerBasedRLEnv, command_name: str, asset_cfg: SceneEntityCfg = SceneEntityCfg("robot")  
    ) -> torch.Tensor:
        # 使用指数核奖励线性速度命令跟踪(注:文档字符串与函数名不完全一致,这是目标距离)
    
        # 从环境场景中获取指定名称的资产对象
        asset: RigidObject = env.scene[asset_cfg.name]  
    
        # 计算误差
        # 计算目标位置的XY平面距离
        goal_track_error_pos = torch.norm(env.command_manager.get_command(command_name)[:,:2], dim=1)  
    
        # 当距离小于1时奖励为1,否则为指数衰减奖励
        reward = torch.where(goal_track_error_pos < 1.0, torch.tensor(1.0, device=env.device), torch.exp(-0.2*goal_track_error_pos))  
    
        # 返回奖励
        return reward
  3. `robot_goal_velocity_projection` 鼓励机器人朝向目标方向移动
  4. `goal_direction` 奖励机器人面向目标的行为

特别值得注意的是,这些函数使用了指数奖励机制,随着机器人接近目标,奖励呈非线性增长,这有效地解决了稀疏奖励问题

此外,代码还实现了一些针对特定行为的奖励函数:

  1. `feet_air_time` 和 `feet_air_time_positive_biped` 鼓励正确的步态模式
  2. `feet_stumble` 惩罚脚部绊倒的情况
  3. `stand_still_penalty` 在低速命令时鼓励机器人站定不动
  4. `stand_still_velocity_penalty` 在达到目标后惩罚继续移动的行为

3.3.2 curriculums.py:课程学习相关,用于逐渐增加训练难度

3.3.3 events.py:事件处理

3.3.4 observations.py:观察空间定义

第四部分 rsl_rl/:经典RL框架的封装(含我对PPO实现的解读)

这是一个强化学习算法框架,包含以下主要组件:

rsl_rl/
    config/ - 配置文件
    rsl_rl/ - 核心代码
        algorithms/ - 强化学习算法实现,如PPO
        env/ - 环境封装
        modules/ - 模型组件
            actor_critic.py - 基础Actor-Critic网络
            actor_critic_depth_cnn.py - 带深度视觉的Actor-Critic网络
            actor_critic_history.py - 带历史信息的Actor-Critic网络
            actor_critic_recurrent.py - 循环神经网络版Actor-Critic
            depth_backbone.py - 深度视觉处理网络
        runners/ - 训练运行器
        storage/ - 数据存储
        utils/ - 工具函数

这个模块定义了强化学习的核心组件,包括actor-critic网络架构(有普通版、CNN版、历史记忆版和RNN版)、PPO算法实现、训练运行器等

从代码中可以看出,它支持不同类型的输入数据(如关节状态、深度图像等)

本部分的内容 后已独立成文,详见《rsl_rl——人形运控部署框架汇总:从经典RL框架rsl_rl到宇树开源的unitree_rl_gym(含unitree_sdk2_python)

第五部分 scripts/

这个目录包含了用于训练、测试和演示的脚本:

scripts/
    cli_args.py - 命令行参数定义
    demo_matterport.py - Matterport环境演示脚本
    play_low_matterport_keyboard.py - 键盘控制机器人脚本
    play.py - 运行训练好的策略
    run_data_collection.py - 数据收集脚本
    train.py - 训练脚本
    utils.py - 工具函数

这些脚本提供了与项目交互的接口。从README中我们可以看到使用示例,如:

# 训练
python scripts/train.py --task=go2_base --history_len=9 --run_name=XXX --max_iterations=2000 --save_interval=200 --headless

# 测试
python scripts/play.py --task=go2_base_play --history_len=9 --load_run=RUN_NAME --num_envs=10

// 待更

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐