拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

六轴机械臂强化学习实战:PyBullet与TensorFlow实现PPO控制

六轴机械臂强化学习实战:PyBullet与TensorFlow实现PPO控制

简介:这是一个面向前端 AI 与机器人爱好者的 6 轴机械臂强化学习测试项目,使用 Tensorflow.js 在浏览器中训练模型,目标是让乐高 EV3 砖块与伺服器组成的机械臂能自动旋转各轴、抵达指定的三维空间点。压缩包共 11 个文件,以 HTML 页面、JavaScript 脚本、JSON 配置、GLB 三维模型及 Markdown 说明为主,整体仅 1.88MB,方便快速部署和阅读。项目从 2D 正方形地图起步,逐步扩展到 10×10×10 的 3D 地图,并采用基于新位置与目标点距离的奖励机制来引导模型寻路,两个阶段的测试代码都包含在内。对于想了解前端强化学习、机器人逆运动学与简单路径规划的开发者,这份资源提供了清晰可运行的实验范本。目前已有 1261 人浏览学习,适合作为入门调试与二次开发的参考。

1. 6轴机械臂强化学习测试:tensorflow-robot-arm 的第一步不是算法

tensorflow-robot-arm 这个项目,本质上是 6轴机械臂强化学习测试的标准解法:把 TensorFlow 的深度强化学习栈、PyBullet 仿真器和一个 UR 系列机械臂模型接在一起,解决一个很具体的问题——在没有真机、或者不敢拿真机乱试的情况下,怎么让机械臂自己学会把末端送到目标点。起步时最容易低估的是网络结构,真正花时间的是仿真环境里状态怎么取、动作怎么给、奖励怎么设。适合两类人:做机械臂运动规划想引入学习方法的工程师,以及刚接触强化学习、想找一个带关节限位和高维动作空间任务的算法学习者。

2. 先把仿真环境立起来:PyBullet + TensorFlow 2 的最小训练底座

2.1 为什么是仿真先行而不是真机直训

真机直训有个很难跨过去的坎:采样慢。6轴机械臂一个 episode 要在真实时间里跑完,通常要 3~5 秒,而强化学习动辄需要几十万步交互,算下来一周的数据量在仿真里一个晚上就采完了。机器本身也扛不住连续高频动作,关节磨损和意外碰撞的代价足够让项目预算失控。所以这个标题里的“测试”二字,我理解成先仿真验证可行性,再谈真机迁移。

选仿真环境时我一般看三样东西:能不能直接加载 URDF、关节级控制接口是否暴露、训练时能不能关掉渲染跑后台。后两点决定你从第一天到跑出第一个奖励曲线要花多久。常见做法是先用 PyBullet 把环境跑通,理由很朴素:它不需要额外建模格式,pybullet_data 自带了 UR5 模型,装完就能动。

2.2 环境选型:PyBullet、MuJoCo 与 Isaac Gym 怎么取舍

环境授权与安装仿真精度训练速度与 URDF 的匹配度上手成本
PyBulletpip 直接装中,满足运动规划级验证CPU 可跑,单进程原生 URDF最低
MuJoCo需要下载激活,学术免费高,接触和摩擦模型更细CPU 可跑,单进程需要转换或写 MJCF中
Isaac Gym需要 NVIDIA 显卡和独立安装中高GPU 并行,几十倍加速支持 URDF 但有额外选项偏高

对 tensorflow-robot-arm 这种“测试”定位,PyBullet 是最稳的起点。Isaac Gym 适合已经跑通一版、要大规模扫超参时再上;MuJoCo 的动力学精度更适合抓取和接触任务,但前期调试成本高。选模拟器不是越精确越好,能让你在三天内产出第一版收益曲线的,才是当下正确的。这是我反复验证过的一条血泪经验:选环境太纠结,会卡住整个项目一周。

还要提一下仿真时间步长。PyBullet 默认的 stepSimulation 每步 1/240 秒,强化学习训练时不需要这么高的控制频率,常见做法是每 3~5 个仿真步取一次观测、发一次动作,这样既能模拟真实控制器的采样周期,又能显著减少训练循环里的 Python 调用次数。这个控制频率本身就是后面要记录的超参,仿真里 30Hz 对应真机上可接受的 25~50ms 控制周期。

2.3 最小可运行环境:加载 UR5、控制单关节、读取末端位姿

先装依赖。TensorFlow 这里注意版本和平台,建议直接用 pip 装 2.x 稳定版,再补一个 tensorflow-probability,后面 PPO 的动作分布要用它。

pip install tensorflow pybullet tensorflow-probability

安装完成后先做一个连通性验证,目的是把模拟器、URDF 和 TensorFlow 三条线同时确认一次:

import pybullet as p import pybullet_data import tensorflow as tf p.connect(p.DIRECT) p.setAdditionalSearchPath(pybullet_data.getDataPath()) p.loadURDF("plane.urdf") print("pybullet ok") print("tensorflow", tf.__version__)

p.connect(p.DIRECT) 是不开 GUI 的纯后台模式,训练时一直用它;刚开始想肉眼看机械臂,把 DIRECT 换成 GUI 再跑一次即可。setAdditionalSearchPath 指向 pybullet_data 的模型目录,UR5 的 URDF 就从这个包里加载。这一步失败 90% 是 pybullet_data 没装或者路径没设置,报错里会出现找不到 ur5/ur5.urdf,解决方式是检查包安装位置,把 getDataPath() 的结果打出来确认文件存在。

接着加载 UR5 并确认关节索引:

robot_id = p.loadURDF("ur5/ur5.urdf", basePosition=[0, 0, 0]) for joint_index in range(p.getNumJoints(robot_id)): info = p.getJointInfo(robot_id, joint_index) joint_name = info[1].decode() joint_type = info[2] if joint_type == p.JOINT_REVOLUTE: print("revolute joint", joint_index, joint_name) p.setJointMotorControl2( robot_id, joint_index, p.VELOCITY_CONTROL, force=0 )

最后一行是很多新手漏掉的细节。PyBullet 加载 URDF 后每个关节默认有马达驱动,不主动关掉,后面用 POSITION_CONTROL 时会和默认马达打架,表现成机械臂自己往下垂还压不住。把速度控制模式的 force 设成 0 相当于解除默认驱动,之后的位置控制才能接管。UR5 的 6 个旋转关节索引一般在 1~6,但这个编号依赖 URDF 内部定义,直接打印出来确认最可靠,拿到的索引列表要在整个训练脚本里复用,存成常量。

做完这步,验证一下位置控制能和关节索引真正配合:

target_position = p.getJointState(robot_id, 0)[0] + 0.3 p.setJointMotorControl2( robot_id, 0, p.POSITION_CONTROL, targetPosition=target_position, maxForce=100.0, ) for _ in range(240): p.stepSimulation() time.sleep(1.0 / 240.0)

maxForce 是关节驱动最大力矩,UR5 有点力气,但别直接给满,第一轮验证给 100 左右足够。240 步对应 1 秒仿真时间,机械臂应该能看出明显但平缓的移动。如果关节纹丝不动,优先检查是不是上一步的默认马达没关。这一步验证通过后,环境底座就立住了,下一步要在这个底座上定义强化学习真正关心的三样东西:状态、动作、奖励。

3. 状态、动作与奖励设计:6轴机械臂强化学习的第一道坎

3.1 状态空间:只用关节角还是把末端和目标坐标也喂进去

很多强化学习入门项目拿 CartPole 只需要两三个数值,但 6轴机械臂不一样:单看关节角,策略根本不知道末端在哪儿;单看末端位置,策略又不知道当前构型、没法做出一步合理动作。常见做法是把两者都放进去,再加目标坐标,形成一个稠密观测。

我常用的一组状态是:关节角 6 维 + 关节角速度 6 维 + 末端位置 3 维 + 目标位置 3 维,共 18 维。关节角速度看起来是冗余的,但它对抑制策略输出的高频抖动很有帮助——没有速度信息,PPO 很容易在相邻两步输出方向相反的关节命令,表现在真机上就是抖。目标位置直接用世界坐标,训练时在可达工作空间内随机采样。

向量各维度的尺度差异也要处理。关节角范围接近正负 π,末端坐标大约在正负 1 米,直接喂网络的话,数值大的维度天然主导梯度。最简单的做法是都归一化到 -1~1 之间:

import numpy as np import pybullet as p JOINT_INDICES = [1, 2, 3, 4, 5, 6] END_EFFECTOR_LINK = 6 def get_obs(robot_id, target_pos): states = p.getJointStates(robot_id, JOINT_INDICES) angles = np.array([s[0] for s in states]) velocities = np.array([s[1] for s in states]) end_pos = np.array(p.getLinkState(robot_id, END_EFFECTOR_LINK)[0]) target_pos = np.array(target_pos) obs = np.concatenate([ angles / np.pi, velocities * 0.1, end_pos / 2.0, target_pos / 2.0, ]) return obs.astype(np.float32)

角速度乘 0.1 是因为真实关节速度经常到 1~5 rad/s,直接放进状态会把距离信号淹没;末端坐标除以 2.0 是假设工作空间半径约 1 米,把最大范围当成归一化常量。这两处缩放看着不起眼,实际对早期训练稳定性影响很大。等你做到抓取任务时,还要把末端姿态的四元数或者欧拉角加进来,但“先做点到点,再做姿态”能省掉大量调试时间。

3.2 动作空间:位置控制下的增量动作与限位处理

6轴机械臂的动作空间有两种主流设计。一种直接输出 6 个关节的目标绝对角度;另一种输出关节角增量。绝对角度的优点是策略每一步都给出明确终点,缺点是网络要同时学会“在哪里”和“去哪里”,探索初期容易把关节一下甩到限位。增量动作每次只改一点,天然带有平滑性,也更符合位置控制器的使用方式。

我一般选增量动作,并且把动作向量每个分量限定在 -1~1 之间,乘上一个步长系数后加到当前关节角上。动作网络的输出层用 tanh 自然满足这个边界:

def apply_action(robot_id, action, joint_indices, step_scale=0.1): action = np.clip(action, -1.0, 1.0) states = p.getJointStates(robot_id, joint_indices) current_positions = np.array([s[0] for s in states]) target_positions = current_positions + step_scale * action for idx, target in zip(joint_indices, target_positions): p.setJointMotorControl2( robot_id, idx, p.POSITION_CONTROL, targetPosition=target, maxForce=200.0, )

step_scale 是这组代码里最重要的参数。0.1 表示每步最大关节角变化约 0.1 弧度,也就是 5.7 度左右,这个量级下末端移动平滑,也留了足够的探索空间。把 step_scale 调到 0.3,训练前期会频繁撞限位;调到 0.03,策略学得稳但收敛明显变慢。常见做法是先 0.1 起步,观察曲线后再按情况调整。这里用的是位置控制,所以不需要反向动力学;如果换成力矩控制,动作就成了 6 维力矩,模型训练难度会高一整个量级,不在“测试”阶段的考虑范围内。

3.3 奖励函数:dense 引导加 sparse 到达,再加三项惩罚

奖励设计是机械臂强化学习里最像玄学的部分,但底层逻辑是清楚的:要让“靠近目标”和“真正到达”都能被策略感知。只用距离惩罚,策略会找到“少动少扣分”的偷懒解;只用到达奖励,几十万步里成功样本太少,几乎学不动。

所以我采用混合结构:距离越近奖励越高,这是 dense 部分;成功到达时给一个较大的正向脉冲,这是 sparse 部分;再叠三项惩罚:动作变化过大的平滑惩罚、关节角接近限位的惩罚、动作过大导致的震荡惩罚。代码如下:

def compute_reward(obs, action, prev_action, reach_threshold=0.05): end_pos = obs[12:15] * 2.0 target_pos = obs[15:18] * 2.0 distance = np.linalg.norm(end_pos - target_pos) reward = -1.0 * distance if distance < reach_threshold: reward += 10.0 smooth_penalty = 0.1 * np.linalg.norm(action - prev_action) reward -= smooth_penalty joint_angles = obs[0:6] * np.pi limit_penalty = np.sum(np.abs(joint_angles) > (np.pi * 0.9)) reward -= 1.0 * limit_penalty return reward

reach_threshold 设 0.05 米,对 UR5 这类臂长来说已经算严格到达,真机上这对应末端定位误差 5 厘米以内。sparse 奖励给 +10,是单步距离惩罚最大值的几倍,能让策略明显感知“这一步做对了”。如果发现成功事件没让曲线产生尖峰,把 +10 提到 +30 也不奇怪——这个系数要和距离惩罚的尺度匹配,唯一原则是成功脉冲一定显著高于日常梯度。

限位惩罚有个经验值:关节角超过 0.9π 才扣分,而不是一过边界就扣。过松会导致机械臂长时间顶着限位,过紧会让探索集中在关节中部、永远不敢接近可动范围边界。写完奖励函数先别急着训练,用随机策略跑 50 个 episode,把 reward 分布打印出来看极端值。如果最大值超过 50,说明 sparse 脉冲太大,后续梯度会不稳定;如果方差特别小,说明密集引导不足。

4. 用 TensorFlow 2 搭 PPO 训练闭环:核心代码与参数调节

4.1 算法选型:为什么 PPO 是机械臂任务的第一选择

连续动作的强化学习里,可选项其实就那几类,提到频率最高的是 DQN 系列、SAC 和 PPO。DQN 这类 Q-learning 算法天然面向离散动作,要做连续关节控制就得离散化动作空间,6 个关节每个分 7 档就是 7 的 6 次方,动作枚举直接爆炸。SAC 采样效率高,但在带关节限位、奖励稀疏的机械臂任务里超参非常敏感,温度系数和两个 Q 网络的更新节奏要花很多时间去调,对“先跑通再优化”的测试阶段不友好。

PPO 的优势是稳定。它把策略更新的步长限制在可接受范围内,超参选择有大量现成经验值,即使不精细调参也能收敛到一个可用的策略水平。我见过不少项目在机械臂任务里从 SAC 切回 PPO,主要原因就是 PPO 的曲线更容易读:奖励平缓上升,而不是像 SAC 那样前期剧烈抖动。至于 TensorFlow 和 PyTorch 的流行趋势,研究圈这两年 PyTorch 势头很猛,但 TensorFlow 2 的 Keras 接口写 PPO 的骨架代码量更直接,这个标题既然定了 TensorFlow,tf.function 编译和后续 TFLite 部署链路也算顺理成章,不必纠结选型问题。

再补充一个方向:离线强化学习在真机采数据场景里有价值,但那是“已有大量真机日志”时才考虑的路线;基于模型的强化学习能减少采样,可机械臂动力学模型的误差会在策略更新里被放大。对 tensorflow-robot-arm 这个项目,在线 PPO 是投入产出比最高的起点,也是机械臂强化学习实战里最常被验证的组合。

4.2 Actor-Critic 网络与训练循环核心代码

策略和价值的共享网络结构按常规来:两层 256 的全连接,ReLU 激活。动作均值用 tanh 压到 -1~1,方差用一个可训练的 log_std 表示,通过 softplus 保证为正。Critic 头输出一个标量价值估计。

import tensorflow as tf import tensorflow_probability as tfp from tensorflow.keras import layers class ActorCritic(tf.keras.Model): def __init__(self, action_dim=6): super().__init__() self.shared = tf.keras.Sequential([ layers.Dense(256, activation="relu"), layers.Dense(256, activation="relu"), ]) self.mu = layers.Dense(action_dim, activation="tanh") self.log_std = tf.Variable(tf.zeros(action_dim), trainable=True) self.value = layers.Dense(1) def call(self, obs, training=False): x = self.shared(obs) action_mean = self.mu(x) return action_mean, self.log_std, self.value(x) model = ActorCritic(action_dim=6)

log_std 初始化成零,对应标准差接近 0.69,探索范围适中;如果训练前期完全不动,把 log_std 初始值改成 0.5 或 1.0 能增大探索噪声。方差单独作为可训练变量而不是从网络输出,是 PPO 里一个稳定训练的小技巧,能避免方差头随梯度剧烈波动。策略网络在训练阶段是个黑匣子,你只能通过指标观察它,所以方差初始化这种细节反而最值得盯。

训练循环里最关键的是策略更新。GAE 的优势估计属于强化学习数学原理里偏差方差折中那一节,实现上建议单独抽成函数,先算 advantage 再用 minibatch 更新:

import numpy as np def compute_gae(rewards, dones, values, gamma=0.99, lam=0.95): advantages = np.zeros_like(rewards, dtype=np.float32) gae = 0.0 next_value = 0.0 for t in reversed(range(len(rewards))): delta = rewards[t] + gamma * next_value * (1 - dones[t]) - values[t] gae = delta + gamma * lam * (1 - dones[t]) * gae advantages[t] = gae next_value = values[t] returns = advantages + values return advantages, returns

GAE 的 lambda 参数在 0.9~0.99 之间可调。lam 越大,advantage 越依赖长远收益,策略会更敢于做长距离移动;lam 越小,越关注短期修正。机械臂点到点任务我一般 0.95 起步。

策略更新代码:

optimizer = tf.keras.optimizers.Adam(learning_rate=3e-4, clipnorm=1.0) @tf.function def ppo_update(states, actions, old_log_probs, advantages, returns): with tf.GradientTape() as tape: mu, log_std, values = model(states, training=True) std = tf.nn.softplus(log_std) dist = tfp.distributions.Normal(mu, std) log_probs = tf.reduce_sum(dist.log_prob(actions), axis=-1) ratio = tf.exp(log_probs - old_log_probs) clipped_ratio = tf.clip_by_value(ratio, 0.8, 1.2) policy_loss = -tf.reduce_mean( tf.minimum(ratio * advantages, clipped_ratio * advantages) ) values = tf.squeeze(values, axis=-1) value_loss = tf.reduce_mean(tf.square(returns - values)) total_loss = policy_loss + 0.5 * value_loss grads = tape.gradient(total_loss, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables))

ratio 是当前策略与旧策略输出概率的比值,clip 到 0.8~1.2 是 PPO 防止策略一步更新过大的核心机制。clipnorm=1.0 是第二个保护:即使 loss 出现尖峰,梯度范数也被压住,这是我在机械臂任务里稳定训练的最有效手段。采样循环就按“解除默认马达 → 按策略或随机动作 → stepSimulation → 拿 obs → 存 transition”执行,每收集 2048 步做一次 ppo_update。tf.function 会自动把整个更新图编译,第一次调用会慢几秒,后面就快了。

4.3 关键超参表:学习率、batch size、GAE lambda 与 clip ratio

参数推荐值调参方向
learning_rate3e-4奖励曲线长期不动就降到 1e-4;前期发散优先检查 reward 尺度
batch_size256~1024偏小容易过拟合当前轨迹,偏大更新方差小但更慢
gae_lambda0.95目标点远就 0.98,需要精细修正就 0.9
clip_ratio0.2(对应 0.8~1.2)训练不稳定时收到 0.1,收敛慢时放到 0.3
gamma0.99episode 短,0.99 和 0.995 差别不大
每批采样步数20486轴任务建议不低于 1024

训练时我最常看的不是策略 loss 而是 entropy。entropy 掉到接近 0 说明策略过早确定,后面很难再探索到新路径,这时把 log_std 初始化调大或者降低学习率,比换奖励函数更有效。value loss 如果爆炸,先查 reward 尺度是不是跨了几个数量级,这是比学习率更隐蔽的原因。

提示:如果一个超参改完曲线完全没变化,先检查代码路径是不是真的走到了更新逻辑,别在玄学上花时间。

5. 机械臂强化学习的 5 个避坑记录:从 NaN 到仿真真机漂移

这一章是我自己跑 tensorflow-robot-arm 这类测试项目时踩过的真实坑,每条按现象、原因、解决的顺序写,你能照着排查。

5.1 现象:loss 一路 NaN,但仿真器没崩

训练到几百步,ppo_update 报 NaN,打印模型权重发现一层全是 nan。仿真器本身正常运行,机械臂还好好的在初始位置。

原因是奖励尺度太大。距离 3 米时 reward 是 -3,配合零均值优势估计,梯度会很大;加之 log_std 如果被优化成负数,softplus 虽然保证正,但 exp(log_probs) 在方差很小时容易产生极端比值。

解决的顺序是:先把 reward clip 到 [-10, 10],再给 optimizer 加 clipnorm=1.0,最后检查 log_std 是否出现过大的负数。这三步按顺序做,一般能定位问题。不要一上来就降学习率,会掩盖真正的原因。

5.2 现象:reward 在涨,机械臂却原地不动

这个坑最阴。训练曲线很漂亮,reward 稳步上升,把策略装回仿真一看,机械臂从头到尾没怎么动过,或者只在初始姿态附近小幅摆动。

原因是 dense 奖励太容易拿:离目标远,站着不动和缓慢靠近之间的距离惩罚差很小,而动作惩罚让策略倾向于不动。策略学会的是“少动作少扣分”,不是“去目标”。

解决方法是给到达加足够大的稀疏奖励,把 +10 改成 +30,同时检查 done 逻辑:只有末端进入目标半径才算 done,时间步超时不算成功。另一个立竿见影的手段是惩罚零动作——对相邻两步完全相同的动作单独扣分,逼策略持续尝试。

5.3 现象:仿真里很顺,真机一跑就抖

仿真里末端走线很平滑,换到真机后关节来回振,末端像得了帕金森。

原因是训练时策略输出了大量高频小幅动作。仿真刚体模型里这些动作没有惩罚,但真机有摩擦、齿隙和通信延迟,20Hz 以上的关节振荡会激发机械臂结构振动。摩擦是仿真里最容易低估的量,PyBullet 里可以给每个关节调 changeDynamics 的 lateralFriction,但先别急着精细建模,先把平滑惩罚加上看效果。

解决分三层:reward 里加动作差分惩罚;把控制频率从 100Hz 降到 25~30Hz,让每次动作指令间隔更接近真机实际控制周期;真机侧再加一阶低通滤波,target_position = 0.6 * new + 0.4 * last。这三层同时做,抖动基本能压住。

5.4 现象:训练慢到劝退,GPU 利用率却很低

开了 GUI 训练,一个 episode 要跑几十秒,GPU 利用率不到 10%,任务看起来根本训不完。

原因是渲染线程占用了大量 CPU,而且采样循环里每一步都调用 getJointStates、getLinkState 各一次,Python 和仿真之间的调用开销比计算本身大。还常见一个隐形问题:数据从 numpy 转 tensorflow 是在每个 step 里做,来回拷贝把时间都吃掉了。

解决:训练时用 p.DIRECT 模式完全不渲染;把观测读取集中到 get_obs 一个函数里;numpy 数据攒满一个 batch 再统一转 tf;如果机器有 GPU,用 CPU 做仿真采样、GPU 只负责 ppo_update,这是最标准的异步分工。

5.5 现象:随机种子一换,结果差一大截

同一个代码,seed 42 能学到 70% 成功率,seed 7 学了半天还在原地。这个坑最容易被当成玄学,其实是目标位置随机化的问题。

原因是训练初始阶段目标点在整个工作空间均匀随机采样,大多数构型下初始策略根本碰不到目标区域,只有少数种子的探索方向恰好先撞上正样本,才走出了收敛路径。网络初始化差异也被放大。

解决不是追求所有种子统一成绩,而是固定几个 seed 做横向对比,同时用课程式目标采样:训练前 1 万步目标点半径限制在初始末端位置附近 0.3 米内,之后逐步扩大到全工作空间。这个做法比调超参实惠得多,也是我在这个项目里最后悔没早做的事。

注意:以上所有排查步骤都在仿真环境里验证过再谈真机,这是机械臂项目的底线。

6. 从仿真策略到真机执行:迁移测试的三个验证技巧

训练出能到达目标的策略只是第一步,换到真机前有三个验证技巧,按顺序做能省掉一次真机翻车后只能靠后悔药的教训。

第一个技巧是仿真内做域随机化。不要只在单一动力学参数下训练,用 changeDynamics 把每个关节的摩擦、阻尼在 0.7~1.3 倍之间随机采样,末端挂一个随机小质量。策略见过多种动力学后,对真机的适应力明显提升。

第二个技巧是分步切入真机。先手动拖到初始姿态,再用随机策略跑 5 分钟,这期间验证通信、急停和限位逻辑;最后才加载训练策略,并且把动作幅度上限临时降一半,比如 step_scale 从 0.1 降到 0.05,观察几轮再说。以下是这个阶段我建议的检查表:

检查项判断标准
关节指令方向与角度符号一致手动加固定角度,关节朝预期方向动
急停能在 100ms 内切断指令按下急停,关节立即锁住
策略动作幅度未超限位日志里记录动作范围,集中检查
跟踪误差均值小于 0.5 度目标角度与实际角度差

第三个技巧是看跟踪误差而不是只看成功数。如果真机实际关节角一直追不上策略给出的目标角,说明仿真里没有建模出真实摩擦和力矩限制,问题不在强化学习,而在环境保真度。这时回仿真修动力学参数,比重训一个策略要有效得多。

我自己现在的习惯是:每改动一次奖励函数或网络结构,先在固定 5 个 seed 下各跑 2000 步,看平均距离曲线的分布,再决定要不要继续。这个习惯帮我少走了很多弯路。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表