NaVILA源码解析——从其VLA部分到其low-level部分:涵盖legged-loco、rsl_rl
前言
本文一开始是属于此文《NaVILA——可语音交互的用于四足和人形导航与避障的VLA模型:在VLM的导航规划下,执行基于视觉的运动策略》的,但因我司准备于25年7月底复现这个NaVILA库
使得其对我司的重要性大大提高了,故把原理 + 部署,与源码剖析的部分 各自独立开来
故成此文
第一部分 NaVILA部分的源码
// 待更
第二部分 NaVILA/legged-loco中isaaclab_exts/模块的解析:侧重H1人形机器人配置
整体代码库主要分为以下几个部分:
- isaaclab_exts - Isaac Lab的扩展,包含机器人配置和控制逻辑
- rsl_rl - 强化学习框架实现
- scripts - 训练、演示和使用的脚本
- 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的每个机器人子目录中都遵循一致的配置模式,主要包含两类配置文件:
- 基础配置 (`*_low_base_cfg.py`) - 包含基础运动控制,不使用视觉信息
- 视觉配置 (`*_low_vision_cfg.py`) - 在基础配置上增加了视觉感知能力
- 强化学习训练参数**,包括奖励函数、观察空间、域随机化策略等
2.1 Go1和GO2的机器人配置/omni/isaac/leggedloco/config/go1/
Go1是Unitree公司的四足机器人。它有两个主要配置文件:
2.1.1 go1_low_base_cfg.py
这个文件定义了Go1机器人的基础配置:
- 导入必要的依赖和基础类
- 定义`Go1RoughPPORunnerCfg`训练配置类
- 定义`UNITREE_GO1_CFG`机器人物理配置
- 定义`Go1_BASE_TERRAINS_CFG`地形生成配置
- 定义`Go1SceneCfg`场景配置类
- 定义`CustomGo1RewardsCfg`奖励函数配置
- 定义`ObservationsCfg`观察空间配置
- 定义`EventCfg`事件和域随机化配置
- 定义`Go1BaseRoughEnvCfg`完整环境配置类
- 定义`Go1BaseRoughEnvCfg_PLAY`用于演示的环境配置类
2.1.2 go1_low_vision_cfg.py
在基础配置的基础上,增加了视觉能力:
- 导入基础配置
- 定义`Go1VisionRoughPPORunnerCfg`支持视觉的训练配置
- 扩展`Go1SceneCfg`,添加深度传感器等视觉组件
- 配置视觉观察空间
- 定义`Go1VisionRoughEnvCfg`完整的视觉环境配置
- 定义`Go1VisionRoughEnvCfg_PLAY`用于演示的视觉环境配置
2.2 Go2 机器人配置 (/omni/isaac/leggedloco/config/go2/)
Go2是Unitree公司的另一种四足机器人,配置结构类似于Go1:
2.2.1 go2_low_base_cfg.py
- 定义`Go2RoughPPORunnerCfg`训练配置
- 定义`UNITREE_GO2_CFG`机器人物理配置
- 定义`Go2_BASE_TERRAINS_CFG`地形配置
- 定义`Go2SceneCfg`场景配置
- 定义`CustomGo2RewardsCfg`奖励函数
- 定义观察空间、事件和完整环境配置
2.2.2 go2_low_vision_cfg.py
- 继承基础配置
- 定义`Go2VisionRoughPPORunnerCfg`
- 定义`Go2_Vision_TERRAINS_CFG`视觉地形配置
- 扩展场景配置,添加视觉传感器
- 定义`Go2VisionRoughEnvCfg`视觉环境配置
2.3 G1 人形机器人配置 (/omni/isaac/leggedloco/config/g1/)
G1是一种人形机器人,配置结构与四足机器人类似:
2.3.1 g1_low_base_cfg.py
- 定义`G1RoughPPORunnerCfg`训练配置
- 定义`ROUGH_TERRAINS_CFG`地形配置
- 定义`G1SceneCfg`场景配置
- 定义`RewardsCfg2DoF`针对2自由度关节的奖励
- 定义`CustomG1Rewards`自定义奖励
- 定义`EventCfg`事件和域随机化
- 定义`G1BaseRoughEnvCfg`环境配置
2.3.2 g1_low_vision_cfg.py
- 导入基础配置
- 定义`G1VisionRoughPPORunnerCfg`视觉训练配置
- 定义`G1_NO_ARMS_CFG`无臂版G1机器人配置
- 定义视觉地形和场景配置
- 定义`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 导入部分
这部分导入了
- Isaac Lab的各种模拟工具、资产配置、管理器
- 传感器、地形生成器和噪音模型等组件
- 自定义的MDP(马尔可夫决策过程)组件
- 预定义的H1机器人模型配置(`H1_MINIMAL_CFG`)
2.4.1.2 PPO 训练配置
首先是 `H1RoughPPORunnerCfg` 类,配置了 PPO(近端策略优化)训练算法的参数:
- 每个环境的步数设为 32
- 最大迭代次数 50000,每 500 次迭代保存一次模型
- 使用三层神经网络架构作为 Actor 和 Critic,尺寸分别为 [512, 256, 128]
- 激活函数采用 ELU,初始噪声标准差为 1.0
- PPO 超参数配置:学习率 0.001,衰减因子 γ=0.99,λ=0.95,以及熵系数 0.005
2.4.1.3 地形配置
`BASE_TERRAIN_CFG` 定义了两种地形类型的混合
- 50% 的平坦地形(flat)
- 50% 的随机粗糙地形(random_rough),噪声范围为 0.02 到 0.10
2.4.1.4 场景配置
`BaseSceneCfg` 描述了仿真环境中的各种元素:
- 地形配置,包含物理材质(摩擦系数为 1.0)和视觉材质
- 机器人模型及其位置
- 接触力传感器,用于检测机器人与地面的接触
- 高度扫描器,使用射线投射技术检测地形高度
- 光照设置
2.4.1.5 观察空间配置
`ObservationsCfg` 定义了三组观察空间:
- `PolicyCfg`:策略网络的输入,包含基本动作信息和噪声,如:
- 线速度、角速度、重力投影
- 速度指令
- 关节位置、速度
- 上一步动作 - `ProprioCfg`:类似于 PolicyCfg,但噪声参数有所不同
- CriticObsCfg`:评论家网络的输入,与策略网络类似,但增加了地形高度扫描,没有噪声干扰
2.4.1.6 动作空间与事件配置
- `ActionsCfg` 定义了动作空间,使用关节位置控制
- `EventCfg` 配置了训练过程中的各种随机化事件,包括:
- 物理材质随机化
- 重置机器人位置、姿态和关节状态等
2.4.1.7 奖励与指令配置
- `CustomH1Rewards` 在基础奖励上增加了对脚部绊倒的惩罚
- `CommandsCfg` 定义了速度指令的范围和重采样时间
- `TerminationsCfg` 设置了终止条件,如时间结束或机器人躯干接触地面
2.4.1.8 主环境配置
`H1BaseRoughEnvCfg` 整合了上述所有配置,并在 `__post_init__` 中进行了额外设置:
- 仿真步长为 0.005 秒,每集长度 20 秒
- 优化了摩擦和弹性参数
- 调整了奖励权重,例如平坦姿态奖励权重为 -5.0
2.4.1.9 游戏测试配置
`H1BaseRoughEnvCfg_PLAY` 继承自主环境配置,但做了以下调整:
- 减少环境数量从 4096 到 40,以减轻计算负担
- 延长每集时间到 40 秒
- 固定机器人速度指令(前向速度固定为 0.5)
- 禁用了观察噪声和随机推力
这个配置主要用于在训练后对模型进行评估和演示
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`定义了七种不同类型的地形,每种占据不同比例:
- 金字塔阶梯(20%):阶梯高度在0.05到0.3米之间
- 倒金字塔阶梯(20%):与上面相反的阶梯结构
- 随机箱体地形(20%):随机分布的箱形障碍物
- 随机粗糙地形(20%):带有0.02-0.10米噪声的不平整表面
- 金字塔斜坡(10%):斜率在0-0.4之间的斜坡地形
- 倒金字塔斜坡(10%):与上面相反的斜坡结构
- 离散障碍物(20%):固定高度为1.5米的障碍物
同时,为每种地形添加了平坦区域采样点,方便机器人学习从平地过渡到复杂地形。
2.4.2.3 场景与传感器配置
`TrainSceneCfg`类配置了训练场景,包括:
- 地形导入器,使用上述复杂地形生成器
class TrainSceneCfg(InteractiveSceneCfg): # 定义训练场景配置,继承自交互场景配置"""带有腿式机器人的地形场景配置"""# 地面地形terrain = TerrainImporterCfg( # 地形导入器配置prim_path="/World/ground", # 地形基元路径terrain_type="generator", # 使用生成器类型terrain_generator=ROUGH_TERRAINS_CFG, # 使用上面定义的粗糙地形生成器max_init_terrain_level=5, # 可以尝试设为9collision_group=-1, # 碰撞组设为-1physics_material=sim_utils.RigidBodyMaterialCfg( # 物理材质配置friction_combine_mode="multiply", # 摩擦力组合模式为乘法restitution_combine_mode="multiply", # 弹性组合模式为乘法static_friction=1.0, # 静摩擦系数1.0dynamic_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, )
- 机器人模型配置
# 机器人# 使用H1最小配置,设置基元路径robot = H1_MINIMAL_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
- 接触力传感器,用于检测机器人与地面的接触
# 传感器# 接触力传感器配置contact_forces = ContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, track_air_time=True, debug_vis=False)
- 高度扫描器,使用射线投射技术检测前方地形高度
# 高度扫描器配置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.0pattern_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
- 天空光照效果
# 灯光# 天空光配置sky_light = AssetBaseCfg( # 基元路径prim_path="/World/skyLight",# 使用半球光 spawn=sim_utils.DomeLightCfg( # 光强度750.0intensity=750.0, # 天空纹理文件texture_file=f"{ISAAC_NUCLEUS_DIR}/Materials/Textures/Skies/PolyHaven/kloofendal_43d_clear_puresky_4k.hdr", ),)
值得注意的是,代码中保留了深度传感器和激光雷达的接口,但当前设置为`None`,这表明未来可能会整合这些传感器。
2.4.2.4 观察空间配置
`ObservationsCfg`类定义了三种观察类型:
- `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),
- `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
- `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`设定了:
- 4096个并行环境用于训练
- 每集20秒的仿真时间
- 物理参数如摩擦系数、弹性系数等
- 奖励权重调整,如平坦姿态奖励权重为-1.0
- 命令范围:前向速度在0到1之间,角速度在-1到1之间
`H1VisionRoughEnvCfg_PLAY`配置专为测试设计:
- 只使用40个环境
- 延长每集时间到40秒
- 减少地形复杂度以节省内存
- 固定前向速度为0.5,取消转向
- 禁用观察噪声和随机外力
整体看来,这个配置与基础配置相比,最大的不同在于添加了更为多样化的复杂地形以及地形感知能力,使H1机器人能够通过高度扫描获取前方地形信息,从而学习如何应对各种复杂地形的导航策略
第三部分 核心MDP组件omni/isaac/leggedloco/leggedloco/mdp
mdp目录包含了马尔可夫决策过程(MDP)的关键组件,这是强化学习环境的核心部分
3.1 actions/
这个目录包含定义机器人动作空间的类,例如:
- `navigation_actions.py` - 导航动作
- `vlm_navigation_actions_gpt.py` - 基于GPT的视觉-语言模型导航动作
- `vlm_navigation_actions.py` - 视觉-语言模型导航动作
3.1.1 navigation_actions.py:高层导航、底层控制
这段代码实现了一个分层控制系统,用于机器人导航任务。`NavigationAction` 类是一个高层控制器,负责将导航指令转换为低层运动控制动作,形成了一个典型的分层机器人控制架构
- 高层导航控制:接收导航指令(如前进、转向等),频率为10Hz
- 低层运动控制:执行实际的关节控制,频率为50Hz「由于物理引擎运行在200Hz,因此采用了4:1的抽取比例」
这种分层架构允许导航策略专注于路径规划和障碍物避开,而将复杂的运动动力学控制委托给预训练的低层策略
首先,初始化过程中,系统加载一个预训练的低层控制策略:
- 首先检查配置中指定的策略文件是否存在
- 使用`torch.jit.load`加载策略模型,并将其冻结为评估模式
- 准备所需的动作缓冲区,包括原始导航指令、处理后的导航指令和低层动作
低层策略接收来自环境的观察("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)
其次,系统运行时遵循以下流程:
- `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)
- `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` 类定义了控制器的配置参数,包括:
- 低层抽取系数(默认为4,使低层控制频率为50Hz)
- 低层策略文件路径
- 低层动作配置
- 路径长度(51个点)
- 图像尺寸(暂未使用,代码中有相关注释掉的深度图像处理部分)
值得注意的是,代码中保留了一些已注释的图像处理功能,包括深度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的物理更新中获得),负责实际的关节控制
这种分层设计思路将复杂的导航问题分解为"决策"和"执行"两个相对独立的子问题,使系统更容易开发和维护。高层专注于"往哪里去"的问题,而低层专注于"如何到达"的问题
首先,系统在初始化阶段执行以下关键步骤:
- 验证低层策略文件是否存在,若不存在则抛出`FileNotFoundError`异常
# 初始化图像计数器为0self.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)
- 使用`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())
- 初始化低层动作执行器,用于将策略输出转换为物理控制指令
# 准备关节位置动作# 创建低层动作项实例self.low_level_action_term: ActionTerm = self.cfg.low_level_action.class_type(cfg.low_level_action, env)
- 创建三种动作缓冲区:原始导航动作、处理后的命令速度动作和低层控制动作
# 准备缓冲区# 设置动作维度为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)
值得注意的是,系统预留了图像处理相关的代码(尽管被注释掉了),包括深度图像处理和图像大小调整,这表明该系统可能计划或曾经支持视觉输入处理
其次,动作处理分为两个主要阶段:
- 高层动作处理(`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)
- 低层动作执行(`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() # 计数器加1self._counter += 1
整个过程形成了一个闭环:高层导航指令→低层运动策略→关节控制命令→物理执行→环境反馈→低层运动策略...
最后,`VLMActionsCfg`类定义了系统的配置参数:
- 低层控制频率抽取系数(默认为4)
- 低层动作配置(由使用者提供)
- 低层策略文件路径(必须提供)
系统设计允许通过配置文件调整这些参数,而无需修改代码,增强了系统的灵活性。值得注意的是,配置中还保留了一些被注释掉的参数(如路径长度和图像尺寸),这可能是未来功能的占位符
3.2 commands/
命令生成器相关文件,例如:
- `goal_command_generator_cfg.py` - 目标命令生成器配置
- `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:机器人目标导航中的奖励设计
首先,代码首先定义了最基础的奖励信号:
- `is_alive` 函数为未终止的环境提供正向奖励,鼓励机器人继续运行
# 定义is_alive函数,判断是否存活 def is_alive(env: ManagerBasedRLEnv) -> torch.Tensor: # 为存活提供奖励# 返回未终止环境的浮点张量,代表存活奖励return (~env.termination_manager.terminated).float()
- `is_terminated` 函数对非超时导致的终止行为施加惩罚
# 定义is_terminated函数,判断是否终止 def is_terminated(env: ManagerBasedRLEnv) -> torch.Tensor: # 对非超时导致的终止进行惩罚# 返回已终止环境的浮点张量,代表终止惩罚return env.termination_manager.terminated.float()
- `is_terminated_term` 类更加细致地处理不同原因的终止事件,允许选择性地惩罚特定终止条件
这种设计允许对机器人的存活时间进行精细控制,区分"正常结束"和"意外失败"两种情况
其次,代码中大量的奖励函数专注于维持机器人的稳定性和自然姿态:
- `lin_vel_z_l2` 和 `ang_vel_xy_l2` 惩罚不必要的垂直运动和倾斜旋转
- `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)
- `base_height_l2` 引导机器人维持适当的高度
这些奖励函数在向前移动的同时,抑制了机器人的额外摇摆和不稳定动作,使其行走更加自然流畅
接着,一系列关节相关的奖励函数控制着机器人的精细动作:
- `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)
- `joint_vel_l1/l2` 和 `joint_acc_l2` 鼓励平滑的关节运动
- `joint_deviation_l1` 和 `joint_pos_limits` 引导关节保持在合理的活动范围内
这些函数共同作用,使机器人的动作更加自然、高效,避免了抽搐或过度疲劳的运动模式
再之后,代码的核心部分是导航相关的目标导向奖励:
- `track_lin_vel_xy_exp` 和 `track_ang_vel_z_exp` 奖励机器人精确跟踪速度命令
- `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
- `robot_goal_velocity_projection` 鼓励机器人朝向目标方向移动
- `goal_direction` 奖励机器人面向目标的行为
特别值得注意的是,这些函数使用了指数奖励机制,随着机器人接近目标,奖励呈非线性增长,这有效地解决了稀疏奖励问题
此外,代码还实现了一些针对特定行为的奖励函数:
- `feet_air_time` 和 `feet_air_time_positive_biped` 鼓励正确的步态模式
- `feet_stumble` 惩罚脚部绊倒的情况
- `stand_still_penalty` 在低速命令时鼓励机器人站定不动
- `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/ - 强化学习算法实现,如PPOenv/ - 环境封装modules/ - 模型组件actor_critic.py - 基础Actor-Critic网络actor_critic_depth_cnn.py - 带深度视觉的Actor-Critic网络actor_critic_history.py - 带历史信息的Actor-Critic网络actor_critic_recurrent.py - 循环神经网络版Actor-Criticdepth_backbone.py - 深度视觉处理网络runners/ - 训练运行器storage/ - 数据存储utils/ - 工具函数
这个模块定义了强化学习的核心组件,包括actor-critic网络架构(有普通版、CNN版、历史记忆版和RNN版)、PPO算法实现、训练运行器等
从代码中可以看出,它支持不同类型的输入数据(如关节状态、深度图像等)
4.1 rsl_rl/algorithms/ppo.py:近端策略优化PPO的实现
这段代码定义了一个名为 `PPO` 的类,它实现了近端策略优化(Proximal Policy Optimization)算法,其详细介绍详见此文的《强化学习极简入门:通俗理解MDP、DP MC TD和Q学习、策略梯度、PPO》第4.4节
4.1.1 初始化__init__
- 构造函数接收一个
`actor_critic` 网络(策略网络和价值网络的组合)
学习超参数(如学习周期 `num_learning_epochs`、小批量数量 `num_mini_batches`、裁剪参数 `clip_param`、折扣因子 `gamma`、GAE lambda `lam` 等)
以及优化器设置(学习率 `learning_rate`、梯度裁剪范数 `max_grad_norm`) - 它初始化 PPO 的核心组件:
`actor_critic`: 传入的神经网络模型,并将其移动到指定的设备(如 CPU 或 GPU)
`storage`: 用于存储经验轨迹(transitions)的 `RolloutStorage` 对象,稍后初始化
`optimizer`: 使用 Adam 优化器来更新 `actor_critic` 网络的参数
`transition`: 一个临时的 `RolloutStorage.Transition` 对象,用于在每个环境步骤中收集数据 - 存储 PPO 算法的关键超参数。
- 设置学习率调度策略(`schedule`)和目标 KL 散度(`desired_kl`),用于自适应学习率调整
4.1.2 存储初始化init_storage
- 这个方法根据环境的数量、每个环境收集的转换(transitions)数量、观察空间形状和动作空间形状来创建 `RolloutStorage` 实例
- `RolloutStorage` 负责存储智能体与环境交互产生的数据序列
4.1.3 模式切换test_mode/train_mode
这些方法用于切换 `actor_critic` 网络到评估(测试)模式或训练模式。这对于包含 Dropout 或 Batch Normalization 层的网络很重要
4.1.4 动作选择act
这是智能体与环境交互的核心。给定当前观察 `obs`(用于策略网络)和 `critic_obs`(用于价值网络,可能与 `obs` 相同或包含额外信息),它执行以下操作:
- 如果网络是循环的(RNN/LSTM),获取并存储隐藏状态。
- 使用 `actor_critic` 网络
计算动作 (`actions`)
状态价值 (`values`)# 使用 Actor-Critic 网络计算动作,并分离计算图self.transition.actions = self.actor_critic.act(obs).detach()
和动作的对数概率 (`actions_log_prob`)# 使用 Actor-Critic 网络评估状态价值,并分离计算图self.transition.values = self.actor_critic.evaluate(critic_obs).detach()
# 获取动作的对数概率,并分离计算图self.transition.actions_log_prob = self.actor_critic.get_actions_log_prob(self.transition.actions).detach()
- 存储动作分布的均值 (`action_mean`) 和标准差 (`action_sigma`)
- 将当前的观察、评论家观察以及计算出的所有信息存储在临时的 `transition` 对象中
- 返回计算出的动作,供环境执行
4.1.5 处理环境步骤process_env_step
在环境执行动作后调用此方法
- 接收奖励 (`rewards`)、完成标志 (`dones`) 和额外信息 (`infos`)
- 将这些信息存储到 `transition` 对象中
重要: 它实现了"超时引导 (Bootstrapping on time outs)"。如果一个 episode 因为达到时间限制而不是因为失败状态而结束(通过 `infos["time_outs"]` 判断),它会将最后一步的估计价值(乘以 `gamma`)加到奖励中
这可以防止智能体因为时间限制而受到不公平的惩罚,并提供更准确的回报估计 - 将完整的 `transition` 添加到 `storage` 中
- 清空 `transition` 对象,为下一步做准备
- 如果网络是循环的,根据 `dones` 信号重置其隐藏状态
4.1.6 计算回报compute_returns
- 在收集了足够多的 transitions 后调用
- 首先,使用 `actor_critic` 网络评估最后一个状态的价值 (`last_values`)
# 计算回报和优势的方法def compute_returns(self, last_critic_obs): # 评估最后一个状态的价值last_values = self.actor_critic.evaluate(last_critic_obs).detach()
- 然后,调用 `storage.compute_returns` 方法
这个方法通常使用广义优势估计 (Generalized Advantage Estimation, GAE)来计算每个时间步的回报 (`returns`) 和优势 (`advantages`)# 调用存储器的 compute_returns 方法计算回报和 GAE 优势self.storage.compute_returns(last_values, self.gamma, self.lam)
GAE 结合了不同时间步长的价值估计,以减少估计的方差
4.1.7 更新update
这是执行策略和价值网络参数更新的核心循环
# 更新策略和价值网络参数的方法def update(self): # 初始化平均价值损失mean_value_loss = 0 # 初始化平均代理损失mean_surrogate_loss = 0
- 它首先根据网络是否是循环的,选择合适的小批量生成器(`mini_batch_generator` 或 `reccurent_mini_batch_generator`) 从 `storage` 中采样数据
# 检查 Actor-Critic 网络是否是循环网络if self.actor_critic.is_recurrent: # 如果是循环网络,使用循环小批量生成器generator = self.storage.reccurent_mini_batch_generator(self.num_mini_batches, self.num_learning_epochs) # 如果不是循环网络else: # 使用标准小批量生成器generator = self.storage.mini_batch_generator(self.num_mini_batches, self.num_learning_epochs)
- 在多个学习周期 (epochs) 内迭代:
# 遍历小批量生成器产生的数据for ( obs_batch, # 观察值小批量critic_obs_batch, # 评论家观察值小批量actions_batch, # 动作小批量target_values_batch, # 目标价值小批量 (用于价值损失计算,通常是回报)advantages_batch, # 优势小批量returns_batch, # 回报小批量 (用于价值损失计算)old_actions_log_prob_batch, # 旧策略下的动作对数概率小批量old_mu_batch, # 旧策略下的动作均值小批量old_sigma_batch, # 旧策略下的动作标准差小批量hid_states_batch, # 隐藏状态小批量 (仅用于循环网络)masks_batch, # 掩码小批量 (仅用于循环网络)) in generator:
对于每个小批量 (mini-batch)
- 重新计算当前策略下的小批量数据的动作对数概率、价值和熵
# 使用当前策略重新评估动作 (主要为了获取内部状态如均值、标准差)self.actor_critic.act(obs_batch, masks=masks_batch, hidden_states=hid_states_batch[0]) # 获取当前策略下动作的对数概率actions_log_prob_batch = self.actor_critic.get_actions_log_prob(actions_batch) # 使用当前策略评估状态价值value_batch = self.actor_critic.evaluate( # 传入评论家观察、掩码和隐藏状态critic_obs_batch, masks=masks_batch, hidden_states=hid_states_batch[1] )# 获取当前策略的动作均值mu_batch = self.actor_critic.action_mean # 获取当前策略的动作标准差sigma_batch = self.actor_critic.action_std # 获取当前策略的熵entropy_batch = self.actor_critic.entropy
- KL 散度与自适应学习率:如果启用了自适应学习率 (`schedule == "adaptive"`),计算当前策略和旧策略(生成数据时的策略)之间的 KL 散度「 详见此文的《强化学习极简入门:通俗理解MDP、DP MC TD和Q学习、策略梯度、PPO》第4.4节」
上面这个KL公式稍微有点小复杂,但为方便大家更好的理解 更为大家看着舒服、省心,我还是把上面这段代码对应的公式 写一下,且把代码的每一行 与公式当中的各个项,逐一对应说明下# KL 散度计算 (用于自适应学习率)# 如果设置了期望 KL 且调度策略是自适应的if self.desired_kl is not None and self.schedule == "adaptive": # 在无梯度计算模式下进行with torch.inference_mode(): # 计算当前策略和旧策略之间的 KL 散度kl = torch.sum( # 对数标准差比项 (+1e-5 防止除零)torch.log(sigma_batch / old_sigma_batch + 1.0e-5) # 旧标准差平方 + 均值差平方项+ (torch.square(old_sigma_batch) + torch.square(old_mu_batch - mu_batch)) # 除以 2 倍当前标准差平方/ (2.0 * torch.square(sigma_batch)) # 减去 0.5- 0.5, # 沿着最后一个维度求和axis=-1, )
假设当前策略的均值和标准差分别为 μ 和 σ,旧策略的均值和标准差分别为和
,则 KL 散度的公式为
首先,其中这行代码对应为# 对数标准差比项 (+1e-5 防止除零)torch.log(sigma_batch / old_sigma_batch + 1.0e-5)
其次,接下来的这行代码对应于# 旧标准差平方 + 均值差平方项+ (torch.square(old_sigma_batch) + torch.square(old_mu_batch - mu_batch))
然后其中的「除以 2 倍当前标准差平方」的代码:对应于# 除以 2 倍当前标准差平方/ (2.0 * torch.square(sigma_batch))
接下来,减去0.5的代码 对应为
最后,沿着最后一个维度求和的代码 对应为
![]()
哦了,公式解释好了,那接下来,根据 KL 散度与 `desired_kl` 的比较,动态调整优化器的学习率# 计算 KL 散度的平均值kl_mean = torch.mean(kl) # 如果 KL 散度远大于期望值if kl_mean > self.desired_kl * 2.0: # 降低学习率 (最小为 1e-5)self.learning_rate = max(1e-5, self.learning_rate / 1.5) # 如果 KL 散度远小于期望值 (且大于 0)elif kl_mean < self.desired_kl / 2.0 and kl_mean > 0.0: # 提高学习率 (最大为 1e-2) self.learning_rate = min(1e-2, self.learning_rate * 1.5)
- 代理损失 (Surrogate Loss): 计算 PPO 的核心损失项。它使用重要性采样比率 (`ratio`) 和优势 (`advantages_batch`),并应用**裁剪 (clipping) 来限制策略更新的幅度,防止策略变化过大
`surrogate_loss` 是裁剪后损失和未裁剪损失中的较大者# 代理损失计算 (PPO 核心目标)# 计算重要性采样比率 (当前概率 / 旧概率)ratio = torch.exp(actions_log_prob_batch - torch.squeeze(old_actions_log_prob_batch)) # 计算未裁剪的代理损失项surrogate = -torch.squeeze(advantages_batch) * ratio # 计算裁剪后的代理损失项surrogate_clipped = -torch.squeeze(advantages_batch) * torch.clamp( # 且将比率裁剪到 [1-clip, 1+clip] 范围内ratio, 1.0 - self.clip_param, 1.0 + self.clip_param )# 取未裁剪和裁剪后损失中的较大者,并计算平均值surrogate_loss = torch.max(surrogate, surrogate_clipped).mean()
- 价值函数损失 (Value Function Loss):计算价值网络的损失
根据此文《ChatGPT技术原理解析:从RL之PPO算法、RLHF到GPT4、instructGPT》的「3.3.2 GAE之下的优势函数计算、回报序列计算、价值序列的迭代」,有
可以选择使用裁剪的价值损失 (clipped value loss),这类似于策略损失的裁剪,有助于稳定训练# 价值函数损失计算# 如果使用裁剪的价值损失if self.use_clipped_value_loss: # 计算裁剪后的价值预测value_clipped = target_values_batch + (value_batch - target_values_batch).clamp( # 将价值预测与目标价值的差值裁剪到 [-clip, clip]-self.clip_param, self.clip_param )# 计算未裁剪的价值损失 (均方误差)value_losses = (value_batch - returns_batch).pow(2) # 计算裁剪后的价值损失 (均方误差)value_losses_clipped = (value_clipped - returns_batch).pow(2) # 取未裁剪和裁剪后损失中的较大者,并计算平均值value_loss = torch.max(value_losses, value_losses_clipped).mean() # 如果不使用裁剪的价值损失else: # 直接计算价值损失 (均方误差)value_loss = (returns_batch - value_batch).pow(2).mean()
- 总损失: 将代理损失、价值损失(乘以系数 `value_loss_coef`)和熵奖励(乘以系数 `entropy_coef`,鼓励探索)结合起来
# 计算总损失 = 代理损失 + 价值损失 - 熵奖励loss = surrogate_loss + self.value_loss_coef * value_loss - self.entropy_coef * entropy_batch.mean()
- 梯度更新: 计算总损失的梯度,执行梯度裁剪 (`clip_grad_norm_`) 防止梯度爆炸,然后让优化器执行一步更新
# 梯度更新步骤# 清空优化器的梯度self.optimizer.zero_grad() # 反向传播计算梯度loss.backward() # 对梯度进行裁剪,防止梯度爆炸nn.utils.clip_grad_norm_(self.actor_critic.parameters(), self.max_grad_norm) # 执行一步优化器更新self.optimizer.step() # 累加当前小批量的价值损失 (转换为 Python float)mean_value_loss += value_loss.item() # 累加当前小批量的代理损失 (转换为 Python float)mean_surrogate_loss += surrogate_loss.item()
- 累积并计算平均的价值损失和代理损失,用于监控训练过程
# 计算总的更新次数num_updates = self.num_learning_epochs * self.num_mini_batches # 计算整个 update 过程中的平均价值损失mean_value_loss /= num_updates # 计算整个 update 过程中的平均代理损失mean_surrogate_loss /= num_updates
- 清空 `storage`,为下一轮数据收集做准备
# 清空经验存储器,为下一轮数据收集做准备self.storage.clear()
- 返回平均损失值
# 返回平均价值损失和平均代理损失return mean_value_loss, mean_surrogate_loss
4.2 rsl_rl/env
4.3 rsl_rl/modules
4.3.1 modules/actor_critic_depth_cnn.py
4.3.2 modules/actor_critic_history.py
4.3.3 modules/actor_critic_recurrent.py
4.3.4 modules/actor_critic.py
4.3.5 modules/depth_backbone.py
4.3.6 modules/normalizer.py
4.4 rsl_rl/runners
// 待更
第五部分 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
// 待更