PPO算法拆解

在强化学习legged_gym中rsl_rl所实现的PPO算法源码如下:

# SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Copyright (c) 2021 ETH Zurich, Nikita Rudin

import torch
import torch.nn as nn
import torch.optim as optim

from rsl_rl.modules import ActorCritic
from rsl_rl.storage import RolloutStorage

class PPO:
    actor_critic: ActorCritic
    def __init__(self,
                 actor_critic,
                 num_learning_epochs=1,
                 num_mini_batches=1,
                 clip_param=0.2,
                 gamma=0.998,
                 lam=0.95,
                 value_loss_coef=1.0,
                 entropy_coef=0.0,
                 learning_rate=1e-3,
                 max_grad_norm=1.0,
                 use_clipped_value_loss=True,
                 schedule="fixed",
                 desired_kl=0.01,
                 device='cpu',
                 ):

        self.device = device

        self.desired_kl = desired_kl
        self.schedule = schedule
        self.learning_rate = learning_rate

        # PPO components
        self.actor_critic = actor_critic
        self.actor_critic.to(self.device)
        self.storage = None # initialized later
        self.optimizer = optim.Adam(self.actor_critic.parameters(), lr=learning_rate)
        self.transition = RolloutStorage.Transition()

        # PPO parameters
        self.clip_param = clip_param
        self.num_learning_epochs = num_learning_epochs
        self.num_mini_batches = num_mini_batches
        self.value_loss_coef = value_loss_coef
        self.entropy_coef = entropy_coef
        self.gamma = gamma
        self.lam = lam
        self.max_grad_norm = max_grad_norm
        self.use_clipped_value_loss = use_clipped_value_loss

    def init_storage(self, num_envs, num_transitions_per_env, actor_obs_shape, critic_obs_shape, action_shape):
        self.storage = RolloutStorage(num_envs, num_transitions_per_env, actor_obs_shape, critic_obs_shape, action_shape, self.device)

    def test_mode(self):
        self.actor_critic.test()
    
    def train_mode(self):
        self.actor_critic.train()

    def act(self, obs, critic_obs):
        if self.actor_critic.is_recurrent:
            self.transition.hidden_states = self.actor_critic.get_hidden_states()
        # Compute the actions and values
        self.transition.actions = self.actor_critic.act(obs).detach()
        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()
        self.transition.action_mean = self.actor_critic.action_mean.detach()
        self.transition.action_sigma = self.actor_critic.action_std.detach()
        # need to record obs and critic_obs before env.step()
        self.transition.observations = obs
        self.transition.critic_observations = critic_obs
        return self.transition.actions
    
    def process_env_step(self, rewards, dones, infos):
        self.transition.rewards = rewards.clone()
        self.transition.dones = dones
        # Bootstrapping on time outs
        if 'time_outs' in infos:
            self.transition.rewards += self.gamma * torch.squeeze(self.transition.values * infos['time_outs'].unsqueeze(1).to(self.device), 1)

        # Record the transition
        self.storage.add_transitions(self.transition)
        self.transition.clear()
        self.actor_critic.reset(dones)
    
    def compute_returns(self, last_critic_obs):
        last_values= self.actor_critic.evaluate(last_critic_obs).detach()
        self.storage.compute_returns(last_values, self.gamma, self.lam)

    def update(self):
        mean_value_loss = 0
        mean_surrogate_loss = 0
        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)
        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:


                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
                if self.desired_kl != None and self.schedule == 'adaptive':
                    with torch.inference_mode():
                        kl = torch.sum(
                            torch.log(sigma_batch / old_sigma_batch + 1.e-5) + (torch.square(old_sigma_batch) + torch.square(old_mu_batch - mu_batch)) / (2.0 * torch.square(sigma_batch)) - 0.5, axis=-1)
                        kl_mean = torch.mean(kl)

                        if kl_mean > self.desired_kl * 2.0:
                            self.learning_rate = max(1e-5, self.learning_rate / 1.5)
                        elif kl_mean < self.desired_kl / 2.0 and kl_mean > 0.0:
                            self.learning_rate = min(1e-2, self.learning_rate * 1.5)
                        
                        for param_group in self.optimizer.param_groups:
                            param_group['lr'] = self.learning_rate


                # Surrogate loss
                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(ratio, 1.0 - self.clip_param,
                                                                                1.0 + self.clip_param)
                surrogate_loss = torch.max(surrogate, surrogate_clipped).mean()

                # Value function loss
                if self.use_clipped_value_loss:
                    value_clipped = target_values_batch + (value_batch - target_values_batch).clamp(-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()

                loss = surrogate_loss + self.value_loss_coef * value_loss - self.entropy_coef * entropy_batch.mean()

                # Gradient step
                self.optimizer.zero_grad()
                loss.backward()
                nn.utils.clip_grad_norm_(self.actor_critic.parameters(), self.max_grad_norm)
                self.optimizer.step()

                mean_value_loss += value_loss.item()
                mean_surrogate_loss += surrogate_loss.item()

        num_updates = self.num_learning_epochs * self.num_mini_batches
        mean_value_loss /= num_updates
        mean_surrogate_loss /= num_updates
        self.storage.clear()

        return mean_value_loss, mean_surrogate_loss

1. 场景比喻

假设你经营着一家披萨店,店里有一位新来实习的学徒,你想教会他如何做出好吃的披萨。你可以把学徒想象成一个「神经网络」,你就是想要训练他的「大脑」,让他学会每次做披萨时都能做出正确的决策(怎么揉面、放多少酱料、何时烤制等)。

但是你不想一下子就把他灌输所有的知识,你希望通过一种“试错”(互动)方式,让学徒自己从实际操作里学到什么决策才是好的、什么决策是坏的。

在这个过程中,你会:

  1. 让学徒去做一段时间披萨(相当于“收集经验”)。
  2. 看学徒表现(把“做披萨”的过程数据存下来)。
  3. 给学徒打分或奖励(如果披萨好吃,就给高分;不好吃,就给低分)。
  4. 在一段时间之后把这些数据拿出来统一进行一次“复盘”,并根据得失进行调整(更新学徒的做法)。

PPO(Proximal Policy Optimization)就是这么一个试错 + 打分 + 调整的过程。代码中,主要有以下角色:

  1. 学徒的大脑(actor_critic)
  • actor(演员):决定下一步行动时该做什么(比如:在披萨上放多少酱料)。
  • critic(评论员):决定学徒当前动作做得好不好,用价值来衡量(比如:预计这样做出来的披萨有多好吃)。
  1. 记事本或账本(RolloutStorage)
  • 记录学徒每一步的操作、获得的分数、以及一些当时的环境信息、学徒的心态等等。
  1. PPO 这个管理者(PPO类本身)
  • 负责告诉学徒什么时候去做披萨,什么时候来复盘学习。
  • 负责管理学徒的日常训练计划,比如学习率、奖励计算、经验数据存储等。
  • 训练中的“奖惩原则”或“学习规则”也都在这里。

下面我们就从头到尾讲一下,这段代码是如何在“店里”训练“学徒”的

2. init:初始化工具

当我们新开店时,会先做一些准备工作。比如,买工具、准备原材料、制定培训计划等等。对应的代码在 init 里完成了这些事:

  • actor_critic:学徒的大脑。本质是一个神经网络,里边包含了“做决定”和“估算好坏”两部分。
  • 一系列超参数:
    • clip_param: PPO 对行动调整幅度的限制。就像你告诉学徒:每次改进可以,但是不要一次性改太激进。
    • gamma: 相当于给奖励打折扣的一个因子。比如说,后面才拿到的好处价值会稍微折扣一下。
    • lam: 用来计算平滑的优势函数(GAE),帮助学徒知道具体哪一步做得好或不好。
    • value_loss_coef, entropy_coef:在总的学习目标里,你要告诉学徒更重视哪部分,比如更重视“预测准确”还是更重视“保持灵活”。
    • learning_rate: 学徒学习的速度。学徒如果一次学太多会消化不良,但学太慢又效率太低,需要找个合适的值。
  • optimizer:训练学徒的方式,这里选择了Adam,就像是你给学徒设定了“连续改进”的学习方式。
  • storage 和 transition:相当于一本“流水账本”和一个“临时记录纸片”,把学徒每个动作和相应的结果记下来,再定期汇总进行系统学习。

3. init_storage(…):开本新账

你开了一本新的“流水账本”(RolloutStorage)用来记录学徒每一步做的事、得到的结果。就像开了一个新笔记本,专门记录学徒试做披萨的过程。

4. act(…):让学徒操作并记录

当顾客来了,学徒开始做披萨了,这里就要让学徒根据所见的环境“观察”(obs、critic_obs)来决定“行动”(动作),并把一些关键信息记到“临时纸片”上。

  • 决定行动(actor):学徒看到饼皮大小、顾客口味等信息后,决定接下来要怎么做(放多少料、怎么搅拌)。
  • 评价好坏(critic):学徒内心也会有个估计,“我觉得这一步做出来的披萨会如何?”
  • 记录下来(transition):把“我的动作”,“我估计的评分”,“当前状态”都先写在一张纸片上。
    等真正送到顾客或烤炉那一步后,学徒会拿到实际的结果,这时再把纸片上写的东西正式抄进账本。

5. process_env_step(…):真正获取反馈并写入账本

就像披萨烤好了,顾客吃完了,你收到了评分(rewards),知道这次表现好不好,还有没有把披萨糊掉(dones 表示这一回合结束没有)。

  • 把奖励记上去:这次顾客给了几星?这就是代码里 self.transition.rewards = rewards.clone()。
  • 记录回合是否结束:如果这块披萨已完成,就要标记 dones,并重置学徒的状态(相当于一单结束重新来)。
  • 处理超时:如果中间某些情况导致“时限到”,还会有一些额外的计算,把学徒自己估计的价值加回奖励(time_outs 的部分)。
  • 正式记账:最后一行 self.storage.add_transitions(self.transition) 就像把那张“临时纸片”正式贴到我们的账本里。
  • 清空纸片:学徒准备下一单做披萨了。

6. compute_returns(…):结账时算总分

等学徒连着做完了若干个披萨,或者一个回合结束了,就需要来做一个小结:这段时间里学徒做得怎么样?要不要给一些鼓励或指导?

  • 这里的 compute_returns 会把最终的回报(return)算出来,结合过去的估计,算出更精确的“优势函数”(优势:到底哪个操作比平庸的操作高出多少分)。
  • 这些数据会留在账本里,以便稍后的整体学习。

7. update(…):复盘学习,改进手法

这就是学徒真正集体反思和学习的阶段。
类似于:让学徒把前面做每个披萨的经历都翻出来,好好对比“我当初决定的做法”和“实际得到的结果”之间的差异,然后根据理论(PPO 策略)做改进:

  1. 拿出之前所有的记账:这里会把刚才存进账本的所有记录分成若干小段(mini-batch)。
  2. 对每个小段数据,学徒先计算现在的想法
    • 学徒看看“如果再来一次,我可能会怎么做(新的动作分布)?”
    • 把这个新的想法跟之前实际做的动作对比,并计算变化率(ratio)。
  3. 防止学徒“一次改进过猛”
    • PPO 有一个 clip_param(剪切范围),像是你对学徒说:“慢慢改,不要一次把配方改得面目全非。”
    • 因此在计算“损失函数”时,会把变化过大的地方剪一下。
  4. 价值函数的改进
    • 学徒还要学会更准确地“预估”自己每个动作带来的结果。这会涉及一个专门的价值损失(value loss)。
    • 如果开启了“剪切价值”,也会对这部分做类似的限制,防止一口气把学徒内心的评分标准改得太离谱。
  5. 加入熵增强探索
    • 让学徒保持对各种可能性的探索,不要太快陷入“老是放同样的料”的习惯。我们给一点额外奖励鼓励多元做法,叫 entropy_coef。
  6. 合并成一个总的损失
    • 学徒这次的整体学习目标,就是“在保证做披萨决策改得恰当”的同时,也要让“评价系统更准”。
    • 然后再在这个损失上进行一次梯度下降,更新学徒的脑袋(神经网络参数)。
  7. 自适应学习率(可选)
    • 如果发现学徒跟之前想法的差异太大(KL 散度过大),就降低学习率;如果差异过小,就提高学习率。就像如果学徒老是变动太大,你劝他先沉住气,别急;如果学徒一直不变动,你希望他大胆一些。

复盘完之后,学徒又变聪明了一些。代码里记录了最后的 mean_value_loss, mean_surrogate_loss 作为“这次复盘学习”的指标,下次你可以继续追踪。

8. 总结

  1. 像学徒制作披萨一样:学徒(Actor-Critic 网络)在不同的状态(环境观测)做出不同决策(动作),得到不同反馈(奖励)。
  2. 像记录流水账:每一步操作都记录在 storage 中,以便后续反思与学习。
  3. 像定期复盘:等学徒完成一段时间练习,就进行一次PPO更新(update),在这过程中分析旧做法与新做法的差异,用“剪切”等安全措施避免学徒一下子完全推翻自己之前的经验。
  4. 持续循环:多次“做批量披萨→记录→复盘更新”,学徒的“脑子”慢慢就学会了做出更好的披萨,动作也更合理,分数越来越高。

前半部分在做“采集经验”和“记账”
后半部分在做“学习更新和改进策略”
用生活经验理解,就是一直在“做→记→评→学→再做→再评”的螺旋式改进过程。

<think>好的,用户现在希望以他们的四足机器人为例,详细讲解PPO算法的每个部分,并将每个部分与机器人的动作联系起来。之前已经解释了策略网络和价值网络的训练内容以及传感器数据的关联,现在需要更详细地分解PPO的各个组件,并将其具体应用到四足机器人的翻身和爬起任务中。 首先,用户可能对PPO算法的整体流程不太清楚,特别是每个步骤如何具体影响机器人的控制。需要将PPO的关键部分如数据收集、奖励函数、策略更新、Clipping机制等逐一拆解,并用机器人的例子说明每一步的作用。 接下来,用户可能希望了解每个算法部分如何与机器人的传感器数据和动作生成相关联。例如,数据收集阶段需要哪些传感器数据,奖励函数如何根据机器人的状态计算,策略网络如何生成动作指令等。需要确保每个部分都结合具体的机器人组件和动作,如IMU数据用于判断姿态,关节传感器用于调整动作输出。 还需要考虑用户可能存在的潜在问题,比如为什么使用Clipping机制,或者价值网络如何帮助策略网络优化。这些都需要在解释中自然地回答,同时保持技术细节的准确性,但避免过于复杂的数学公式,除非必要。 另外,用户之前提到过传感器包括足端力、关节传感器和IMU,所以在每个部分的应用中需要反复提及这些传感器数据的使用,例如在状态构建、奖励计算、动作执行等步骤中如何整合这些数据。 最后,需要确保整个解释结构清晰,分步骤说明,并可能用示例或伪代码辅助理解。同时,注意用户可能的实际应用场景,比如仿真到现实的迁移问题,或者如何处理传感器噪声,这些都可以在注意事项中提及,以增强回答的实用性。</think>好的!我将以您的四足机器人翻身/爬起任务为例,详细拆解PPO算法的每个组成部分,并说明其与机器人动作控制的具体关联。以下是分步解析: --- ### **一、PPO算法核心组件与机器人动作的对应关系** #### **1. 状态(State)与传感器数据** - **定义**:状态$s_t$是策略网络和价值网络的输入,描述机器人当前环境感知。 - **机器人中的具体构成**: ```python s_t = [ # IMU数据(本体状态) pitch, roll, yaw, # 本体姿态角 angular_vel_x, angular_vel_y, # 角速度 linear_acc_x, linear_acc_y, # 线加速度 # 关节传感器(每条腿3个关节 ×4条腿) joint_angle_1, joint_vel_1, torque_1, joint_angle_2, joint_vel_2, torque_2, ..., # 足端力传感器(4个足端) foot_force_1, foot_force_2, ..., # 附加信息(可选) time_since_fallen # 倒地后经过的时间 ] ``` - **关键作用**: - IMU判断是否成功翻身(如`roll > 90°`表示背部朝上) - 足端力检测触地情况,决定是否可发力蹬地 #### **2. 动作(Action)与运动控制** - **定义**:策略网络输出的动作$a_t$,直接控制机器人关节。 - **机器人中的具体形式**: - **连续动作空间**:输出每个关节的目标角度(或力矩) $$ a_t = [\theta_{FL\_hip}, \theta_{FL\_knee}, ..., \theta_{RR\_ankle}] $$ - **离散动作空间**(可选):选择预定义动作模式(如“蜷缩→蹬腿→伸展”) - **执行示例**: - 当策略网络输出“增大右前腿髋关节角度”,机器人会做出类似“蹬腿”动作辅助翻身 #### **3. 奖励函数(Reward)与任务目标** - **定义**:奖励$r_t$引导策略学习,需紧密匹配翻身/爬起任务需求。 - **机器人定制化奖励设计**: $$ r_t = w_1 \cdot e^{-|\text{目标姿态差}|} + w_2 \cdot \text{触地足数} - w_3 \cdot \|\text{关节力矩}\| - w_4 \cdot \text{能量消耗} $$ - **正向奖励项**: - 姿态接近站立状态(通过IMU计算姿态误差) - 足端触地数量多(通过足端力传感器判断) - **惩罚项**: - 关节力矩过大(通过关节传感器检测) - 总能耗高(通过电机电流估算) - 超时未完成(通过`time_since_fallen`计时) --- ### **二、PPO训练流程与机器人动作的交互** #### **1. 数据收集(与环境交互)** - **步骤**: 1. 机器人以当前策略$\pi_\theta$执行动作$a_t$(如尝试蜷缩后蹬腿) 2. 传感器记录下一状态$s_{t+1}$(如翻身后的新姿态) 3. 计算即时奖励$r_t$(如因姿态改善获得正奖励) - **关键细节**: - **动作限幅**:输出关节角度需限制在机械限位内 - **安全中断**:若检测到关节超限(通过关节传感器),立即终止当前回合 #### **2. 优势估计(Critic的价值评估)** - **定义**:价值网络$V_\phi(s_t)$评估当前状态的长期收益。 - **机器人中的应用**: - 当机器人处于“侧翻但三足触地”状态时,Critic会给出较高价值评分(因为容易发力翻身) - 若Critic发现“仰面朝天且无足触地”,则给出低价值评分(难以自主恢复) - **优势函数计算**: $$ A_t = \sum_{k=0}^{T-t} \gamma^k r_{t+k} + \gamma^{T-t} V_\phi(s_T) - V_\phi(s_t) $$ - 若机器人快速完成翻身($T$小),则$A_t$显著正向 #### **3. 策略更新(Clipping机制)** - **核心公式**: $$ L^{CLIP} = \mathbb{E}_t \left[ \min\left( \frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)} A_t, \text{clip}\left(\frac{\pi_\theta(a|s)}{\pi_{\theta_{old}}(a|s)}, 1-\epsilon, 1+\epsilon\right) A_t \right) \right] $$ - **机器人中的意义**: - 当旧策略让机器人“蹬腿力度较小”时,新策略尝试“增大蹬腿力度” - Clipping确保策略更新幅度不超过$\pm \epsilon$,防止单次更新导致动作突变(如从“轻蹬”直接变为“暴力弹跳”) #### **4. 价值网络更新** - **目标**:最小化预测价值与实际回报的误差 $$ L^{VF} = \mathbb{E}_t \left[ (V_\phi(s_t) - R_t)^2 \right] $$ - **机器人中的训练信号**: - 当机器人成功站起时,$R_t$包含最终成功奖励(如+100),Critic需学会提前预测这一高价值状态 --- ### **三、PPO超参数与机器人训练的关联** | 超参数 | 机器人任务中的影响 | 典型值建议 | |--------|--------------------|------------| | 折扣因子$\gamma$ | 决定未来奖励的重要性。<br>若翻身需快速完成(<5秒),设$\gamma=0.99$ | 0.95~0.995 | | Clipping范围$\epsilon$ | 控制策略更新幅度。<br>过大易导致关节抖动,过小收敛慢 | 0.1~0.3 | | 学习率(lr) | 影响网络权重更新速度。<br>复杂动作需更低学习率(如1e-4) | 1e-5~3e-4 | | 批量大小(batch_size) | 与传感器数据量相关。<br>高维状态(如40维)需更大批量 | 512~4096 | --- ### **四、实例:一次翻身动作的PPO决策流程** 1. **初始状态**:机器人背部着地(IMU检测到`roll=180°`),四足悬空(足端力≈0) 2. **策略网络决策**: - 输出动作:`[蜷缩腿部→向右扭转躯干→爆发式伸展后腿]`(对应关节角度序列) 3. **环境反馈**: - 新状态:`roll=120°`(部分翻身),右后足触地(力传感器>阈值) - 奖励计算:姿态改善(+2.3) + 1足触地(+0.5) - 关节力矩惩罚(-0.8) ⇒ $r_t=2.0$ 4. **Critic评估**:$V_\phi(s_t)=15.2$(比旧状态$V_\phi(s_{t-1})=8.7$显著提升) 5. **策略更新**:调整网络权重,使得“蜷缩→扭转→伸展”动作的概率增加,但受Clipping限制避免过度优化 --- ### **五、调试建议** 1. **动作失效分析**: - 若机器人反复“蹬腿但无法翻身”,检查: - 足端力传感器是否准确检测触地 - 奖励函数中是否缺乏对“躯干扭转角度”的奖励 2. **收敛加速技巧**: - 初期增加模仿学习(IL)引导,例如人工演示翻身动作 - 对IMU姿态误差使用课程学习(Curriculum Learning),从简单姿态逐步增加难度 如果需要更具体的某部分(如网络结构设计、奖励函数权重调参)的展开说明,可以告诉我! 🤖
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值