简介:这是一套基于NVIDIA Isaac Gym构建的机械臂深度强化学习框架,集成了SAC2019与从演示中学习的先进算法,主要面向计算机、自动化、电子信息等专业的毕业设计、课程设计或大作业场景。压缩包共1372个文件,约223.83MB,包含stl、obj、dae等三维模型文件,urdf、xacro机器人描述文件,py算法脚本,yaml/launch配置以及说明文档,可支撑从仿真环境搭建、模型训练到结果分析的完整流程。目前已有53人学习下载。项目代码均经过运行测试,配套项目备注和说明,可直接复用或二次开发,便于进行算法对比、超参数调试以及论文实验数据支撑。目录中还整理了多种机械臂模型与仿真环境配置,适合快速上手机器人操作任务,也能为项目初期立项演示提供完整参考。
1. Isaac-健身房里的机械臂强化学习:毕业设计到底在做什么
第一次把机械臂放进 Isaac-健身房 时,我最大的误判是以为难点在 SAC2019 或演示学习的公式推导。真跑起来才发现,难点在 Manipulator 环境的搭建:机械臂模型加载、末端坐标、奖励函数设计,任何一环错了,训练曲线都会归零。这个框架解决的是一个很实际的毕设诉求:在支持 GPU 并行的机器人仿真平台上,把机械臂的深度强化学习、SAC2019 算法、来自演示的强化学习三块串成一条可交付的链路。计算机、自动化、电子信息等专业做机器人方向的毕业设计或大作业,通常需要的是这一整套东西,而不是某个孤立的算法 demo。它适合你手里已经有一个 zip 工程、想搞懂框架怎么跑起来,也适合你自己从零组装一个同类框架。你拿到的是一个能训练机械臂抓取、能在 TensorBoard 里看到奖励上升、能导出训练好的策略权重用于后续验证的项目骨架。
2. 为什么在 Isaac-健身房里练机械臂:GPU 并行与 Manipulator 任务选型
2.1 并行环境数量能让训练曲线差多少
Isaac-健身房最核心的卖点是 GPU 并行。常见的机械臂抓取任务是每回合随机初始化机械臂和物体位置,智能体需要反复试错。同类 CPU 仿真器在单机上通常只能同时开几个环境,而机械臂任务又是回合制、每回合几十步到几百步,样本积累速度非常慢。Isaac-健身房把物理仿真搬到 GPU 上,一次可以创建几百到几千个环境,训练时所有环境同时推进,样本收集速度和奖励曲线稳定度都提升一个量级。对 SAC2019 这类 off-policy 算法来说,批量环境让回放缓冲区更快积累多样化数据,训练更容易收敛。
并行环境数量直接决定你的训练策略。我一般会先按显存大小估算环境数:场景里只有一个六轴机械臂和一个立方体时,几十个环境起步、上百个环境不卡是常见体验。环境太少,SAC2019 的样本多样性不足,奖励曲线会抖得很厉害;环境太多,又会把训练推进速度拖慢,并且回放缓冲区里的老样本长期得不到更新,策略容易出现过拟合。实际操作中,我会把环境数量当成一个和 batch_size 联动调参的变量:环境数增加时,batch_size 也可以适当放大,否则一次 update 用到的样本占比太低,更新方向会很噪声。
另一个容易被忽略的选型理由是这个平台原生支持关节位置控制、速度控制和力控制。Manipulator 任务最常用的就是位置控制,SAC2019 输出的动作经过缩放直接作为关节目标角度,物理引擎不需要额外做反向动力学,训练循环非常简单。做毕业设计时,我建议选择一个固定基座的六轴机械臂 URDF,不要上来就用灵巧手。灵巧手自由度多、状态空间大、奖励更稀疏,调试周期会成倍拉长。固定基座六轴机械臂的动作维度一般在 6 维左右,SAC2019 的双 Q 网络和 actor 都能轻松处理,而且抓取任务的效果展示非常直观。
2.2 URDF/资产加载:机械臂模型和抓取目标怎么放
Isaac-健身房 里的场景搭建通常分三步:创建 Sim、加载资产、把资产放进 Env。机械臂模型建议使用标准 URDF,模型中需要明确指定各个关节的 axis、limit、parent link 和 child link。最容易出问题的点是末端执行器的定义:很多 URDF 的最后一个 link 只是装饰,没有可抓取的几何形状,也没有用于计算奖励的坐标系。常见做法是在 URDF 末端额外加一个极小的球形 link,并通过一个固定关节接在腕部,这样训练代码可以直接读取这个末端 link 的位姿,作为观测和奖励计算的基准。
下面是一个最小的资产加载片段,这个写法不依赖外部配置,适合先跑通场景。
from isaacgym import gymapi def create_sim_and_assets(urdf_path, num_envs=512): gym = gymapi.acquire_gym() sim_params = gymapi.SimParams() sim_params.dt = 1.0 / 60.0 # 物理步长,抓取任务常用 60Hz sim_params.substeps = 2 # 每个渲染步内部做 2 次物理步进 sim_params.use_gpu_pipeline = True # 必须打开,否则退化成 CPU 仿真 sim = gym.create_sim(0, 0, gymapi.SIM_PHYSX, sim_params) asset_options = gymapi.AssetOptions() asset_options.fix_base_link = True # 底座固定,模拟机械臂安装在工作台上 asset_options.flip_visual_attachments = True arm_asset = gym.load_asset(sim, urdf_path, asset_options) envs = [] for _ in range(num_envs): env = gym.create_env( sim, gymapi.Vec3(-2.0, -2.0, 0.0), gymapi.Vec3(2.0, 2.0, 4.0), 1, # 每个 env 一个 actor ) pose = gymapi.Transform() pose.p = gymapi.Vec3(0.0, 0.0, 0.02) pose.r = gymapi.Quat(0.0, 0.0, 0.0, 1.0) arm_actor = gym.create_actor(env, arm_asset, pose, "arm", 0, 1) envs.append(env) return gym, sim, envsdt=1/60是机械臂仿真的常用频率,和真实控制周期接近。substeps=2用来提高碰撞稳定性和关节约束求解质量,抓取任务里夹爪与物体接触时如果不设 substeps,容易出现穿透。fix_base_link必须设置为 True,固定基座机械臂的底座一旦被物理引擎当作自由体处理,机械臂会在训练开始前就倒掉,奖励曲线自然全部归零。
加载目标物体时,我一般直接用gym.create_actor再加载一个立方体或者圆柱体资产,放在机械臂正前方随机位置。这里需要特别注意物体的尺寸和摩擦系数:物体太小,夹爪很难精确抓取;物体太大,即使夹爪闭合也会产生虚假接触。毕业设计里我通常选边长 4 到 6 厘米的立方体,桌面摩擦系数设在 0.5 到 0.8,这样成功率既不会低到无法展示,也不会高到像是记住单一轨迹。
2.3 训练曲线与任务成功率的对应关系
Manipulator 任务的奖励并不只是“靠近物体”。如果奖励只给末端与物体之间的距离,策略很容易出现原地抖动、反复伸爪但不闭合的毛病。做演示学习时,还会出现更隐蔽的问题:算法利用演示数据把末端推到物体附近,但夹爪闭合这个关键动作没学到。所以训练时必须同时记录两个指标:每个回合的平均奖励,以及回合结束时物体是否被夹离桌面。奖励上升不等于策略可用,只有成功率同时上升才说明 Manipulator 框架真正闭环了。
在 Isaac-健身房 里记录这两个指标不复杂。每步仿真结束后从刚体状态张量中取末端位置和物体位置,判断末端距离是否小于阈值,再取物体位置的高度判断是否被抬起。我要特别提醒:成功率计算不要用物体 z 值大于某个固定值这种单纯判断,因为机械臂可能把物体撞飞而不是抓起。更稳妥的做法是在回合结束前一刻夹爪已经闭合,并且物体位置 z 比初始高出至少一个物体尺寸以上。这个判定会被一次又一次地用到,值得一开始就写成独立函数。
3. 搭建 Manipulator 训练环境:从空场景到可交互的机械臂
3.1 最小可跑通的场景脚本
从空场景到可交互的机械臂,我建议按三步走:先创建场景,再写 reset,再写 step。不要在第一天就接强化学习,先手动给关节发一组目标角度,看机械臂是否按预期运动。下面这个脚本是环境类的最小骨架,覆盖了创建、重置和步进三个核心方法。
import torch from isaacgym import gymapi from isaacgym import gymtorch class ManipulatorEnv: def __init__(self, urdf_path, num_envs, device="cuda:0"): self.device = device self.num_envs = num_envs sim = self._init_sim() arm_asset = self._load_asset(sim, urdf_path) self.env_handles = [] lower = gymapi.Vec3(-2.0, -2.0, 0.0) upper = gymapi.Vec3(2.0, 2.0, 4.0) for i in range(num_envs): env = sim.create_env(lower, upper, 1) pose = gymapi.Transform() pose.p = gymapi.Vec3(0.0, 0.0, 0.02) pose.r = gymapi.Quat(0.0, 0.0, 0.0, 1.0) arm = env.create_actor(arm_asset, pose, "arm", 0, 1) self.env_handles += [env] self.sim = sim self.dof_num = 6 def reset(self): # 重置所有机械臂到固定初始姿态,物体位置随机化 init_joint_pos = torch.zeros( (self.num_envs, self.dof_num), dtype=torch.float32, device=self.device ) # 常见做法:把非原点关节补一个初始偏置,避免奇异位形 init_joint_pos[:, 2] = 0.5 # set_dof_state 相关接口写回设备张量 self._set_initial_state(init_joint_pos) return self._get_obs() def step(self, actions): actions = torch.clamp(actions, -1.0, 1.0) scaled_actions = actions * 0.2 # 缩小到 0.2 rad 量级 self._set_joint_position_target(scaled_actions) self.sim.step() # 推进一个物理步 return self._get_obs(), self._get_reward(), self._get_done()代码里eval和reset被简化成三个_set_方法,实际工程里它们分别对应gym.set_actor_dof_states和gym.set_dof_position_targets这类接口。这样写的目的是先让你理解控制流:reset 负责把机械臂和物体放回随机起始状态,step 接收策略输出的动作、把动作映射成关节目标、推进仿真、再返回观测和奖励。
动作缩放这一步非常重要。SAC2019 的 actor 输出通常会被约束在tanh的范围,也就是约[-1, 1],但机械臂每个关节的物理限位并不相同:第一个关节可能允许正负 180 度,末端关节可能只有正负 60 度。如果直接把[-1, 1]当作关节角度,动作稍微抖动就会打到关节限位,机械臂表现就是剧烈抽搐。我通常的做法是让每个关节单独使用自己的缩放系数,幅度设为最大允许角度的二分之一左右,留下安全的余量。
3.2 动作空间、观测空间与奖励函数的三个必调参数
动作空间我建议使用关节相对位置增量。也就是每一步输出的动作是“在这个控制周期内关节目标角度相对当前位置的增量”,而不是绝对目标角度。相对位置增量可以让策略更容易学出“当前角度越接近期望位置,动作量越小”这种自然趋势,换到绝对位置坐标时,一旦机械臂初始姿态变化,策略就很容易失效。
观测空间至少包含三部分:关节位置和速度、末端执行器的位置和姿态、目标物体的相对位置。很多人只给关节状态,结果策略学到一团乱麻。原因是机械臂抓取这个任务里,奖励大部分来自末端和物体的位置关系,如果观测里没有这些信息,算法只能靠长时间的历史状态隐式猜测,训练效率很低。我会把观测拼成一个一维向量,再做一个 running normalization。SAC2019 对观测尺度很敏感,关节角度和物体距离不在一个量级时,Q 网络更新会很不稳定。
奖励函数建议按三个目标拆开写:
def compute_reward(delta_dist, success, action_penalty): # 缩短距离:这一步相对上一步靠近目标物体的距离差 reach_reward = 10.0 * delta_dist # 成功抓取:回合结束时物体被夹离桌面 success_reward = 50.0 if success else 0.0 # 动作惩罚:惩罚高频抖动和过大的关节变化 penalty = 0.02 * action_penalty return reach_reward + success_reward - penaltydelta_dist是上一步末端到物体的距离减去当前距离。距离缩短为正、变远为负,这个 shaping reward 让智能体在最开始也能收到密集反馈。success_reward只在回合结束时给,用来强调最终目标。action_penalty一般使用动作平方和或相邻两步动作差的平方和。三个数值里最需要调的是reach_reward的系数和success_reward:成功奖励太小,策略满足于靠近物体不抓取;成功奖励太大,策略可能故意用撞击物体来获取奖励,而不是真的夹取。我一般先固定reach_reward=10.0,然后让success_reward在 30 到 100 之间扫几组。
不要只看单步奖励总和,要多看几条累计奖励曲线的形状。如果奖励曲线快速上升后突然停顿,通常不是算法坏了,而是 reaching 已经完成、grasping 还没开始。这时提高success_reward,或者把夹爪是否闭合放进观测,往往能解决。
4. 把 SAC2019 和演示学习塞进框架:算法侧的关键改动
4.1 SAC2019 的核心更新点:自动熵调节与两个 Q 网络
SAC2019 相比早期 SAC 最重要的改进是自动熵调节。熵系数alpha不再是一个需要手动反复试的常量,而是通过梯度更新自动调整,让策略尽量满足给定最小熵的同时最大化回报。对机械臂抓取这种多模态任务,这个特性非常有用:抓取路径往往不是唯一的,策略如果过早确定成一条路径,遇到物体位置变化就失败;自动熵让策略保持一定随机性,探索阶段更长。
实现时建议严格按照标准更新式,用两个 Q 网络的最小值来近似目标 Q 值,并给 Q 网络使用索引更新方式传来的目标网络。下面这段代码是 loss 计算的核心部分:
with torch.no_grad(): dist = actor(next_state) next_action = dist.rsample() log_prob = dist.log_prob(next_action).sum(dim=-1) entropy = -log_prob q1_target = qnet1_target(next_state, next_action) q2_target = qnet2_target(next_state, next_action) q_next = torch.min(q1_target, q2_target) - alpha * entropy q_target = reward + gamma * (1 - done) * q_next # alpha loss: 让平均熵逐渐贴近 target_entropy alpha_loss = -(log_alpha * (entropy - target_entropy).detach()).mean() alpha = log_alpha.exp() # Q loss q1_loss = F.mse_loss(qnet1(state, action), q_target) q2_loss = F.mse_loss(qnet2(state, action), q_target)代码里target_entropy通常是负的动作维度数,也就是-action_dim。比如六自由度机械臂设置为-6。这个值不是玄学,它表示我们希望策略在后续每一步保留的信息量大致相当于 6 维动作的基本熵;如果动作维度更高,目标熵绝对值也要更大。alpha用一个log_alpha参数来表示,是为了确保它永远为正。
双 Q 网络是另一个关键点。只用单个 Q 网络时,Q 值容易被高估,策略会选择那些被高估的动作,导致训练中期突然崩溃。两个 Q 网络独立输出、取最小值作为目标,可以有效压缩高估偏差。做毕业设计时不要贪多去加第三第四个 Q 网络,收益很小,训练开销却明显增加。
4.2 来自演示的强化学习:回放缓冲区预填充还是额外策略约束
“来自演示的强化学习”在机械臂任务里通常有两种落地路线。第一种是预填充回放缓冲区,把专家演示切分成一条条 transition,放进普通 SAC2019 的回放缓冲区里一起训练。第二种是额外加一个判别器,让判别器区分演示样本和策略样本,并把判别器输出当作奖励的一部分,这类方法接近 GAIL。毕业设计时间有限,我强烈建议先用第一种。
演示数据的核心价值是告诉策略“哪些状态-动作是合理的”,让训练早期不会从一个完全随机、乱抖、撞物体的策略开始。做法是在训练启动前把演示轨迹转成四元组(state, action, reward, next_state, done),然后按比例混入采样。常见比例是演示样本占每个 batch 的 10% 到 30%。比例过低时演示样本被淹没,策略还是从零开始;比例过高时策略会过度模仿演示,失去探索新轨迹的能力。
下面是把演示样本混入训练 batch 的核心逻辑:
# replay_buffer 中同时记录 is_demo 标记 rl_batch = replay_buffer.sample(batch_size - demo_size) demo_batch = replay_buffer.sample_demo(demo_size) def merge_batch(rl_batch, demo_batch): state = torch.cat([rl_batch.state, demo_batch.state]) action = torch.cat([rl_batch.action, demo_batch.action]) reward = torch.cat([rl_batch.reward, demo_batch.reward]) next_state = torch.cat([rl_batch.next_state, demo_batch.next_state]) done = torch.cat([rl_batch.done, demo_batch.done]) return Batch(state, action, reward, next_state, done)demo_size我一般取batch_size // 4,也就是四分之一。这个比例的另一个作用是防止回放缓冲区里老策略产生的样本与演示样本分布差距过大,否则 Q 网络会学出一种矛盾的目标。训练过程中可以逐步降低demo_size,让策略在后期完全靠自己学习,这样更容易看出去掉演示后策略是否真的稳定。
还有一点需要注意:演示样本里的奖励必须和环境本身定义的奖励一致。如果你在生成演示时用了额外的辅助奖励,比如每一步都给“靠近物体 +1”,而训练时环境不提供这个奖励,策略就会在演示区域附近产生严重的奖励偏差。我通常会把演示轨迹生成时的奖励全部清零,只保留成功奖励和 shaping reward 中的距离差部分,并让它们与环境在同一个评估函数下计算。
4.3 训练入口与实验记录的落地写法
项目打包成 zip 交给老师或同学时,训练入口必须是一个命令能跑完的脚本。我习惯把配置独立成 YAML,算法代码和场景代码分开,训练入口只负责加载配置、构建环境、创建 buffer 和循环训练。
python train.py \ --config configs/arm_sac_rlfd.yaml \ --num_envs 512 \ --headless \ --logdir runs/arm_sac_rlfd对应的configs/arm_sac_rlfd.yaml里至少应该包含这些字段:
env: urdf_path: "assets/arm/robot.urdf" num_envs: 512 action_scale: [0.2, 0.2, 0.2, 0.1, 0.1, 0.1] success_reward: 50.0 sac: batch_size: 512 buffer_size: 1000000 gamma: 0.99 tau: 0.005 alpha_lr: 0.0003 q_lr: 0.0003 actor_lr: 0.0003 demos: path: "data/demos_arm_v1.npz" demo_ratio: 0.25这些参数不是拍脑袋定的。gamma=0.99是机械臂回合任务里的常见值,回合一般几十到几百步,0.99 可以兼顾短期和长期奖励。tau=0.005控制 target 网络跟随速度,太快会使 target 更新过于频繁,影响稳定性;太慢会让训练前期目标移动过慢。三个学习率都设成3e-4是 SAC 系算法的典型起点,不要为了“更快”而调大,Q 网络很容易发散。
训练过程中一定要落日志。至少保存三类数据:每个 step 的平均奖励、每个 step 的成功率、当前alpha值。如果alpha一直不降或者一直停在极大值,通常说明奖励尺度有问题,算法需要靠随机动作才能拿到正奖励。TensorBoard 的标量图里同时看这三条曲线,能很快定位是场景 bug 还是算法 bug。
5. 实战避坑:机械臂训练不收敛的常见问题与排查路径
5.1 机械臂原地抖动,奖励不涨
现象:训练开始后很多环境里的机械臂在原地小幅反复颤动,目标物体纹丝不动,奖励曲线在一个水平线上震荡。
原因:动作尺度太大或者动作惩罚太小。SAC2019 早期探索阶段的动作充满噪声,如果把[-1, 1]直接映射到关节极限角度,每步关节变化可能超过 1 弧度,物理引擎会把机械臂“弹”到限位位置,随后传感器数据出现跳变,策略很难收敛。另一个原因是训练用了绝对角度目标,而动作噪声产生的角度差值较大。
解决:把动作输出先缩放到最大关节角度的 20% 左右,并且在前 1000 步内不要用太小的动作惩罚。如果已经抖动,先把action_scale调小到 0.05,确认机械臂能稳定停在目标位置,再逐步放大。我还习惯在观测里加入关节角速度,这样 Q 网络能学出“动作过大时角速度也异常”的相关性,会更早学会平缓控制。
5.2 演示数据进去反而学坏
现象:加入演示数据后,刚开始奖励上升很快,但训练一段时间后突然掉头向下,生成的动作明明很接近演示,却抓不起物体。
原因:演示数据的生成方式与当前环境奖励不一致,或者演示样本占比太高。比如你是用正运动学手写的路径生成演示,但机械臂初始位姿随机化程度超过演示路径覆盖范围,策略只能在这些状态上模仿,无法泛化。另一个常见原因是演示样本里的动作没有经过与真实环境相同的缩放,出现“动作尺度不匹配”。
解决:先关掉演示样本单独训练 1 万步,确认环境本身没问题,再加入演示。加入时让demo_ratio从 0.1 开始,每 5 万步衰减一半。同时检查演示数据的动作分布,如果某些关节动作值集中在极端值附近,说明演示生成时没有加噪声或者动作限位设置不对。更稳妥的做法是只把演示样本预填充到回放缓冲区,前 1000 步正常训练后就让它们自然过期,不长期混入采样。
5.3 多进程与 GPU 显存问题
现象:训练脚本在本地能跑,但到了远程无显示器机器上一启动就崩溃,提示物理设备无法初始化。或者num_envs调到 1024 后,显存占用直接溢出,系统卡死。
原因:Isaac-健身房 在无显示环境下必须启用 headless 模式,否则图形设备初始化失败。而显存溢出通常不是 GPU 显存本身不够大,而是回放缓冲区、PyTorch 网络参数、仿真张量全部挤在同一个 GPU 上。我见过很多同学把num_envs=1024写死,结果 8G 显存只有 2G 可用。
解决:远程训练时启动命令必须加上--headless,并显式指定CUDA_VISIBLE_DEVICES=0。显存不足时不要盲目加大 batch_size,先把num_envs降到 256,确认单卡能正常训练再逐步加。另外,把回放缓冲区放在 CPU 内存,只在采样时才异步搬到 GPU,可以明显缓解显存压力。如果训练过程中出现突然掉帧或者卡顿,优先检查是不是回放缓冲区被反复拷贝导致的 CPU-GPU 同步瓶颈。
5.4 仿真里成功率很高,实际部署到真机上却很烂
现象:仿真成功率超过 90%,换成真机测试时机械臂要么找不到物体,要么夹爪触碰物体前就会把物体推走,一切从零开始。
原因:仿真里没有做域随机化。真实环境的光照、相机噪声、物体摩擦、关节摩擦和控制延迟都无法通过固定仿真参数完全模拟。另一个原因是训练时对象位置是均匀随机生成的,而真机上的物体摆放范围更窄,策略生成的路径可能存在大量急停,真机控制周期一旦跟不上就会失稳。
解决:如果你只做毕业设计,可以把“真机部署”定位成开放讨论,不要求完全迁移。如果必须演示,可以在训练时给物体的摩擦系数、几何尺寸加扰动,给关节目标角度加延迟,并在评估时使用与训练不同的物体位置。更简单的做法是让机械臂末端速度限制在一个比较低的范围内,换取更稳定的轨迹。不要追求仿真 100% 成功率,最后过拟合到仿真环境的策略,往往真机一碰就翻车。
6. 用一组固定配置复现 SAC2019 + 演示学习:验证与下一步
先给一组我自己常用的可复现配置,适合六轴机械臂抓取任务。动作维度 6,观测维度 12 左右,并行环境 512,batch_size 512,初始demo_ratio=0.25,每 5 万步衰减一半,训练总步数 50 万步。奖励参数使用第 3 章的三项式,reach_reward=10.0、success_reward=50.0、action_penalty=0.02。在这个配置下,我通常在前 5 万步看到奖励快速上升,10 万步左右出现第一次成功抓取,20 万步后成功率逐渐稳定。
验证时不要只看训练日志,每一步都会调用 eval 函数,专门记录成功抓取的比例。更直观的验证是多录制几段回合视频:让机械臂从不同初始位置开始,观察它是否先靠近、再夹紧、最后抬起来。这里有一个容易被忽略的细节:夹爪接近物体时,如果视频里出现反复接近又缩回的动作,说明策略在利用距离 shaping reward 而不是真正在抓取,需要把动作惩罚调高。
下一步如果你要在这个框架上继续做深化,我建议做两件事。第一,把演示数据换成更合理的规划器自动生成,而不是手写轨迹;这样能节省大量手调时间,也更容易写成可复现的毕设步骤。第二,把随机化加到环境里,物体位置、物体摩擦力、关节初始姿态都做随机,训练完成后画一张不同泛化测试下的成功率表。这张表和训练曲线一起放进论文,比单纯贴一个最终奖励值有说服力得多。
我踩过最狠的坑就是第一版用绝对关节角度做动作空间,模型学得又快又好,但换一个初始姿态就全部失灵。后来我改成相对增量动作,训练速度虽然慢了一点点,但策略泛化能力立刻上来了。现在每次换机械臂模型,我都会先跑一个固定初始位形的 100 次验证,确认动作空间和奖励函数都没有问题,再开完整训练。希望帮到你。
本文还有配套的精品资源,点击获取