IsaacLab从入门到精通(五)机器人强化学习的观测量、动作和奖励函数等设置

在上一节中,我们进行了强化学习环境的创建,并初步跑通了整个流程,然而我们还需要根据实际需要研究的任务设置不同的强化学习环境,那么我们该如何设置呢

1.1 机器人与物体

在教程(二)中,我们已经简单介绍过如何导入自己的机器人与物体,在这里我们将进一步介绍一些设置以及一些实际的例子。 首先,看例子https://github.com/NathanWu7/isaacLab.manipulation/blob/main/isaacLab/manipulation/tasks/Robot_arm/reach/reach_env_cfg.py 中这一部分的代码:

@configclass
class ReachSceneCfg(InteractiveSceneCfg):
    """Configuration for the scene with a robotic arm."""

    # world
    ground = AssetBaseCfg(
        prim_path="/World/ground",
        spawn=sim_utils.GroundPlaneCfg(),
        init_state=AssetBaseCfg.InitialStateCfg(pos=(0.0, 0.0, -1.05)),
    )

    table = AssetBaseCfg(
        prim_path="{ENV_REGEX_NS}/Table",
        spawn=sim_utils.UsdFileCfg(
            usd_path=f"{
      
      ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd",
        ),
        init_state=AssetBaseCfg.InitialStateCfg(pos=(0.55, 0.0, 0.0), rot=(0.70711, 0.0, 0.0, 0.70711)),
    )

    # robots
    robot: ArticulationCfg = MISSING

    # lights
    light = AssetBaseCfg(
        prim_path="/World/light",
        spawn=sim_utils.DomeLightCfg(color=(0.75, 0.75, 0.75), intensity=2500.0),
    )

我们可以看到,首先设置了地面和桌子。然后设置了机器人和光照。其中,桌子是通过Nucleus添加的,也可以改成工程目录assets下自己的物体,这部分的设置可以参考AssetsBaseCfg的文档:

https://isaac-sim.github.io/IsaacLab/source/api/lab/omni.isaac.lab.assets.html#omni.isaac.lab.assets.AssetBaseCfg

在这里我们可以导入不同的物体,并调整物体初始化的位置、姿态等属性。利用这个设置,我们可以搭建出自己的任务环境,这里你也可以导入一些需要操作的特殊物体,这样就不用再env_cfg.py同级的Config里导入了。这两个位置可以自由选择,如果更改机器人的时候不更改物体,可以在这里导入物体,但是需要补充一些接口。如果需要在更改机器人时更改物体,建议放在Config文件夹下特定机器人的设置中。

1.2 mdp文件夹

在env_cfg.py同级的mdp文件夹中,我们可以加入自定义的Obs、Action、Reward等强化学习基本元素,注意只要在文件夹的init文件中设置好引用关系,就可以在env_cfg.py中通过mdp接口实现不同元素的管理,非常方便。mdp其实是马尔科夫决策过程(Markov Decision Process)的简称,这个接口提供了一次决策过程所需要的所有元素,整体功能非常符合它的名字。

1.3 环境观测量

环境观测量由ObsGroup进行统一管理,这里可以在本文件env_cfg.py同级的mdp文件夹中加入一些自定义的观测量,这样在引用时就可以通过mdp接口调用了。示例代码如下:

class ObservationsCfg:
    """Observation specifications for the MDP."""

    @configclass
    class PolicyCfg(ObsGroup):
        """Observations for policy group."""

        # observation terms (order preserved)
        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=-0.01, n_max=0.01))
        pose_command = ObsTerm(func=mdp.generated_commands, params={
    
    "command_name": "ee_pose"})
        actions = ObsTerm(func=mdp.last_action)

        def __post_init__(self):
            self.enable_corruption = True
            self.concatenate_terms = True

    # observation groups
    policy: PolicyCfg = PolicyCfg()

其中,joint_pos代表当前关节角的位置,joint_vel代表当前关节角的速度,pose_command是一个指令类,它会生成一个目标点,actions代表上一次执行的actions。注意这里官方提供的示例代码中,actions并没有经过裁剪,因此在某些情况下,actions的输出会很大,导致Obs中这一项很大,从而引起Critic网络的崩溃。当然,有同学会问,Obs有没有经过裁剪?在其他函数中,例如joint_pos和joint_vel,这些部分的obs是经过裁剪的,而last_actions这一项比较特殊,没有经过裁剪,如果希望保留这一项而不改变actions的输出,可以试试单独裁剪这一项。

当然,除了使用这些观测函数以外,我们也可以在mdp中定义自己的观测量,或者对原观测量进行一些重载。例如:https://github.com/NathanWu7/isaacLab.manipulation/blob/main/isaacLab/manipulation/tasks/Robot_arm/reach/mdp/observations.py 在自定义的obs函数中,我们需要查看文档和变量名,获取到需要获取的物理量,并return这个物理量,就能通过mdp调用将其作为观测量了。

有一些特殊的观测量,例如图像、点云等,这些观测量也是可以在这里进行设置,但一般不推荐直接作为Obs输入,最好先经过一些网络处理进行降维,这样强化学习训练过程会更容易收敛。目前还没有研究对图像和点云的观测量进行测试,后续的教程中可能会加入一些这部分的内容。

1.4 机器人的动作Action

机器人的动作部分比较特殊,分为基础的动作类和特定机器人的动作类,基础的动作类在mdp中设置,而特定的动作类和机器人的型号相关,需要在Config文件夹下特定机器人的设置文件中设置。

1.4.1 基础动作类

基础动作类主要对强化学习Actor网络的输出进行了处理, 从Actor到网络的输出,一般会经过三步:

(1)网络输出裁剪。将网络输出裁剪到(-1,1)。这一步是进行一个初步的限制,因为Actor的输出在某些情况下是非常大的,可能会影响到整体网络的收敛性。这一部分在官方Manager based的源码中是没有的,但是在Direct方式的例子中却有,建议最好加上,在笔者的代码https://github.com/NathanWu7/isaacLab.manipulation/blob/main/isaacLab/manipulation/tasks/Robot_arm/reach/mdp/actions.py 中已经加上了。

(2)线性映射。这一步是源代码Actor类设置的,在process action函数中,代码如下:

        scaled_actions = self._raw_actions * self._scale + self._offset

就是输出动作=初始动作*缩放因子+偏置。这一步的目的是为了将输出进一步调整,将(-1,1)的范围进行一个线性变换。例如,乘以pi就得到一个关节旋转的范围,再加一个pi就得到0到2pi这样一个0到360度的范围。

(3)限制位裁剪。这一步在官方的例子中同样没有,但是笔者建议加上。它的目的是实际机器人的关节有些位置是到不了的,因此如果要sim2real则必须考虑这个问题,好在我们可以直接获取各个关节的限位,因此这个功能实现起来很简单:

        self.processed_actions = self.processed_actions.clamp(
            self._asset.data.soft_joint_pos_limits[:, self._joint_ids, 0],
            self._asset.data.soft_joint_pos_limits[:, self._joint_ids, 1],
        )

这一段在笔者的参考代码中放在了apply action这个函数中,在这一步之前还有一些前处理,根据你控制方式(特定动作)的不同,如速度控制,位置控制,力矩控制等,我们会对第二步scaled_actions做一些处理。在最后处理后,关节电机的位置将会赋值给仿真场景,因此这一步一般需要加在最后。如果在控制量计算的过程中就需要进行裁剪,则需要调整裁剪的顺序等。这里根据实际需求的不同,会有很多种处理方式,因此笔者的例子也仅供参考。

1.4.2 特定动作类

经过基础动作类的处理,网络输出的Action已经成功转换到机器人的控制动作了。然而,机器人关节控制有很多种方式,参考官方的文档:

https://isaac-sim.github.io/IsaacLab/source/api/lab/omni.isaac.lab.envs.mdp.html#module-omni.isaac.lab.envs.mdp.actions

这些方式实现了机器人各种方式的控制,它们的实现函数很多都是基于基础动作类通过重载引入不同的运动学和动力学模型实现的,我们主要在config文件夹下机器人的接口内设置,举例如:

https://github.com/NathanWu7/isaacLab.manipulation/blob/main/isaacLab/manipulation/tasks/Robot_arm/reach/config/kinova_gripper/joint_pos_env_cfg.py

对于机械臂和机械手,个人用的比较多的是RelativeJointPositionCfg和BinaryJointAction,如果是用JointPositionAction的话就是直接给出一个目标关节位置,依靠机器人的PD控制去到达,这个方式在笔者使用的机械臂上需要单独做下位机控制接口,因此笔者很少选择这种方式。RelativePosition方式就是一种意义上的速度控制,即在同一个时间间隔里移动不同的距离,这种方式也是需要调整PD参数,保证和真机的Gap很小的情况下比较好。Velocity方式理论上应该和RelativePosition有着相似的效果,Effort方式在一些特殊控制方式下比较有用。Binary方式大多用于二指平行夹爪,这种方式指定开合位置后,通过action的正负实现Binary的开合,值得注意的是我们需要调整夹爪的PD参数和关节限制,否则在一些具有被动关节(mimic)的夹爪(Robotiq)上会出现一些bug。

1.5 奖励函数与终止条件

奖励函数的设置也是通过mdp管理的,我们同样可以在mdp文件夹下建立reward.py,在其中设计自己的奖励函数,然后通过RewTerm在env_cfg.py里引用就可以了。Reward的设置与Termination,也就是终止条件的设置也很相关。例如,我们需要机器人到达一个目标点,此时任务终止并且给充分奖励,加上一些时间惩罚penalty,机器人会尝试在最短时间内完成任务。在这种情况下,我们需要先在reward里设置一个触发奖励,再在mdp的termination里设置一个类似的函数,注意Termination里函数的返回值需要是bool类型。

1.6 事件与课程

Event类型主要用于随机物体和目标的位置,还有引入一些干扰等,也包括域随机化(Domain Randomization)。这个类型可以给策略训练的过程一些不确定性,从而使最后策略的效果更好。Curriculum类型主要是依据强化学习中的课程学习方法进行设置,这个接口目前主要的作用是在一定步数后,修改某些奖励函数的权重。例如,我们可以在训练初期给比较难的目标一个小的奖励值,让策略主要着重与比较简单的目标,在后期一定步数后(在简单的目标上收敛)通过Curriculum方法提高困难目标的权重,使其能够继续学习更复杂的行为。要注意的是,设置的奖励不能形成冲突,即困难目标和简单目标有一定的冲突后,智能体的表现会变得很差。

强化学习的基本设置在这里就介绍完毕了,下一节内容将会简单介绍实际策略在真实系统上的部署。

猜你喜欢

转载自blog.csdn.net/DejaWu33/article/details/140514339
今日推荐