第八讲、Isaaclab创建基于管理器的基础环境

0 前言

官方教程:https://isaac-sim.github.io/IsaacLab/main/source/tutorials/03_envs/create_manager_base_env.html
Isaacsim+Isaaclab安装:https://blog.csdn.net/m0_47719040/article/details/146389391?spm=1001.2014.3001.5502

上一节中我们介绍了一个scene.InteractiveScene场景类,它提供了一个便捷的接口,用于在模拟中生成prim并进行管理。本节将研究如何在Isaaclab中创建环境,所谓环境实际上就是汇集了模拟的不同方面,如场景、观测和动作空间、重置事件等。环境将仿真的多个要素(场景、观测空间、动作空间、重置事件等)统一封装,形成标准化接口,通过环境可以使不同的应用场景的创建变的更简单化、模块化。

在Isaaclab中基于管理器的环境被分为两个类来实现,一下是两类区别

类名适用场景核心功能差异典型用途示例
envs.ManagerBasedEnv传统机器人控制不包含奖励机制和终止条件机械臂轨迹跟踪、无人机定点控制
envs.ManagerBasedRLEnv强化学习任务包含奖励计算、终止判断、课程学习系统DRL算法训练、自适应控制策略开发

在本教程中,我们将研究envs.ManagerBasedEnv类及其对应的envs.ManagerBasedEnvCfg配置类。我们将使用之前的 cartpole 环境来介绍创建envs.ManagerBasedEnv新环境时使用的不同组件。

教程对应的脚本为create_cartpole_base_env.pyscripts/tutorials/03_envs目录下。

运行该程序:

  • 进入安装 isaac lab 时创建的conda虚拟环境
  • 在该环境下进入 isaac sim文件夹中运行source setup_conda_env.sh
  • 终端中输入python scripts/tutorials/03_envs/create_cartpole_base_env.py --num_envs 32运行你的代码。
    在这里插入图片描述

1 场景设置

创建新环境的第一步是配置其场景。对于 cartpole 环境,我们将使用上一个教程中的场景,官方省略了这部分的详解。
在查看原始python文件可以发现场景是通过下述代码引入的,该包的实际地址在/IsaacLab/source/isaaclab_tasks/isaaclab_tasks/manager_based/classic/cartpole/cartpole_env_cfg.py并非我们上一节中自己创建的:

from isaaclab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg

因此首先注释上述代码,并加入两行新代码:

# from isaaclab_tasks.manager_based.classic.cartpole.cartpole_env_cfg import CartpoleSceneCfg
import sys
sys.path.append("scripts/tutorials/02_scene")
from create_scene import CartpoleSceneCfg

运行后会发现有报错,注意绿色框部分,翻译过来就是提醒我们,你是不是启动了多余的Isaacsim APP:
在这里插入图片描述回忆一下之前的代码就明白,我们需要在上一节的代码中注释掉一部分:

# import argparse

# from isaaclab.app import AppLauncher

# # add argparse arguments
# parser = argparse.ArgumentParser(description="Tutorial on using the interactive scene interface.")
# parser.add_argument("--num_envs", type=int, default=2, help="Number of environments to spawn.")
# # append AppLauncher cli args
# AppLauncher.add_app_launcher_args(parser)
# # parse the arguments
# args_cli = parser.parse_args()

# # launch omniverse app
# app_launcher = AppLauncher(args_cli)
# simulation_app = app_launcher.app

同时,由于键值有一点小小的变化,调整CartpoleSceneCfg类:

@configclass
class CartpoleSceneCfg(InteractiveSceneCfg):
    """Configuration for a cart-pole scene."""

    # ground plane
    ground = AssetBaseCfg(prim_path="/World/defaultGroundPlane", spawn=sim_utils.GroundPlaneCfg())

    # lights
    dome_light = AssetBaseCfg(
        prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
    )

    # articulation
    #cartpole: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
    robot: ArticulationCfg = CARTPOLE_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

最后,再次运行没有问题。

2 定义动作

对于动作的控制实际上分为两种,一种是直接控制,一种是管理器控制:

  • 直接控制:使用assets.Articulation.set_joint_effort_target()直接向关节施加力。(之前几节中的方法
    • 缺点:代码耦合度高,难以扩展复杂控制逻辑。
  • 管理控制:使用managers.ActionManager统一管理动作,通过组合多个ActionTerm实现模块化控制。
    • 优点:代码结构清晰,支持多组件独立控制(如机械臂和夹爪分开控制)。

ActionManager 是一个“动作管理中心”,负责协调多个动作项(ActionTerm)。当用户输入动作(如一个力值)时,ActionManager 会将动作分发给所有注册的ActionTerm,每个Term根据自己的逻辑处理动作(如施加力、设置位置等)。

  • 每个ActionTerm是独立的动作控制单元,负责具体的物理交互。例如:
    • JointEffortActionTerm:通过力/力矩控制关节。
    • JointPositionActionTerm:通过目标位置控制关节。
    • GripperActionTerm:控制夹爪的开合。
  • Cartpole 示例:目标是控制推车的力以平衡杆子,因此需定义一个CartForceActionTerm
    • 绑定到推车的关节。
    • 将用户输入的动作值(标量力)转换为关节的力。
    • 通过set_joint_effort_target()或其他底层方法实现控制。
@configclass
class ActionsCfg:
    """Action specifications for the environment."""
	#定义当前环境的动作空间结构和控制参数。此处只定义了一个动作项 joint_efforts,表示通过关节力控制推车。
    joint_efforts = mdp.JointEffortActionCfg(asset_name="robot", joint_names=["slider_to_cart"], scale=5.0)
# asset_name="robot":指定要控制的机器人资产名称(在场景中定义的推车模型名)
# joint_names=["slider_to_cart"]:目标关节名(推车与底座之间的滑动关节)
# scale=5.0:动作缩放系数:将输入的动作值(如 [-1, 1])映射为实际力值(-5.0 到 5.0)

当环境初始化时,框架会根据 ActionsCfg 中的配置自动创建对应的 ActionTerm(如 JointEffortActionTerm)并注册到 ActionManager。等效的手动代码如下:

# 自动生成的等效代码
from omni.isaac.orbit.managers import ActionManager
from omni.isaac.orbit.managers.action_terms import JointEffortActionTerm

action_manager = ActionManager()

# 根据配置创建 ActionTerm
cart_term = JointEffortActionTerm(
    asset_name="robot",
    joint_names=["slider_to_cart"],
    scale=5.0
)
action_manager.add_term("joint_efforts", cart_term)  # "joint_efforts" 是键名

3 定义观测

  • 观察(Observations)与场景状态(Scene State)的区别:
    示例:在 Cartpole 中,场景状态包含推车和杆的全部动力学信息,而观察可能仅包含推车位置、速度和杆的倾斜角。
    • 场景状态:环境的完整物理状态,包括所有物体的位置、速度、关节角度等,可能包含冗余或不可观测的信息(如内部传感器噪声)。
    • 观察:智能体(Agent)实际能感知到的状态子集,经过选择性处理和加工(如去噪、归一化、特征提取)。

ObservationManager 的核心作用:管理环境中所有观察项的生成与组合,负责将原始场景状态转换为智能体可用的观察。对于 ObservationManager来说实际上就是,输入:场景的原始物理状态;输出:结构化观察数据(如张量)。

与动作管理器类似,观察管理器可以包含多个观察项。

  • ObservationGroup(观察组):一组具有相同维度的观察项的集合,用于定义不同用途的观察空间。
    • “policy”:供强化学习策略使用的主要观察空间。
    • “critic”:供值函数(Critic)使用的额外观察信息。
    • “safety”:用于安全监控的专用观察(如碰撞检测)。
  • ObservationTerm(观察项):单个观察信号的生成规则,例如关节角度、末端执行器位置、图像像素等。
    • func:计算该观察项的函数或可调用类(核心参数)。
    • noise:添加的噪声模型(如高斯噪声)。
    • clip:观察值的裁剪范围(如 [-1, 1])。
    • scale:观察值的缩放系数。
@configclass
class ObservationsCfg:
    """Observation specifications for the environment."""
	'''
    ObservationsCfg 类
    目的:
	定义环境中所有观察组的配置。此处仅包含一个名为 policy 的观察组,符合 Isaac Lab 的默认要求。
	policy: PolicyCfg = PolicyCfg():将嵌套的 PolicyCfg 类实例化为 policy 属性,作为主要的观察组。
    '''
    @configclass
    class PolicyCfg(ObsGroup):
        """Observations for policy group."""
        '''
        PolicyCfg 类(继承自 ObsGroup)
        joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel):定义一个观察项,计算关节相对位置。
        joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel):定义另一个观察项,计算关节相对速度。
        ObsTerm:等价于 managers.ObservationTermCfg,封装单个观察项的配置。
        func 参数:指定计算该观察项的函数或类(如 mdp.joint_pos_rel)。
        '''

        # 观察项定义
        joint_pos_rel = ObsTerm(func=mdp.joint_pos_rel)
        joint_vel_rel = ObsTerm(func=mdp.joint_vel_rel)
        # 后初始化设置
        def __post_init__(self) -> None:
            self.enable_corruption = False
            self.concatenate_terms = True

    # 将 PolicyCfg 实例化为 policy 属性
    policy: PolicyCfg = PolicyCfg()

4 定义事件

至此,我们已经为 cartpole 环境定义了场景、动作和观察。所有这些组件的总体思路是定义配置类,然后将它们传递给相应的管理器。事件管理器也是如此。

  • EventManager 的核心作用:管理仿真过程中的动态事件,如随机化物理参数、重置场景状态、修改视觉属性等。
  • 类比前述管理器:
管理器管理对象触发机制
ActionManager动作项(ActionTerm)每一步(step()
ObservationManager观察项(ObservationTerm)每一步(step()
EventManager事件项(EventTerm)按模式(mode)触发(启动、重置、间隔)
  • 事件模式(Modes):Isaac Lab 内置三种事件触发模式
    • “startup”:
      • 触发时机:仅在环境初始化时执行一次。
      • 典型用途:初始化耗时操作(如加载复杂模型、预计算物理参数)。
      • 示例:随机化杆的质量(因修改质量需要重新构建物理引擎的内部结构,开销较大)。
    • “reset”:
      • 触发时机:每次环境重置时执行(如 episode 结束后的 reset())。
      • 典型用途:重置初始状态、随机化目标位置、更换纹理等。
      • 示例:随机化推车和杆的初始关节位置与速度。
    • “interval”:
      • 触发时机:每隔固定步数(如每 100 步)执行一次。
      • 典型用途:动态难度调整、周期性扰动(如模拟环境变化)。

在本例中,我们定义了一些事件,用于在启动时随机化杆的质量。由于此操作开销较大,我们不想在每次重置时都执行此操作,因此只需执行一次。我们还创建了一个事件,用于在每次重置时随机化推杆和杆的初始关节状态。

@configclass
class EventCfg:
    """Configuration for events."""

    # 在环境初始化时(仅一次),随机增加杆(pole)的质量,以创建不同的物理动态特性。
    add_pole_mass = EventTerm(
        func=mdp.randomize_rigid_body_mass,#预定义的函数,用于随机化刚体质量。
        mode="startup",# 启动时执行
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=["pole"]),
            #指定目标实体:场景中名为 "robot" 的资产下,刚体名称为 "pole"。
            "mass_distribution_params": (0.1, 0.5),
            #质量随机分布参数:生成 Uniform(0.1, 0.5) 的随机值,并叠加到原有质量上。
            "operation": "add",
            #操作模式为“叠加”而非“覆盖”("add" 表示增加质量,"set" 表示直接设置)。
        },
    )

    # on reset
    reset_cart_position = EventTerm(
        func=mdp.reset_joints_by_offset,#预定义的函数,通过偏移量重置关节状态。
        mode="reset",#重置时执行
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["slider_to_cart"]),
            # 目标关节:推车的滑动关节(slider_to_cart)。
            "position_range": (-1.0, 1.0),
            # 推车初始位置的随机范围(单位:米)。
            "velocity_range": (-0.1, 0.1),
            # 	推车初始速度的随机范围(单位:米/秒)。
        },
    )

    reset_pole_position = EventTerm(
        func=mdp.reset_joints_by_offset,
        mode="reset",# 重置时执行
        params={
            "asset_cfg": SceneEntityCfg("robot", joint_names=["cart_to_pole"]),
            # 目标关节:杆的旋转关节(cart_to_pole)。
            "position_range": (-0.125 * math.pi, 0.125 * math.pi),
            # 杆初始角度的随机范围(约 -22.5° 到 22.5°)。
            "velocity_range": (-0.01 * math.pi, 0.01 * math.pi),
            # 杆初始角速度的随机范围(约 -1.8°/秒 到 1.8°/秒)。
        },
    )

5 把所有东西联系起来

定义了场景和管理器配置后,通过 envs.ManagerBasedEnvCfg 类整合环境配置。

ManagerBasedEnvCfg 是环境的总配置类,该类接收场景、动作、观察和事件配置及envs.ManagerBasedEnvCfg.sim定义模拟参数(例如时间步长、重力等)的参数,可以通过__post_init__()来修改模拟参数,该方法在配置初始化后调用。

@configclass
class CartpoleEnvCfg(ManagerBasedEnvCfg):
# ManagerBasedEnvCfg:基类,Isaac Lab 中基于管理器环境的通用配置模板,包含场景、动作、观察、事件等模块的配置槽位。
    """Configuration for the cartpole environment."""

    # 场景配置
    scene = CartpoleSceneCfg(num_envs=1024, env_spacing=2.5)
    # Cartpole 场景的专用配置类,定义推车、杆子、轨道等物理实体的初始化参数。上一节中的知识
    # num_envs=1024:并行环境实例数量(1024 个环境同时仿真,用于强化学习的高效批量训练)。
    # env_spacing=2.5:环境之间的间隔距离(单位:米),避免不同环境中的推车碰撞。
    # 基础模块配置
    observations = ObservationsCfg() # 注入观察配置类,定义智能体可观测的状态(如推车位置、杆子角度)。
    actions = ActionsCfg() # 注入动作配置类,指定如何将策略输出的动作转换为关节力。
    events = EventCfg() # 注入事件配置类,定义随机化质量、初始状态等动态事件。

	# 后初始化设置 (__post_init__)
    def __post_init__(self):
        """Post initialization."""
        # 查看器设置
        self.viewer.eye = [4.5, 0.0, 6.0] # 摄像机位置 (X, Y, Z)
        self.viewer.lookat = [0.0, 0.0, 2.0] # 摄像机焦点 (X, Y, Z)
        # 步进设置
        self.decimation = 4  # 每隔 4 个仿真步执行一次环境步
        # 仿真参数
        self.sim.dt = 0.005  # 仿真步长 5ms,对应 200Hz

6 运行模拟

基本步骤其实跟之前的类似的,只不过把所有的东西都整合到了env中。具体内容如下:
对于ManagerBasedEnv官方文档做了更详细的说明,传送门放在下方:
https://isaac-sim.github.io/IsaacLab/main/source/api/lab/isaaclab.envs.html#isaaclab.envs.ManagerBasedEnv

def main():
    """Main function."""
    # 解析命令行参数,整合环境配置
    env_cfg = CartpoleEnvCfg()
    env_cfg.scene.num_envs = args_cli.num_envs # 从命令行参数设置并行环境数
    # 根据环境配置初始化环境(实例化)
    env = ManagerBasedEnv(cfg=env_cfg)

    # 仿真循环
    count = 0
    while simulation_app.is_running():# 检查仿真应用是否在运行(如 Isaac Sim 窗口未关闭)
        with torch.inference_mode():# 禁用梯度计算,提升性能
            # 定期重置环境
            if count % 300 == 0:
                count = 0
                env.reset() # 重置所有并行环境,实际上就是EventCfg中指定好的内容
                print("-" * 80)
                print("[INFO]: Resetting environment...")
            # 生成随机动作
            joint_efforts = torch.randn_like(env.action_manager.action)
            # 执行环境步进
            obs, _ = env.step(joint_efforts) # obs 是观察值字典,_ 占位奖励和终止标志(此处未使用)
            # 打印第一个环境的杆关节观察值(假设为倾斜角)
            print("[Env 0]: Pole joint: ", obs["policy"][0][1].item())
            # 更新计数器
            count += 1

    # 关闭环境,释放资源
    env.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值