【05.isaac-lab】最新从零无死角系列-(05) isaac-lab之框架剖析:强化学习库包装,视屏录制包装-记录保存训练视屏

若需观看机器人系列相关博客,请劳驾至:【足式机器人无死角系列之-【强化学习基础-通用】、【仿真及训练环境】、【强化学习】:isaac-gym 与 isaac-lab 从零开始

郑重声明:该系列博客为本人 ( W e n h a i Z h u ) 独家私有 , 禁止转载与抄袭 , 首次举报有谢 , 若有需请私信授权! \color{red}郑重声明:该系列博客为本人(WenhaiZhu)独家私有,禁止转载与抄袭,首次举报有谢,若有需请私信授权! 郑重声明:该系列博客为本人(WenhaiZhu)独家私有,禁止转载与抄袭,首次举报有谢,若有需请私信授权!

本系列博客链接为: {\color{blue}本系列博客链接为:} 本系列博客链接为:【05.isaac-lab】最新从零无死角系列-(00) 目录最新无死角源码讲解:https://blog.youkuaiyun.com/weixin_43013761/article/details/143084425

上一篇博客,了解 Isaac-sim 设计理念与基本结构。其中包含任务设计工作流程、强化学习库比较、Hydra 配置系统。目前 isaac-lab 支持的强化学习库有 SKRL、 rsl_rl、 rl_games、 Stable-Baselines3。后续过程中,首先以 rsl_rl 库为主,进行学习与讲解,因为本人方向主要为足式机器人,且对该库比较熟悉。不过也会进行拓展,对其他强化学习库也进行讲解与剖析。

思来想去,本人最终于还是决定从主体框架入手进行讲解,只有知道整体流程是如何的,在后续分析细节时才能不迷惘,所以顺着以下思路对代码进行剖析:
         1: 了解 Isaac-sim 设计理念与基本结构。
         2: 掌握训练过程的总体流程(以任务为导向)。
        3: 环境与强化学习库,或者强化学习算法是如何联系到一起的 --> 该篇博客。
        4: ......待定

注意:如果有提示大家,[大致过一遍即可,不用在意细节] 的地方,就不要钻牛角尖,快速阅读一下即可,后面会回过来百分百的进行详细分析。

本博客编写于: 20250105 ,台式机为 u b u n t u 20.04 , 3090 G e F o r c e R T X 显存 24 G { \color{purple} 本博客编写于:20250105,台式机为 ubuntu 20.04,3090 GeForce RTX 显存24G} 本博客编写于:20250105,台式机为ubuntu20.043090GeForceRTX显存24G:与你现在的代码,或者环境等存在一定差异也在情理之中,故切勿认为该系列博客绝对正确,且百密必有一疏,若发现错误处,恳请各位读者直接指出,本人会尽快进行整改,尽量使得后面的读者少踩坑,评论部分我会进行记录与感谢,只有这样,该系列博客才能成为精品,这里先拜谢各位朋友了。

文末正下方中心提供了本人 联系方式, 点击本人照片即可显示 W X → 官方认证,请备注 强化学习 。 {\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证,请备注\color{red} 强化学习}。 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX官方认证,请备注强化学习

一、前言

正式分析代码之前,先来说一个实用的功能,那就是训练过程中可以通过 “–video”、“–video_length” 等相关参数进行可视化界面的录制(使用无头训练,即附加 “–headless” 指令,也可录制),比如说本人训练 go2 机器狗执行如下指令:

# 训练
python scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Unitree-Go2-v0 --headless  --video --video_length 1000 --video_interval 5000
"--video" : 附加该参数,则表示启用视屏录制
"--video_length": 每个视屏录制的 step 总数(与环境交互的总次数)
"--video_interval":间隔多少个 step 数进行一次视屏录制

然后等待其训练一段时间之后,IsaacLab 源码目录下可以看到如下结构

logs/
└── rsl_rl
    └── unitree_go2_rough
        └── 2025-01-11_14-47-31
            ├── events.out.tfevents.1736578071.ub20-04.5698.0
            ├── git
            │   └── IsaacLab.diff
            ├── model_0.pt
            ├── params
            │   ├── agent.pkl
            │   ├── agent.yaml
            │   ├── env.pkl
            │   └── env.yaml
            └── videos
                └── train
                    ├── rl-video-step-0.meta.json
                    └── rl-video-step-0.mp4

其中 logs/rsl_rl/unitree_go2_rough/2025-01-11_14-47-31/videos/train/rl-video-step-0.mp4 就是录制下来的视屏,本人截图如下所示:

在这里插入图片描述

图一

二、视屏录制包装器

上一篇博客对 scripts/reinforcement_learning/rsl_rl/train.py 源码进行分析时,知道其使用了两个包装器,使用到三个包装器,相关代码如下所示:

# STEP: 5.如果任务为多类智能体任务,则将多类智能体任务转换为单类智能体任务,这是因为 RSL-RL 默认只支持单类智能体算法
env = multi_agent_to_single_agent(env)

# STEP: 6.视屏录制包装器,通过 gym.wrappers.RecordVideo 包装之后,训练过程中可根据参数自动录制视频
env = gym.wrappers.RecordVideo(env, **video_kwargs) # 将 env 包装为 gym.wrappers.RecordVideo 对象

# STEP: 7.将环境包装为 RSL-RL 的向量(并行-多智能体)环境,简单的说就是对 env 输出结果进行处理,使其符合 OnPolicyRunner 的输入
env = RslRlVecEnvWrapper(env)

所谓的 env 包装器,就是在原本 env 基础上进行功能的增加或者数据格式的转换,这里先对 gym.wrappers.RecordVideo 这个包装器讲解,使用该包装器的相关源码如下所示:

    # STEP: 6.视屏录制包装器,通过 gym.wrappers.RecordVideo 包装之后,训练过程中可根据参数自动录制视频
    # wrap for video recording
    if args_cli.video:
        video_kwargs = {
            "video_folder": os.path.join(log_dir, "videos", "train"), # 视频保存目录
            "step_trigger": lambda step: step % args_cli.video_interval == 0, # 每间隔 video_interval 步,该函数返回 True,则开始录制视频
            "video_length": args_cli.video_length, # 视频录制的长度
            "disable_logger": True, # 是否禁用日志记录
        }
        print("[INFO] Recording videos during training.")
        print_dict(video_kwargs, nesting=4)
        env = gym.wrappers.RecordVideo(env, **video_kwargs) # 将 env 包装为 gym.wrappers.RecordVideo 对象

包装器 gym.wrappers.RecordVideo 并非由 IsaacLab 提供或封装,而是由 isaac_sim 的源码提供的,其实现于源码中的 /isaac-sim/exts/omni.isaac.ml_archive/pip_prebundle/gymnasium/wrappers/record_video.py 文件。这里不对 RecordVideo 细节进行详细分析,因为个人觉得会使用这个包装器录制训练过程中的视屏即可,并不是啥核心代码,主要对如下几个核心部分进行介绍:

1.初始化函数

于 /isaac-sim/exts/omni.isaac.ml_archive/pip_prebundle/gymnasium/wrappers/record_video.py 文件可以看到 RecordVideo 初始化函数参数:

    def __init__(
        self,
        env: gym.Env, 
        video_folder: str,
        episode_trigger: Callable[[int], bool] = None,
        step_trigger: Callable[[int], bool] = None,
        video_length: int = 0,
        name_prefix: str = "rl-video",
        disable_logger: bool = False,
    ):
        """Wrapper records videos of rollouts.

        Args:
            env: The environment that will be wrapped, 需要被包装的  gym.Env 实例对象
            video_folder (str): The folder where the recordings will be stored 视频保存目录
            
            episode_trigger: Function that accepts an integer and returns ``True`` iff a recording should be started at this episode
                接受一个函数,该函数输入一个整数(当前 episode 数),返回一个布尔值,表示是否应该在这个 episode 开始录制视频

            step_trigger: Function that accepts an integer and returns ``True`` iff a recording should be started at this step
                接受一个函数,该函数输入一个整数(当前 step 数),返回一个布尔值,表示是否应该在这个 step 开始录制视频

            video_length (int): The length of recorded episodes. If 0, entire episodes are recorded.
                Otherwise, snippets of the specified length are captured
                视频录制的长度, 如果为0, 录制整个 episode, 否则录制指定长度的视频

            name_prefix (str): Will be prepended to the filename of the recordings # 视频文件名前缀

            disable_logger (bool): Whether to disable moviepy logger or not.
                是否禁用 moviepy logger, 禁用则即不打印且不记录日志
        """

scripts/reinforcement_learning/rsl_rl/train.p 代码中默认只设置了 step_trigger 函数,如下所示:

"step_trigger": lambda step: step % args_cli.video_interval == 0, # 每间隔 video_interval 步,该函数返回 True,则开始录制视频

其接受一个 int 参数为当前 step 数,若判断已经间隔 video_interval 数则进行视屏录制,从这里可以知道的一点是,video_interval 参数最好大于 video_length 参数,否则可能出现一些意料之外的情况。

2.step(self, action)

有的朋友可能对视屏的渲染与录制视屏过程比较好奇,比如说保存为视屏的图像是如何获取的,本人并没有深入对源码进行分析,大致梳理后知道其获取仿真界面图像画面的调用过程如下所示:

def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
	runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True)
		obs, rewards, dones, infos = self.env.step(actions)
			obs_dict, rew, terminated, truncated, extras = self.env.step(actions)
				self.video_recorder.capture_frame()
					frame = self.env.render()
				self.recorded_frames.append(frame)

直白的说,其就是通过 ManagerBasedRLEnv 实例对象的 render(self, recompute: bool = False) 函数获取经过渲染后的视屏帧,添加到 VideoRecorder 对象成员 self.recorded_frames 之中,当帧数足够时则会统一保存为视屏。具体的细节这里就是不再多说了。

三、数据格式

讲解完视屏录制包装器之后,来讲解比较重要的强化学习库包装器,前面博客 【05.issa-lab】最新从零无死角系列-(03) isaac-lab之框架剖析,Omniverse、Isaac-sim 、Isaac-lab 对比,任务流程,Hydra 配置,强化学习库比较 有提到:

强化学习的库主流的有好几个,比如说:SKRL、RSL-RL、RL-Games 等,要知道他们的参数配置,数据格式,以及输入标准都是不一样的,isaac-lab 是如何对他们进行统一的呢?这个就要涉及到强化学习包装器

该包装器的目的其实十分简单,就是把强化学习库 SKRL、RSL-RL、RL-Games 的输出结果,转换成 train.py 代码中:

env = gym.make(args_cli.task, cfg=env_cfg, render_mode="rgb_array" if args_cli.video else None)

创建的 env 对象能够接收的数据格式即可,那么具体又是实现的呢?

1.RSL-RL接收格式

本人这里以 RSL-RL 这个这个强化学习库为例进行探索,其他的强化学习库略有不同,但是大致上来说,流程与实现逻辑都是一致的,若后续本人使用到其他强化学习库,也会单独进行详细分析。首先来说,通过前面博客知道,关于强化学习库的主要代码入口如下所示,位于 source/standalone/workflows/rsl_rl/train.py:

	runner = OnPolicyRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device)
	runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True)

通过对 runner.learn 函数进行追踪可以看到如下代码:

	runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True)
		def learn(self, num_learning_iterations: int, init_at_random_ep_len: bool = False):
			for it in range(start_iter, tot_iter): # 循环迭代,迭代次数为 num_learning_iterations
				 for i in range(self.num_steps_per_env): # 循环与环境进行交互,每次交互的步数为 num_steps_per_env
				 	obs, rewards, dones, infos = self.env.step(actions) # 与环境进行一次交互,获取观测、奖励、终止状态、额外信息

先不讨论具体细节,可以知道每次训练迭代都会与环境交互 self.num_steps_per_env 个 step,每次交互都获取到 4 个结构数据,分别为:

	tuple[torch.Tensor, torch.Tensor, torch.Tensor, dict]

通过上述分析,可以知道 RSL-RL 强化学习库在训练过程中,每次与环境交互需要获取 obs, rewards, dones, infos 这四个数据。那么当然需要满足 RSL-RL 的需求,毕竟 RSL-RL 不属于 isaac-lab,而是一个安装库,当然是不能去修改别人的源码的。

2.ManagerBasedRLEnv输出格式

通过上面的分析,知道了强化学习库在训练过程中,每次与环境交互需要获取 obs, rewards, dones, infos(具体含义后面章节详细分析) 这四个数据。不过可惜的是,预定义好的 isaac-lab 仿真环境 ManagerBasedRLEnv 示例对象,进行环境交互之后并不是返回这四个数据。首先来看看为什么是 ManagerBasedRLEnv 实例对象完成了与环境的交互。

首先来来说要回顾一下,执行训练的指令参数 --task Isaac-Velocity-Rough-Unitree-Go2-v0,其表明训练的任务为 Isaac-Velocity-Rough-Unitree-Go2-v0,那么该参数与那么些配置文件或者类对象有关联呢?在源码中搜索,可以在
source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/locomotion/velocity/config/go2/_init_.py
文件中看到如下代码:

gym.register(
    id="Isaac-Velocity-Rough-Unitree-Go2-v0",
    entry_point="omni.isaac.lab.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",
        "skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
    },
)

这里就不对其进行细节讨论了,因为下一篇博客会进行详细的分析,这里只需要关注:

entry_point="omni.isaac.lab.envs:ManagerBasedRLEnv"

该配置表明整个仿真环境的入口类为 omni.isaac.lab.envs:ManagerBasedRLEnv,直白说 scripts/reinforcement_learning/rsl_rl/train.py 中代码

	env = gym.make(args_cli.task, cfg=env_cfg, render_mode="rgb_array" if args_cli.video else None)

就是构建这 omni.isaac.lab.envs:ManagerBasedRLEnv 的实例,该类实现于 source/isaaclab/isaaclab/envs/manager_based_rl_env.py 文件中,该篇博客不做细节讲解,只需知道其中包含函数 def step(self, action: torch.Tensor),其返回值如下:

    def step(self, action: torch.Tensor) -> VecEnvStepReturn:
    	......
    	# return observations, rewards, resets and extras
        return self.obs_buf, self.reward_buf, self.reset_terminated, self.reset_time_outs, self.extras

易知 ManagerBasedRLEnv 与环境交互之后其返回 self.obs_buf, self.reward_buf, self.reset_terminated, self.reset_time_outs, self.extras(具体含义后面章节详细分析) 这里 5 个数据,比强化学习框架训练所需要的 4 个数据 obs, rewards, dones, infos 多出一个。

四、强化学习库包装

通过上面的分析可以知道强化学习库 RSL-RL 每次所需要的数据为 obs, rewards, dones, infos,而仿真环境 ManagerBasedRLEnv 获取到的数据为 self.obs_buf, self.reward_buf, self.reset_terminated, self.reset_time_outs, self.extras。其中部分数据已经对齐,如下:
self.obs_buf=obs          self.reward_buf=rewards        self.extras=infos \color{purple} \text {self.obs\_buf=obs ~~~~~~~~~self.reward\_buf=rewards~~~~~~~~self.extras=infos} self.obs_buf=obs          self.reward_buf=rewards        self.extras=infos也就是说,只有 dones 与 self.reset_terminated、self.reset_time_outs 没有对应起来,那么如何对应起来呢?其实也比较简单,因为前面 dones 就是后者 self.reset_terminated 与 self.reset_time_outs 合并之后的结果。

为什么这样说呢?dones 表示智能体挂掉了,但是没有具体细分是如何挂掉的,比如说摔倒了,或者通过了这都属于 self.reset_terminated,若是生命周期达到极限了(比如说卡住,或者没有找到终点)则属于 self.reset_time_outs。总的来说,dones 包含了后面的两种情况。

源码 scripts/reinforcement_learning/rsl_rl/train.py 中的 env = RslRlVecEnvWrapper(env) 其主要作用就是把从 ManagerBasedRLEnv 实例与环境交互获取到的 self.reset_terminated、self.reset_time_outs 合并成 dones,让后传递给强化学习框架 RSL-RL 进行迭代训练,RslRlVecEnvWrapper 具体实现于 source/isaaclab_rl/isaaclab_rl/rsl_rl/vecenv_wrapper.py 文件中,值需要关注其函数 def step(self, actions: torch.Tensor) 即可,如下所示:

    def step(self, actions: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, dict]:
        # record step information
        obs_dict, rew, terminated, truncated, extras = self.env.step(actions)
        # compute dones for compatibility with RSL-RL
        # 计算终止和截断的布尔值,并转换为 long 类型
        dones = (terminated | truncated).to(dtype=torch.long)
        # move extra observations to the extras dict
        obs = obs_dict["policy"]
        extras["observations"] = obs_dict
        # move time out information to the extras dict
        # this is only needed for infinite horizon tasks
        if not self.unwrapped.cfg.is_finite_horizon:
            extras["time_outs"] = truncated

        # return the step information
        return obs, rew, dones, extras

易知从环境 ManagerBasedRLEnv 获取到的相关数据与信息,其首先把 terminated(self.reset_terminated) 与 truncated(self.reset_time_outs) 合并成 dones,然后往字典 extras 中还额外添加了一些信息,最后返回即可。可以看到最后返回 4 个数据 obs, rew, dones, extras 与 强化学习库 RSL-RL 迭代所需数据是一致的。

五、结语

通过该篇博客,了解到 isaac-lab 中包装器这个比较重要的概念,其本质的作用主要充当转接口或装饰器。视屏录制包装器(gym.wrappers.RecordVideo),其类似于一个装饰器的作用,即为基础环境 ManagerBasedRLEnv 实例增加一个视屏录制功能。而后介绍的强化学库包装器(RslRlVecEnvWrapper)则类似于一个转接口的作用,对数据结构进行转换,但是并未添加新的功能。那么到这里为止就完成了第3点的讲解:

         3: 环境与强化学习库,或者强化学习算法是如何联系到一起的 --> 该篇博客。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南才尽,年少无知!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值