简介:这是一份面向机器人控制与前端JavaScript开发者的TensorFlow.js实验项目,重点演示六轴机械臂的强化学习流程。作者用乐高EV3砖块和伺服器搭建实体机械臂,并借助网页端AI训练模型,让模型自动旋转各轴,最终把机械臂尖端送到三维空间中的目标点。压缩包共十一个文件,约1.88MB,包含网页演示、脚本、配置文件、样式表、说明文档和三维模型;项目从10×10方格的二维路径规划起步,逐步升级到10×10×10的三维地图,以新位置与目标点的距离作为奖励信号,引导模型寻找最快路线。目前已有1261人学习下载。读者可从这套代码中看到完整的奖励函数设计、可视化交互页面和六轴机械臂简易模型,也适合想了解浏览器端强化学习、机械臂逆运动学或游戏AI路线规划的开发者参考。
1. 把强化学习跑通在6轴机械臂上:这份测试资源到底解决什么问题
接到这份 tensorflow-robot-arm 资源时,我第一反应是它把"6轴机器人手臂 + 强化学习测试"这条链路从零散的论文公式变成了能直接跑起来的训练工程。大多数想上手机械臂强化学习的人,卡点往往不在算法理解,而在于环境没人替你写好:状态向量怎么拼、奖励函数怎么设计、机械臂仿真器怎么交互、训练曲线怎么解读,这些琐碎工作在这份资源里被打包成了一整套默认可跑的测试框架。它解决的不是"让机械臂按轨迹动起来"的传统运动学问题,而是"让机械臂在仿真环境中通过试错学会接近目标点"的训练问题。适合三类人:刚接触连续控制、想复现 PPO 或 DDPG 效果的开发者;课程设计或模拟项目X里需要一个可演示试验台的在校生;以及想快速评估多种强化学习算法差异的一线工程师。
资源默认场景是一个桌面安装的 6 轴机械臂,末端执行器需要从初始姿态出发,接近一个在可达空间内随机生成的目标点,成功判据是末端与目标的三维距离低于阈值。训练端用 TensorFlow 实现,监控端用 JavaScript 写了一个浏览器里的 3D 回放台,可以逐帧观察机械臂每一个训练回合的实际动作。这份资源最值得抄的,是所有参数都给了默认值并且能跑出收敛曲线,你可以先跑通再逐项替换,不用从零去猜。
2. 机械臂建模先行:运动学、状态空间与动作空间的落地选择
把任何强化学习算法塞给机械臂之前,先把三件事定死:运动学用哪套、状态观测包含什么、动作输出怎么定义。这三项直接决定了后面训练曲线是平稳上升还是全程玄学。我拆这份资源时注意到它的代码顺序很讲究:先搭环境,再写算法,算法文件里甚至注释了"改环境请去 arm_env 目录"。
2.1 正运动学还是逆运动学:仿真里先算哪个决定训练能不能收敛
机械臂运动学分成两派:正运动学已知六个关节角度求末端位姿,逆运动学已知末端位姿反求六个关节角度。传统机械臂控制几乎离不开逆运动学,但在强化学习仿真里,我一般建议优先用正运动学,让智能体自己学着把状态映射成动作。原因有两个。
第一,逆运动学在高自由度机械臂上存在多解和奇异点问题。同一个末端位置可能对应多组关节角,IK 求解器返回的可能是离当前姿态很远的那组解,动作序列因此产生跳变,奖励信号里就混入了大量噪声,智能体很难学出平滑策略。
第二,强化学习的本质就是让策略网络隐式学出"从观测到动作"的逆映射。如果你每一步都先 IK 求逆再让智能体只输出一个姿态目标,相当于替智能体把最难的部分做了,它学出来的策略既理解不了关节空间,也没法迁移到结构稍有不同的机械臂上。
这份资源的环境层提供了一套标准 DH 参数,正运动学在一个文件里就能算完。值得注意的参数是各连杆长度 a1、a2、a3 和关节偏移 d1、d4,改这套参数等于换了一台不同的机械臂,观测维度和奖励逻辑都不用动。
| 对比项 | 正运动学方案 | 逆运动学方案 |
|---|---|---|
| 动作空间含义 | 关节角度或角度增量 | 末端位姿增量 |
| 奇异点影响 | 无,关节角始终合法 | 接近奇异位形时求解不稳定 |
| 多解跳变 | 不存在 | 常见,导致动作抖动 |
| 训练稳定性 | 较高 | 依赖求解器连续性 |
| 资源默认选择 | 是 | 否 |
2.2 状态空间设计:除了6个关节角,还得让智能体"看见"什么
最简单的状态空间就是六个关节角,但只给它角度,智能体既不知道目标在哪,也不知道关节正在往哪个方向转,等于让一个蒙眼的人去够东西。这份资源把观测拼成了一个 19 维向量,包含了关节角度、关节角速度、末端坐标、目标坐标和当前距离。
def build_observation(env, prev_action): # 6维:当前六个关节角度(弧度) joint_pos = env.get_joint_positions() # 6维:六个关节角速度(弧度/秒) joint_vel = env.get_joint_velocities() # 3维:末端执行器在基座坐标系下的笛卡尔坐标 ee_pos = env.get_end_effector_position()[:3] # 3维:目标点在基座坐标系下的坐标 target_pos = env.get_target_position() # 1维:末端到目标的欧氏距离,作为显式进度信号 dist = float(np.linalg.norm(np.array(ee_pos) - np.array(target_pos))) obs = np.concatenate([ joint_pos, joint_vel, ee_pos, target_pos, [dist] ]).astype(np.float32) return obs, {"distance": dist}joint_vel 这 6 维经常被新手省略,但它的作用很大。强化学习环境里每个决策步之间存在惯性,智能体只知道当前角度不知道角速度的话,就无法推断关节下一刻会滑向哪个位置,策略只能靠大量试错去盲猜。末端坐标和目标坐标各占 3 维,是为了让智能体不需要去记忆坐标变换关系;最后的 1 维距离是显式的进度信号,能让前几百回合收敛快很多。
我一般会在观测里保留距离这一项训练到基本收敛,然后再做一次消融测试,把距离那维去掉继续训练,对比去掉后的成功率是否有明显下降。如果差距在十个点以内,说明策略已经真正学会了利用坐标信息,而不是在偷懒地拟合距离值。这个习惯帮我避掉过几次"假聪明"策略。
2.3 动作空间两种定义:位置增量控制与力矩控制怎么取舍
机械臂连续控制里,动作空间的定义方式大致分成两类。一类是位置增量控制:智能体每步输出六个关节角度的增量,环境将当前角度加上增量后,交给底层的 PD 控制器去执行。另一类是力矩控制:智能体直接输出六个关节的力矩,环境把它作为电机扭矩施加到模型上。
位置增量控制和力矩控制的差异,在训练难度上非常明显。力矩控制更接近真实物理,但智能体必须额外学会重力补偿、摩擦补偿和惯性补偿,在仿真里想让一只悬空的机械臂稳定停在某个角度都很难,更别说够目标点了。位置增量控制把底层动力学问题交给 PD 控制器解决,智能体只需要关注"下一帧关节往哪转",学起来快得多。
这份资源默认采用位置增量控制,动作输出经过 tanh 压缩到 [-1, 1],再乘以 action_scale 得到最终的关节增量。默认 action_scale 是 0.1 弧度,也就是每一步最多转 5.7 度。这个值卡得比较保守,既给智能体足够的活动空间,又防止它一步把关节甩到限位。
如果你后面想把训练好的策略搬到真实机械臂上,常见做法是保留这套观测和动作定义,只把环境里的 PD 控制器替换成真实电机的位置环接口。真实机械臂的关节速度限制一旦比仿真里慢,就需要把 action_scale 调小到 0.03~0.05,否则真实动作会跟不上仿真节奏。
3. 算法选型与奖励函数设计:PPO与DDPG在6轴臂上怎么选、怎么配
环境定完之后才轮到算法选型。6 轴机械臂的强化学习测试属于典型的高维连续控制任务,动作空间是连续的,奖励信号是稠密的,可选的算法就那么几个。这份资源里默认实现了 PPO 和 DDPG 两套基线,切换只需要改一个启动参数。
3.1 连续控制算法的适用边界:三种基线算法的取舍
连续控制领域最常见的三个算法是 PPO、DDPG 和 SAC。三者各有明确的使用边界,没有哪一个能在所有场景里通吃。
PPO 是 on-policy 算法,用当前策略采样、用当前策略更新,每一步采集的数据只用一次。它的优势是训练稳定,对奖励函数的缩放不敏感,超参数稍微偏一点也能收敛,代价是样本效率低,想达到同样的成功率需要更多的交互回合。DDPG 是 off-policy 算法,所有历史数据都放进经验回放池里反复使用,样本效率高,但对超参数和观测归一化非常敏感,策略估值偏高的问题也容易导致训练后期突然崩溃。SAC 在 DDPG 的基础上引入最大熵正则,比 DDPG 稳定、样本效率也高,但需要同时维护两个 Critic 网络,训练开销更大,调试的复杂度也随之上升。
我的选型建议是:第一次复现这份资源时先跑 PPO,因为它曲线最不容易崩,能让你把注意力集中在环境正确性上。等 PPO 跑出稳定收敛的成功率之后,再切到 DDPG 对比样本效率。如果任务里需要在真实机械臂上直接部署,SAC 是更合适的方向,因为它学出来的策略通常对扰动更鲁棒。
| 算法 | 采样方式 | 样本效率 | 收敛稳定性 | 主要调参压力 | 资源默认 |
|---|---|---|---|---|---|
| PPO | on-policy | 低 | 高 | 学习率、clip范围 | 是 |
| DDPG | off-policy | 高 | 中低 | 噪声、观测归一化 | 备选 |
| SAC | off-policy | 高 | 中高 | 温度系数、双Critic | 未内置 |
3.2 奖励函数逐项拆解:到达步、接近步与惩罚项的配比
奖励函数是这份资源里最值得抄的部分之一。纯粹稀疏奖励只有到了目标给 1 分、没到给 0 分,前期探索期会非常长,网络很难从零开始稳定学习。这份资源采用稠密奖励,把奖励拆成了四个部分,每一部分的量级都经过了推敲。
def compute_reward(obs, action, info): dist = info["distance"] # 当前末端到目标距离 prev_dist = info["prev_distance"] # 上一帧末端到目标距离 reward = 0.0 # 1) 到达步:进入阈值范围,一次性奖励,事件信号 if dist < SUCCESS_DIST: # 阈值一般取 0.02~0.05 米 reward += 10.0 info["success"] = True # 2) 接近步:距离缩短给正奖励,拉远给负奖励 reward += (prev_dist - dist) * 2.0 # 3) 动作惩罚:抑制高频抖动和幅度过大 reward -= 0.01 * float(np.sum(np.abs(action))) # 4) 关节限位惩罚:由环境检测关节是否越界后置位 reward -= info["joint_penalty"] * 0.5 return reward到达步的 10.0 是整段训练里最大的一笔奖励,作用是为智能体指明"什么才算真正成功"。接近步的增益 2.0 提供连续梯度,让智能体在没到达目标之前也有正向反馈可以追。动作惩罚项的权重只有 0.01,它不会显著影响策略方向,但会抑制掉那些幅度特别大的抖动操作。关节限位惩罚 0.5 是为了避免智能体为了够到目标把关节顶死在限位上。
这里的配比关系有一条血泪经验:到达奖励的值至少要是接近步单帧增益的 10 倍以上。如果你把接近步调得太大,智能体很快就会学会"在目标附近绕圈"这个投机行为,因为每帧逼近一点都能拿奖励,远比真正到达目标划算;看起来成功率在涨,实际上末端一直在目标外围画圈。我曾在这个配比上翻过车,后来养成了一个习惯,单独打印每个奖励分量的累计值,一眼就能看出哪个分量在主导整个回合。
3.3 资源默认超参数一览:照着改的基线配置
这份资源把训练相关的超参数集中放在配置文件里,不散落在代码各处。我第一次运行前先过了一遍所有参数,大部分保持默认,只关注四个最关键的量。
| 参数 | 默认值 | 作用 | 调参优先级 |
|---|---|---|---|
| gamma | 0.99 | 折扣因子,决定远期奖励权重 | 通常不用动 |
| lam | 0.95 | GAE 平滑参数,影响优势估计 | 通常不用动 |
| learning_rate | 3e-4 | 网络更新步长 | 训练震荡时优先降 |
| clip_ratio | 0.2 | PPO 裁剪范围 | 收敛慢时可放宽 |
| batch_size | 256 | 每次更新的样本量 | 显存不足时减小 |
| update_epochs | 10 | 每个 batch 重复更新次数 | 过拟合时减小 |
| max_steps | 200 | 每个回合最大决策步数 | 目标太远时增加 |
| action_scale | 0.1 | 动作限幅 | 迁移到真实臂时减小 |
| noise_std | 0.1 | 探索噪声标准差 | 探索不足时增大 |
如果训练前几百回合的回报就出现剧烈震荡,优先把 learning_rate 降一个数量级到 3e-5,而不是去调网络结构。如果智能体到后期限位边上陷入局部最优,优先检查 noise_std 是不是已经衰减到 0.01 以下。max_steps 需要和仿真频率配合理解:默认 30Hz 的采样频率、200 步对应一个回合约 6.6 秒,目标点离初始位置太远时,这个时长不够用,把 max_steps 提高到 400 通常比调别的参数更有效。
4. 从零复现训练:环境搭建、脚本入口与JavaScript可视化监控台
理论部分讲完,进入动手环节。下面按我实际运行这份资源的顺序,把安装、配置、启动和监控四步拆开说清楚。
4.1 依赖安装与目录结构:这份资源的工作台长什么样
安装依赖建议使用独立虚拟环境,避免污染系统 Python。这份资源的核心依赖是 TensorFlow 2.x、numpy 和物理仿真器绑定库,requirements.txt 里已经列全。
# 创建并激活虚拟环境 python -m venv .venv source .venv/bin/activate # 安装全部依赖 pip install -r requirements.txt # 验证 TensorFlow 能正常完成一次计算 python -c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([2,2])).numpy())"如果没有 GPU,CPU 训练也能跑,只是时间长一些。6 轴机械臂这个任务网络规模不大,单个回合 200 步,CPU 训练一万回合大约需要一到两小时,可以先减小 total_timesteps 到 20 万步验证链路,再决定是否拉长训练。
装完依赖后建议先看一遍目录结构,这份资源的代码分层很清楚:
tensorflow-robot-arm/ ├── arm_env/ # 环境层:运动学、仿真交互、观测拼装 │ ├── kinematics.py # DH 参数与正运动学计算 │ ├── simulator.py # 仿真器封装,统一 step/reset 接口 │ └── reward.py # 奖励函数 ├── agents/ # 算法层 │ ├── ppo.py │ └── ddpg.py ├── configs/ # 参数文件 │ └── train_ppo.yaml ├── run_train.py # 训练入口 ├── monitor/ # JavaScript 可视化监控 │ ├── index.html │ └── viewer.js └── logs/ # 训练日志与模型权重环境层和算法层严格分离,改奖励函数只动 reward.py,改算法只动 agents 目录,这个设计对新手很友好。logs 目录会自动按回合生成 CSV 日志文件,后面要讲的 JavaScript 监控台就是靠读这些 CSV 文件来工作的。
4.2 训练入口与参数文件:第一次启动前要改的四个地方
训练入口做得比较薄,核心逻辑都在 agent 和环境里,入口只负责读配置、构建智能体、调 train 函数。
import yaml import argparse from agents import build_agent def main(): parser = argparse.ArgumentParser() parser.add_argument("--config", default="configs/train_ppo.yaml") parser.add_argument("--algo", choices=["ppo", "ddpg"], default="ppo") args = parser.parse_args() with open(args.config) as f: cfg = yaml.safe_load(f) agent = build_agent(args.algo, cfg["agent"]) agent.train(total_steps=cfg["env"]["total_timesteps"]) agent.save("logs/checkpoints/final.h5") if __name__ == "__main__": main()第一次启动前,我建议先改四个地方再做完整训练。第一,把 train_ppo.yaml 里的 total_timesteps 从 100 万改小到 20 万,先验证环境能跑通。第二,把 reward.py 里的 SUCCESS_DIST 确认一下,资源默认是 0.03 米,如果你想快速看到成功回合,可以先放宽到 0.05。第三,检查 config 里目标点采样范围,默认三个坐标轴的范围都要落在机械臂可达空间内,不然智能体永远够不着目标。第四,确认保存模型的间隔,资源默认每 2 万步存一次 checkpoint,训练中途崩了也有后悔药可以吃。
启动命令很简单,训练日志会实时打印到终端,模型和回合日志会写到 logs 目录:
python run_train.py --config configs/train_ppo.yaml --algo ppo4.3 用JavaScript写一个实时监控端:把训练日志变成可见的机械臂
训练曲线只能告诉你"数值在涨",却说不清机械臂实际在怎么动。这份资源在 monitor 目录里用 JavaScript 写了一个基于 WebGL 的轻量 3D 监控端,直接读 logs 目录下的回合 CSV,在浏览器里逐帧回放机械臂姿态,不需要额外安装客户端。
// viewer.js —— 读取训练日志,在浏览器里逐帧回放机械臂姿态 const frameRate = 30; // 与仿真步长一致 const csv = await fetch("logs/episode_027.csv").then(r => r.text()); const rows = parseCsv(csv); // 每行: t, j1..j6, x, y, z, tx, ty, tz function renderTick(i) { const arm = document.querySelector("#arm-3d"); // 用与训练环境同一套 DH 参数做正运动学 applyForwardKinematics(arm, [ rows[i].j1, rows[i].j2, rows[i].j3, rows[i].j4, rows[i].j5, rows[i].j6 ]); renderTarget(arm, rows[i].tx, rows[i].ty, rows[i].tz); } setInterval(() => { tick = (tick + 1) % rows.length; renderTick(tick); }, 1000 / frameRate);这个监控端最实用的地方在于 applyForwardKinematics 用的 DH 参数和训练环境完全一致,所以你看到的就是智能体真正执行的动作。在浏览器里跑起来只需要在项目根目录起一个静态文件服务,然后访问本机对应端口:
python -m http.server 8000浏览器打开 index.html 后,下拉选择任意一个回合的 CSV,就能看到完整动作回放。我复盘训练问题时基本不盯曲线,直接挑几个 success 和失败回合逐帧回放,末端绕着目标画圈、关节顶死、动作高频抖动这类问题一眼就能看出来,比看十条指标曲线都快。
5. 常见问题与避坑手册:6轴机械臂RL训练里的五个翻车现场
训练不收敛或者收敛之后表现怪异,大多数问题不在算法本身,而在环境和奖励的某个细节。这一章把我实际踩过的五个坑按现象、原因、解决的顺序写清楚。
5.1 智能体学会了"缩成一团":关节越界却照样拿高奖励
现象:训练几百回合后,机械臂不是伸向目标,而是把所有关节朝向同一个方向折叠,末端缩在基座附近,3D 回放里整条臂团成一团,但回报曲线还在缓慢上涨。
原因:状态空间里没有关节限位信息,奖励函数也没有对越界行为做惩罚。动作是增量式的,策略只要持续朝一个方向输出,关节就会慢慢顶到限位。智能体发现缩成一团时,如果目标恰好在这个方向上,距离确实在缩小,接近步奖励还能照常拿到。
解决:在环境 step 函数里检测六个关节角度是否超过限位,超限就返回 joint_penalty = 1.0,并在当回合提前终止。更稳妥的办法是额外把限位误差写进观测向量,让策略能感知到"再前一步就顶死"。我一般还会在 3D 渲染里把越界关节标成红色,肉眼立刻能看到风险姿态。
5.2 奖励在涨、末端不动:动作被限幅"卡死"的假性收敛
现象:平均回报稳步上升,但打开 3D 回放,末端几乎不动,目标点也没变,智能体像是在原地混时间。
原因:动作输出经过 tanh 之后再乘以 action_scale,如果 action_scale 设得偏小,策略任何输出都被限制在极小范围内;另一种可能是 PD 控制器的比例增益太低,关节实际角度根本追不上目标角度。两种情况下奖励上涨都是假象,智能体可能是在刷时间步奖励或者逃避超时惩罚。
解决:先查两个值:action_scale 是否在 0.05 到 0.1 之间,PD 增益 kp 是否能让关节在 0.1 秒内到达目标角度。我习惯写一个开环测试脚本,输入固定的关节增量动作,检查末端位移是否达到理论预期值。开环测试能过,再回去看策略输出。
5.3 训练前期剧烈震荡到崩溃:学习率与归一化同时出错
现象:前几千步回报还在正常范围,随后突然掉到负值,并且再也回不来,loss 曲线上出现明显尖刺。
原因:观测向量的数值尺度差异太大。关节角在 0 到 6 弧度之间,距离在 0 到 0.5 米之间,网络输入没做归一化,在反向传播时大数值维度会主导梯度更新。再加上学习率偏大,策略一步更新过猛,直接跨出了有效区域。
解决:对观测向量做 running mean/variance 归一化,或者简单地把角度除以 2π、把距离除以最大距离,统一压到 [-1, 1] 量级。同时把学习率先降到 1e-4,等前 10 万步稳定之后再调回去。这是这份资源里最容易踩的坑,P PO 对观测尺度比 DDPG 更敏感,没有归一化的 PPO 几乎必然震荡。
5.4 明明到了目标却判失败:碰撞检测与判定阈值的双重误伤
现象:3D 回放里末端已经贴上目标点,但训练成功率始终很低,很多回合以超时结束,日志里 success 标记为零。
原因:判定成功用的是末端执行器某个参考点到目标的距离,而回放里视觉上"贴上"的可能是另一段机构;另一类情况是仿真器的碰撞检测把末端和目标物体的接触当成了碰撞,提前终止了回合。
解决:先确认 SUCCESS_DIST 的参考点是不是末端坐标系原点,而不是机械臂某个 mesh 的中心点;再检查碰撞检测的碰撞体清单,把目标物体本身排除在碰撞结算之外。最有效的自查方法是写一个规则脚本,手动把末端放到目标点,直接调用环境的判定逻辑,看是否返回成功。这个自检脚本值得在改任何环境参数后都跑一遍。
5.5 训练收敛、实测乱抖:训练噪声与测试策略没做切换
现象:训练曲线很漂亮,成功率到了 90% 以上,但单独把模型拿出来回放,末端在目标附近高频抖动,动作序列里正负交替出现。
原因:训练时为引入探索给动作叠加了高斯噪声,噪声标准差没有随训练进行衰减;策略发现叠加噪声之后的动作依然能拿到高奖励,就把动作边界充分利用起来输出抖动。另一个诱因是动作惩罚项权重过低,网络没有动力输出平滑序列。
解决:在回放和部署阶段切换到确定性策略,也就是把探索噪声手动置零。PPO 直接把策略标准差缩到 0,DDPG 用 Actor 网络输出的均值作为动作。同时在训练奖励里保留动作惩罚项,专门抑制高频抖动。
6. 把"收敛"变成可量化指标:确定性评估循环的一个实用技巧
训练曲线好看不等于策略能用。我见过太多训练时成功率 90%、一关噪声就动作乱甩的模型,所以现在每次训练完都强制跑一遍确定性评估循环:固定随机种子,跑固定数量的回合,统计成功率、平均到位时间、关节越界率和末端稳态抖动。五个指标都达标,才敢说这个模型真的跑通了。
import numpy as np from arm_env import ArmEnv from agents import build_agent # 确定性模式:探索噪声置零,动作取策略均值 agent = build_agent("ppo", cfg, mode="test") successes = [] settle_steps = [] violation_rates = [] jitters = [] for seed in range(20): # 固定 20 个随机种子,保证结果可对比 np.random.seed(seed) env = ArmEnv(scene="random_target", seed=seed) obs = env.reset() dist_history = [] for step in range(200): action = agent.act(obs, deterministic=True) obs, _, done, info = env.step(action) dist_history.append(info["distance"]) if done: break successes.append(1 if dist_history[-1] < SUCCESS_DIST else 0) settle_steps.append(int(np.argmin(dist_history))) violation_rates.append(float(info["joint_penalty"])) jitters.append(float(np.std(np.diff(dist_history[-10:])))) print("success rate:", np.mean(successes)) print("avg settle step:", np.mean(settle_steps)) print("joint violation rate:", np.mean(violation_rates)) print("稳态抖动均值(米):", np.mean(jitters))四个指标各有门槛:成功率低于 80% 说明训练没真正收敛;平均到位时间如果超过 150 步,说明策略在目标附近反复试探,需要回头调整接近步奖励和动作惩罚的配比;关节越界率超过 5% 说明限位惩罚力度不够;末端稳态抖动超过 0.5 厘米,基本就是训练噪声没关干净或者动作惩罚太小。
要注意测试时的目标点分布比训练时略收窄一点,专门看策略在边界附近的余量。如果边界上的成功率掉得厉害,说明动作限幅对可达空间的覆盖设计有问题,回去把 action_scale 调大一点。这套评估循环还有一个隐藏作用:因为固定了随机种子,不同算法的评估结果可以直接横向对比,谁快谁稳一目了然。
以前我只看训练曲线,被假收敛坑过两次:一次是奖励在涨但机械臂在抖,一次是策略只在训练噪声掩护下才能站稳。从那以后每次训练完都强制走一遍这个确定性评估循环,五条指标不过关就不算跑通。希望帮到你。
本文还有配套的精品资源,点击获取