若需观看机器人系列相关博客,请劳驾至:【足式机器人无死角系列之-【强化学习基础-通用】、【仿真及训练环境】、【强化学习】:isaac-gym 与 isaac-lab 从零开始
郑重声明:该系列博客为本人 ( W e n h a i Z h u ) 独家私有 , 禁止转载与抄袭 , 首次举报有谢 , 若有需请私信授权! \color{red}郑重声明:该系列博客为本人(WenhaiZhu)独家私有,禁止转载与抄袭,首次举报有谢,若有需请私信授权! 郑重声明:该系列博客为本人(WenhaiZhu)独家私有,禁止转载与抄袭,首次举报有谢,若有需请私信授权!
本系列博客链接为: {\color{blue}本系列博客链接为:} 本系列博客链接为:【05.issac-lab】最新从零无死角系列-(00) 目录最新无死角源码讲解:https://blog.csdn.net/weixin_43013761/article/details/143084425
上一篇博客对包装器进行较为详细的概念介绍,且细致分析视屏录制包装器(gym.wrappers.RecordVideo)以及强化学库包装器(RslRlVecEnvWrapper)相关代码,得知 [境与强化学习库,或者强化学习算法是如何联系到一起的]。 √ ...... ......已分析 √ 2: 掌握训练过程的总体流程(以任务为导向)。 √ 3: 环境与强化学习库,或者强化学习算法是如何联系到一起的?。 4:文件配置是如何加载解析的,如搭建个人项目应该如何进行文件配置? --> 该篇博客 5: 如何训练自定义智能体,且使训练过收敛。 6: ......待定 注意:如果有提示大家,[大致过一遍即可,不用在意细节] 的地方,就不要钻牛角尖,快速阅读一下即可,后面会回过来百分百的进行详细分析。 |
本博客编写于: 20250111 ,台式机为 u b u n t u 20.04 , 3090 G e F o r c e R T X 显存 24 G { \color{purple} 本博客编写于:20250111,台式机为 ubuntu 20.04,3090 GeForce RTX 显存24G} 本博客编写于:20250111,台式机为ubuntu20.04,3090GeForceRTX显存24G:与你现在的代码,或者环境等存在一定差异也在情理之中,故切勿认为该系列博客绝对正确,且百密必有一疏,若发现错误处,恳请各位读者直接指出,本人会尽快进行整改,尽量使得后面的读者少踩坑,评论部分我会进行记录与感谢,只有这样,该系列博客才能成为精品,这里先拜谢各位朋友了。
文末正下方中心提供了本人 联系方式, 点击本人照片即可显示 W X → 官方认证,请备注 强化学习 。 {\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证,请备注\color{red} 强化学习}。 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX→官方认证,请备注强化学习。
一、前言
该篇博客主要对 isaac-lab 的参数解析与加载, Hydra 配置进行分析。其是该部分内容在前面的博客也有涉及,如 Hydra 于 【05.issa-lab】最新从零无死角系列-(03) isaac-lab之框架剖析,Omniverse、Isaac-sim 、Isaac-lab 对比,任务流程,Hydra 配置,强化学习库比较 中有简单介绍,不过只说明了使用方法,并没有介绍其具体实现的,不过没有关系,该篇博客会对相关源码进行分析。
前面博客训练的时候,执行了指令:
# 训练
python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 --headless --video --video_length 1000 --video_interval 5000
其中最重要的参数就是 Isaac-Velocity-Rough-Unitree-Go2-v0,若在源码中进行搜索,可以于 source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/go2/_init_.py 文件中看到类似如下代码:
gym.register(
id="Isaac-Velocity-Rough-Unitree-Go2-v0", # 任务名称
entry_point="isaaclab.envs:ManagerBasedRLEnv", # 环境实例类名
disable_env_checker=True, # 是否禁用环境检查器
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:UnitreeGo2RoughEnvCfg", # 环境类实例对应的配置文件
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:UnitreeGo2RoughPPORunnerCfg", # RSL-RL强化学习库算法配置文件
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml", # SKRL强化学习库算法配置文件
},
)
其表达含义也是比较好理解的,若是训练指令设置 --task 参数为 Isaac-Velocity-Rough-Unitree-Go2-v0,则会构建 isaaclab.envs:ManagerBasedRLEnv 实例,其具体使用那个配置文件构建该实例,还得看一下运行训练的指令,如下:
# rsl_rl, 则表示使用配置类 f"{agents.__name__}.rsl_rl_ppo_cfg:UnitreeGo2RoughPPORunnerCfg"
python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 --headless
# skrl,则表示使用配置类 f"{agents.__name__}:skrl_rough_ppo_cfg.yaml"
python scripts/reinforcement_learning/skrl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 --headless
那么这些配置文件在哪里呢?其实也比较好找,比如说使用 rsl_rl 进行训练,可在源码中搜 UnitreeGo2RoughPPORunnerCfg,易知该类实现于 source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/go2/agents/rsl_rl_ppo_cfg.py。其也就是使用:
python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0
指令进行训练对应的配置文件了,可以看到配置文件中主要实现一个类,类中包含了各种配置参数,关于配置参数作用与注释后面再进行分析。先来看看配置文件时如何加载的。
二、配置文件
通过上面的分析,可以知道配置文件中的类 UnitreeGo2RoughEnvCfg 会被环境类 ManagerBasedRLEnv 使用到,但是具体是如何使用的呢?或者说配置文件是被如何加载的?这个问题依旧得回到 scripts/reinforcement_learning/rsl_rl/train.py 这个训练启动脚本来分析。该文件首先调用的就是 main() 函数,这是一个比较常规的操作,不过该函数的实现比较特别,其实用到了一个装饰函数,代码如下:
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
显然这个装饰函数就是 @hydra_task_config(), 其接收两个参数,第一个参数为训练任务名称(id), 比如前面提到的 id=“Isaac-Velocity-Rough-Unitree-Go2-v0”,第二个参数是配置文件的入口函数名,这里就是 “rsl_rl_cfg_entry_point”: f"{agents.name}.rsl_rl_ppo_cfg:UnitreeGo2RoughPPORunnerCfg"。前面提到,若使用 rsl_rl 需要运行 scripts/reinforcement_learning/rsl_rl/train.py 脚本,其原因就在这里,该装饰函数第二参数,即配置类对应的 key 值其是固定的。下面就来看看 @hydra_task_config() 接收这两个参数之后做了些什么。
大致过一遍即可,不用在意细节: \color{blue}大致过一遍即可,不用在意细节: 大致过一遍即可,不用在意细节: 首先 @hydra_task_config() 实现于文件 source/isaaclab_tasks/isaaclab_tasks/utils/hydra.py 之中, 这里先给出代码注释:
def hydra_task_config(task_name: str, agent_cfg_entry_point: str) -> Callable:
"""Decorator to handle the Hydra configuration for a task, 这个装饰器用于处理任务的Hydra配置
This decorator registers the task to Hydra and updates the environment and agent configurations from Hydra parsed
command line arguments, 这个装饰器注册任务到Hydra, 并从Hydra解析的命令行参数更新环境配置和智能体配置
Args:
task_name: The name of the task, 任务的名称
agent_cfg_entry_point: The entry point key to resolve the agent's configuration file, 解析智能体配置文件的入口点
因为每个深度学习框架的配置文件格式不一样,所以需要通过入口点来解析,如 RSL-RL 入口设置为 rsl_rl_cfg_entry_point。
Returns:
The decorated function with the envrionment's and agent's configurations updated from command line arguments.
返回一个装饰器函数,该函数在调用时会注册任务到Hydra,并从Hydra解析的命令行参数更新环境配置和智能体配置。
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# STEP: 1.根据任务名称和智能体配置文件入口点,构建配置类实例,且把配置类实例注册到 Hydra 配置存储中
# register the task to Hydra,
env_cfg, agent_cfg = register_task_to_hydra(task_name, agent_cfg_entry_point)
# STEP: 2.通过 hydra.main 装饰器装饰 hydra_main 函数,该装饰器的作用是解析命令行参数,
# STEP: 并更新配置实例对象 env_cfg, agent_cfg 对应的属性
# define the new Hydra main function
@hydra.main(config_path=None, config_name=task_name, version_base="1.3")
# STEP: 3. 定义 hydra_main 函数,该函数会对 env_cfg, agent_cfg 配置进行一些处理
def hydra_main(hydra_env_cfg: DictConfig, env_cfg=env_cfg, agent_cfg=agent_cfg):
# STEP: 3.1 将递归将配置实例对象转换为原始的 Python 容器类型(字典或列表)。
# convert to a native dictionary
hydra_env_cfg = OmegaConf.to_container(hydra_env_cfg, resolve=True)
# STEP: 3.2将字符串中的切片对象替换为实际的切片对象,如 "some_key": "slice(0,5,1)" 替换为 slice(0,5,1)
# replace string with slices because OmegaConf does not support slices
hydra_env_cfg = replace_strings_with_slices(hydra_env_cfg)
# STEP: 3.3 将 Hydra 解析的命令行参数更新到 env_cfg
# update the configs with the Hydra command line arguments
env_cfg.from_dict(hydra_env_cfg["env"])
# STEP: 3.4 设置观测空间(env_cfg.observation_spaces)、动作空间(env_cfg.action_spaces) 反序构建实例化操作,
# STEP: 直白的说就是根据字符串反序列化构建实例对象,如
# STEP: env_cfg["action_spaces"]="type:gymnasium", space:Box, low:-1.0, high:1.0, "shape":3"
# STEP: 替换为
# STEP: env_cfg["action_spaces"] = Box(low=[-1.0], high=[1.0], shape=[3])
# STEP: 具体参考 source/extensions/omni.isaac.lab/omni/isaac/lab/envs/utils/spaces.py 中的 deserialize_space() 函数
# this must be done after converting the env configs from dictionary to avoid internal reinterpretations
env_cfg = replace_strings_with_env_cfg_spaces(env_cfg)
# STEP: 3.5 获取智能体配置参数,然后与 env_cfg 一起传递给 main 函数
# get agent configs
if isinstance(agent_cfg, dict) or agent_cfg is None:
agent_cfg = hydra_env_cfg["agent"]
else:
agent_cfg.from_dict(hydra_env_cfg["agent"])
# call the original function
func(env_cfg, agent_cfg, *args, **kwargs)
# call the new Hydra main function
hydra_main()
return wrapper
return decorator
关于装饰器的基本介绍这里就略过了,若有兴趣可以自行查阅相关资料。总的来说,main() 函数被 hydr_task_config() 函数装饰之后,其再调用 main() 函数之前会先调用装饰函数 hydr_task_config()。直白的说,就是增加了一个预处理操作,把处理好的数据传递给 main() 函数。不过比较特别的是,其并没有直接调用 main() 函数,而是定义了一个新的函数 hydra_main(),该函数的作用是解析命令行参数,后面再分析。由上源码可以知道 hydr_task_config() 函数第一步工作就是调用了:
def register_task_to_hydra(
task_name: str, agent_cfg_entry_point: str
) -> tuple[ManagerBasedRLEnvCfg | DirectRLEnvCfg, dict]:
env_cfg = load_cfg_from_registry(task_name, "env_cfg_entry_point") # 加载环境配置类实例
agent_cfg = None
if agent_cfg_entry_point:
agent_cfg = load_cfg_from_registry(task_name, agent_cfg_entry_point) # 加载智能体配置类实例
ConfigStore.instance().store(name=task_name, node=cfg_dict) # 注册配置类实例到 Hydra
函数 register_task_to_hydra 具体作用还是比较大的,其会首先根据 task_name 和 agent_cfg_entry_point 返回环境配置以及智能体配置的实例。前者环境配置主要与仿真(isaac-sim)相关,后者智能体配置主要是与强化学习库(比如RSLl_RL)相关。具体细节,本人这里就不展开了,有兴趣的可以自行查阅源码,总的来说,就是构建了 UnitreeGo2RoughEnvCfg 与 UnitreeGo2RoughPPORunnerCfg 这两个配置类实例。注意,这里这是本人的实例,随着训练脚本或任务的不同,这里的配置类实例会有所不同。
另外需要注意到 ConfigStore.instance().store(name=task_name, node=cfg_dict) 这句代码,其是把配置类实例注册到 Hydra 容器中存储,这里的 cfg_dict 包含了环境配置 env_cfg 和智能体配置 agent_cfg,这是比较重要的。
三、Hydra 配置
再讲解 Hydra 配置 之前,还是得回顾一下 Hydra 如何使用,比如说在训练的时候,可以通过如下指令指定训练的环境(智能体数量):
python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 env.scene.num_envs=32
其中通过 python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 env.scene.num_envs=32 修改训练时智能体数量,就是使用 Hydra 配置 的具体好处,当然其他的参数全部都是可以修改的,有兴趣的朋友可以自己尝试一下。
回到提到 ConfigStore.instance().store() 会把 env_cfg, agent_cfg 这两个配置类实例注册到 Hydra 容器中存储,接着回到前面的 decorator() 函数,其位于调用 register_task_to_hydra() 函数之后被调用。特别的一点是,该函数又被装饰器函数 @hydra.main() 装饰,其作用是解析命令行参数,并更新存储与 Hydra 容器中的环境配置 env_cfg 以及智能体配置 agent_cfg。剩余的代码有了比较详细的注释,这里就不再赘述了。注意的是:
env_cfg.from_dict(hydra_env_cfg["env"])
agent_cfg.from_dict(hydra_env_cfg["agent"])
这两句代码也是比较重要的,因为装饰器函数只会更新存储于 Hydra 中的配置,而不会更新 main() 函数中的 env_cfg 与 agent_cfg,所以需要通过这两句代码把 Hydra 解析的命令行参数更新到 外部的 env_cfg 与 agent_cfg 中,接着其执行了代码:
func(env_cfg, agent_cfg, *args, **kwargs)
这里的 func 就是需要被装饰的 main 函数,比如说再次回到代码 scripts/reinforcement_learning/rsl_rl/train.py 中,找到 main() 函数定义如下:
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
......
其接收 ManagerBasedRLEnvCfg 类型实例 env_cfg,以及 RslRlOnPolicyRunnerCfg 实例 agent_cfg,与前面讲解过程是意义对应的。
四、结语
通过该篇博客,从源码策层面分析了 isaac-lab 配置文件是如何被解析并加载的,且对 Hydra 配置进行了详解分析,该框架的引入使得训练时可以通过命名行覆盖任意配置文件参数,极大方便了训练者。总的来说,如下部分的分析已经完成。
√ 4:文件配置是如何加载解析的,如搭建个人项目应该如何进行文件配置? --> 该篇博客 |