
简介这份PDF文档围绕深度确定性策略梯度算法在工业机械臂轨迹规划中的应用使用PyTorch实现面向想学习强化学习与机器人控制的开发者和研究者。文档共二十九页先介绍工业机械臂结构、运动学基础与轨迹规划目标说明传统几何模型、运动学模型和动力学模型在复杂环境、多任务和实时性要求下的局限再讲解DDPG核心原理包括策略网络、价值网络、经验回放和目标网络并给出PyTorch环境搭建、状态空间与动作空间定义、奖励函数设计等内容。代码实现按模块拆分为环境类、网络模型、经验回放缓冲区、噪声生成、训练器、评估器与主程序便于对照调试最后通过奖励曲线、轨迹可视化、目标到达率及与传统方法对比验证算法在机械臂轨迹规划中的有效性和鲁棒性。资源为单个PDF文件大小二点零七兆字节支持目录章节跳转与左侧大纲定位查阅方便。目前已有八十六人学习下载适合需要完整走通从理论到代码落地的学习者。1. 为什么是DDPG机械臂轨迹规划的连续动作空间难题做机械臂轨迹规划传统路径是先建运动学模型再算逆解、查碰撞这套依赖精确模型的打法在动态环境里经常被打破。DDPG深度确定性策略梯度走的是另一条路不建模型让Actor网络直接输出关节速度Critic网络打分通过与环境大量交互自主学习最优轨迹这是机器人运动控制里被反复验证的新范式。这份资源把这条路线用PyTorch完整落地——从强化学习基础、算法原理、环境搭建到Actor-Critic实现和实验评估全流程覆盖。对机器人方向的硕博生、刚转向强化学习的机械臂工程师来说它可以少走不少弯路如果你正被「DDPG在PyTorch里怎么写」卡住这份资料能直接用来复现和做实验对照。2. 把DDPG拆开看从公式到四个组件的落地逻辑2.1 连续动作空间为什么查表式Q-learning在机械臂上走不通强化学习里最经典的Q-learning面向离散动作。机械臂的控制量是关节速度或关节角度增量在连续区间内取值理论上动作数量无限。要把Q-learning硬套上来只能离散化把每个关节的角度量化成几十档六自由度机械臂的动作组合瞬间膨胀到天文数字而且量化本身的精度损失对轨迹规划是致命的——末端误差几个毫米就是废品。策略梯度类算法REINFORCE、A2C可以处理连续动作但它们在更新策略时依赖蒙特卡洛采样估计回报方差大、样本效率低。机械臂仿真的每一步都涉及动力学计算样本效率低意味着训练时间被拉长到无法接受。DDPG选择了另一个方向用一个确定性网络直接输出动作再用另一个网络评价这个动作有多好把策略梯度里的随机积分替换成确定性的链式求导方差问题被绕开了样本效率也上了一个台阶。这个差异也解释了为什么你在做机械臂轨迹规划、而不是玩Atari游戏时会优先考虑DDPG而不是DQNDQN打游戏可以但要输出六维连续关节控制量它连动作都表示不了。2.2 确定性策略梯度策略网络和价值网络各自学什么DDPG里有两个网络。Actor网络策略网络输入状态 $s$输出动作 $a\mu(s|\theta^\mu)$它的目标是找到一个策略让Critic给这个动作打高分。Critic网络价值网络输入状态 $s$ 和动作 $a$输出 $Q(s,a|\theta^Q)$它的目标是预测「在状态 $s$ 下执行动作 $a$后续能拿多少累积奖励」。训练Critic用的是回归思路把目标价值 $y r \gamma \cdot Q(s, \mu(s|\theta^{\mu})|\theta^{Q})$ 当作标签让 $Q(s,a)$ 的输出向 $y$ 靠近损失函数是均方误差。这里最关键的是$y$ 的计算必须用目标网络带上标撇号的 $Q$ 和 $\mu$不能用在线网络自己算自己。如果用在线网络自举目标值会跟着参数一起漂移训练必然发散——这是很多初跑DDPG的人遇到的第一个玄学问题后面避坑章节我会展开讲。训练Actor用的是梯度上升对每个采样的状态 $s$计算 $Q(s,\mu(s))$ 对动作 $a$ 的梯度再乘上 $\mu(s)$ 对 $\theta^\mu$ 的梯度两个梯度链式相乘方向就是让动作往评价更高的方向挪。这个乘积用一句话说就是——Critic告诉Actor「往哪个方向改动作分数会变高」Actor照着做。2.3 经验回放与目标网络稳定性的两个支柱经验回放解决的是样本相关性问题。连续交互产生的状态序列高度相关直接用这批数据训练等价于在很窄的分布里反复拟合Critic会学到一堆局部假象。把 $(s,a,r,s)$ 存进缓冲区训练时随机采样一批就把相关性打破了。缓冲区容量常见做法是5万到10万条太小随机性不够太大旧策略数据占比高学习变慢。目标网络解决的是自举发散问题。软更新的做法是每个训练步把在线网络参数往目标网络方向挪一小步tau 0.005 for target_param, param in zip(target_net.parameters(), net.parameters()): target_param.data.copy_(tau * param.data (1.0 - tau) * target_param.data)这里的tau是软更新系数取值0.005意味着目标网络在一次更新里只往在线网络移动0.5%参数变化非常平缓给训练过程提供了一个几乎静止的靶子。tau设太大会震荡设太小收敛慢0.001到0.005是常见区间。注意这里用的是copy_原地赋值不是直接赋值直接赋值会破坏PyTorch的自动求导图关系训练时梯度会算到目标网络上去。2.4 DDPG、PPO、TD3怎么选算法采样方式适用场景主要缺点DDPGoff-policy经验回放连续动作、仿真样本获取成本高超参敏感容易出现Q值过估计PPOon-policy需要稳定策略更新、环境便宜样本效率比off-policy低TD3off-policyDDPG的改进针对过估计问题双Critic结构实现复杂度高如果仿真环境跑一步代价高、想省样本DDPG类是首选。如果环境便宜、试错成本低PPO是安全牌。TD3在DDPG代码基础上加双Critic和目标策略平滑做机械臂任务如果发现DDPG的Q值明显过估计表现为Critic loss异常高但reward正常直接换TD3是顺理成章的事。2.5 训练主循环五步压缩成一节每一步的做法可以压缩成五步从Actor拿到动作加探索噪声执行动作拿奖励和下一个状态把转移元组存进经验回放缓冲区从缓冲区随机采样一个mini-batch依序更新Critic回归损失、Actor策略梯度、软更新目标网络这个循环跑起来之后事情就变成一个工程问题环境返回什么格式、维度怎么对齐、奖励怎么归一化。接下来的章节就把这些工程细节逐一解决。3. PyTorch环境搭建从空环境到能跑DDPG的完整配置3.1 先花三分钟确认硬件和系统环境DDPG训练是计算密集任务纯CPU不是不能跑只是慢得让人怀疑人生。我一般先确认三件事显卡是不是NVIDIA的、驱动能不能被操作系统正常加载、磁盘有没有至少10GB空间。Windows、Ubuntu和macOS都能装PyTorchUbuntu在深度学习场景下最容易排查驱动问题因为nvidia-smi一条命令就能看到驱动版本和CUDA版本。Python版本建议3.9或3.10。深度学习库更新快太旧的Python版本会卡在一些依赖库的兼容性上。如果电脑上已经装了多个Python版本后面用conda建独立环境能省掉很多麻烦。3.2 conda创建独立环境避免依赖地狱Anaconda或Miniconda是管理Python环境最顺手的工具。给这个项目单独建一个环境不污染系统Python是值得养成的习惯conda create -n ddpg_env python3.9 conda activate ddpg_env建环境时指定python3.9会装一个干净的解释器后续所有包都装在这个环境里。激活成功后命令行前面会出现(ddpg_env)前缀说明已经切进独立环境。这一步做完后面装什么都不会影响其他项目。3.3 pip安装PyTorchCPU版和GPU版两条路线安装PyTorch最稳妥的方式是去pytorch官网的install页面它会根据你的操作系统和CUDA版本生成对应的命令。以CUDA 11.3为例pip安装命令是# CPU版不依赖显卡先跑通代码用 pip install torch torchvision torchaudio # GPU版需要预先装好NVIDIA驱动和匹配的CUDA pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113如果电脑没有NVIDIA显卡或者驱动版本不够新直接装CPU版不影响跑通DDPG训练流程只是每个episode的交互和反向传播会慢几倍。对验证算法逻辑来说CPU版完全够用对跑长时间训练实验GPU是必须的。判断该用哪个CUDA版本在命令行执行nvidia-smi看右上角的CUDA Version即可。3.4 验证安装CPU张量计算和GPU可用性装完先验证再做其他事python -c import torch; print(torch.__version__); print(torch.cuda.is_available())输出两行第一行是PyTorch版本号第二行是GPU是否可用。如果torch.cuda.is_available()返回False说明GPU版的CUDA没配对。常见原因有三个装的是CPU版、显卡驱动太旧、CUDA版本和PyTorch不匹配。注意is_available()返回False不会报错很多人忽略了这一行后面训练一直在CPU上跑了几小时才发现——这是最常见的隐形坑。3.5 配套库numpy、matplotlib、gymDDPG代码还依赖三个常用库一次装齐pip install numpy matplotlib gymnumpy负责状态和动作的数值运算matplotlib用来画奖励曲线和轨迹图gym是OpenAI的强化学习环境接口标准。如果后面要自己写机械臂仿真可以按gym的接口封装自定义环境让环境类实现reset()和step()两个方法DDPG训练代码就不需要改了。3.6 冒烟测试模型能不能跑通一次前向环境装好后的第一件事不是跑完整训练而是先跑一个冒烟测试初始化一个最小的Actor网络喂一个随机状态向量确认前向传播不报错。python -c import torch from torch import nn class Actor(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() self.net nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, 64), nn.ReLU(), nn.Linear(64, action_dim), nn.Tanh() ) def forward(self, s): return self.net(s) actor Actor(18, 6) s torch.randn(1, 18) print(actor(s).shape) 输出torch.Size([1, 6])就说明PyTorch环境、网络定义、张量维度全部就绪。这一步能过滤掉八成环境问题比直接跑完整训练脚本高效得多。4. 模型设计与核心代码状态、奖励、Actor与Critic网络全流程4.1 状态空间定义把机械臂姿态、目标与障碍物拼进一个向量状态空间是DDPG输入分布的基础。对工业机械臂状态一般包含关节角度六自由度就是6维、末端执行器位置和姿态33维、目标位置和姿态33维、障碍物距离视传感器输出定。一个有用的设计技巧是状态里尽量用「差值」而非「绝对目标」。把目标位置减去当前位置得到偏差向量网络更容易学到「往哪个方向挪」如果直接给绝对目标坐标网络在不同目标之间泛化会很困难。import torch import numpy as np def build_state(joint_angles, ee_pos, ee_ori, target_pos, target_ori, obstacle_dist): # 推荐用相对量目标与末端的偏差 pos_err np.array(target_pos) - np.array(ee_pos) ori_err np.array(target_ori) - np.array(ee_ori) # 关节角度 末端位姿 偏差 障碍物距离 state np.concatenate([ joint_angles, # 6维 ee_pos, ee_ori, # 6维 pos_err, ori_err, # 6维相对量 obstacle_dist # 障碍物相关信息 ]).astype(np.float32) return torch.from_numpy(state)参数说明pos_err和ori_err是核心设计决策——用偏差替代绝对目标值让网络学的是「我离目标还差多少、下一步该往哪个方向动」这个映射关系在不同任务目标之间是可迁移的。.astype(np.float32)是必须的PyTorch默认张量类型是float32如果喂float64会触发类型不匹配报错。4.2 动作空间与探索噪声输出范围怎么映射动作定义有两种习惯关节角速度控制和关节角度增量控制。角速度控制适合平滑运动但需要积分累积增量控制更直接网络直接输出每个关节该偏转多少。常见的做法是让Actor输出tanh激活后的[-1, 1]值然后在环境层映射到真实关节限位def map_action_to_joint(action, joint_low, joint_high): # action 是 [-1, 1] 的 tanh 输出 return joint_low (joint_high - joint_low) * (action 1.0) / 2.0这里joint_low和joint_high是每个关节的角度或速度下限、上限。比如关节2的活动范围是[-45度, 45度]映射后网络输出0.0就对应中间位置。这个映射如果不做网络输出的[-1,1]会被环境直接当作关节角度使用小角度关节会超出物理限位仿真里表现为机械臂姿态乱飞。探索噪声DDPG在确定性动作上加噪声经验回放里才能覆盖足够广的状态空间。OU噪声在机械臂场景里用得比较多但高斯噪声更简单实际效果并不差。一个重要的习惯是训练初期噪声大、后期噪声小做线性衰减class OrnsteinUhlenbeckNoise: def __init__(self, dim, mu0.0, theta0.15, sigma0.2): self.theta theta self.mu mu self.sigma sigma self.dim dim self.reset() def reset(self): self.x np.ones(self.dim) * self.mu def sample(self): dx self.theta * (self.mu - self.x) self.sigma * np.random.randn(self.dim) self.x dx return self.x参数说明theta控制噪声向均值回归的速度sigma控制噪声幅度。sigma0.2适合探索初期训练到后段衰减到0.05左右策略才不会被噪声带偏。OU噪声的特点是相邻两步的动作有相关性这对机械臂这种惯性系统更友好不会出现一步向左一步向右的抖动。4.3 奖励函数设计四项拼在一起量纲必须统一奖励函数的核心原则每一项的量纲要接近否则大尺度项会淹没小尺度项。位置误差用米量级0.1姿态误差用弧度量级0.01直接相加会导致网络只优化位置不管姿态。常见做法是把各分量归一化def compute_reward(current_pos, target_pos, current_ori, target_ori, collision, prev_action, curr_action, tol0.02): pos_dist np.linalg.norm(np.array(current_pos) - np.array(target_pos)) ori_dist np.linalg.norm(np.array(current_ori) - np.array(target_ori)) # 位置与姿态各自归一化到单位量级 r_pos -pos_dist / 3.0 r_ori -ori_dist / np.pi r_collision -100.0 if collision else 0.0 r_smooth -np.linalg.norm(np.array(curr_action) - np.array(prev_action)) # 稀疏的到达奖励避免只接近不精确 r_arrive 100.0 if pos_dist tol and ori_dist 0.05 else 0.0 return r_pos r_ori r_collision r_smooth r_arrive这个设计里r_arrive是关键如果没有这个稀疏奖励智能体会学到「靠近目标但永远不精确到达」因为持续靠近已经能拿到不错的奖励反而没有动机去精确对准最终位姿。r_smooth惩罚动作突变防止轨迹出现抖动。这几个分量相加量纲都在[-1, 1]附近没有哪一项能完全主导梯度。4.4 Actor与Critic网络实现PyTorch里的标准写法import torch import torch.nn as nn class Actor(nn.Module): 策略网络状态 - 动作 def __init__(self, state_dim, action_dim, hidden_dim256): super().__init__() self.fc1 nn.Linear(state_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.fc3 nn.Linear(hidden_dim, action_dim) self.relu nn.ReLU() self.tanh nn.Tanh() def forward(self, state): x self.relu(self.fc1(state)) x self.relu(self.fc2(x)) return self.tanh(self.fc3(x)) # tanh把输出压缩到[-1, 1] class Critic(nn.Module): 价值网络状态动作 - Q值 def __init__(self, state_dim, action_dim, hidden_dim256): super().__init__() self.fc1 nn.Linear(state_dim action_dim, hidden_dim) self.fc2 nn.Linear(hidden_dim, hidden_dim) self.fc3 nn.Linear(hidden_dim, 1) self.relu nn.ReLU() def forward(self, state, action): # 状态和动作拼成一个向量再进网络 x torch.cat([state, action], dim1) x self.relu(self.fc1(x)) x self.relu(self.fc2(x)) return self.fc3(x)Actor的最后一层用tanh是为了把输出限制在[-1, 1]配合前面map_action_to_joint的线性映射。Critic的第一层输入是state_dim action_dim因为Q值函数依赖于「状态和动作的组合」必须拼接后输入。如果Critic只输入状态不输入动作网络就不知道Q值对应的是哪个动作训练完全失去意义——这是我见过最多人搞错的地方。隐藏层hidden_dim取256是常规配置六自由度机械臂任务下容量足够。如果状态维度很高或者任务复杂可以加到512但要注意过拟合和训练变慢的问题。激活函数ReLU是默认选择换GELU或Swish在部分任务上有提升但优先保证ReLU基线跑通。4.5 经验回放与训练主循环把模块串起来from collections import deque import random class ReplayBuffer: 经验回放缓冲区 def __init__(self, capacity100000): self.buffer deque(maxlencapacity) def push(self, state, action, reward, next_state, done): # 统一存float32避免采样时维度/类型错误 self.buffer.append(( torch.FloatTensor(state), torch.FloatTensor(action), torch.tensor(reward, dtypetorch.float32), torch.FloatTensor(next_state), torch.tensor(done, dtypetorch.float32) )) def sample(self, batch_size): batch random.sample(self.buffer, batch_size) state, action, reward, next_state, done zip(*batch) return (torch.stack(state), torch.stack(action), torch.stack(reward), torch.stack(next_state), torch.stack(done)) def __len__(self): return len(self.buffer)push里强制torch.FloatTensor转换是值得养成的习惯——环境返回的state经常是numpy数组或内置列表不转换在训练时会报类型错误。deque(maxlencapacity)自带容量控制超出上限自动丢最旧的样本不用手动管理。训练主循环的每个训练步更新顺序必须是先更新Critic用目标网络计算目标价值再更新Actor让Critic打分更高的方向挪最后软更新目标网络。顺序不能乱Actor用了Critic的梯度如果先更新Actor再更新CriticActor拿到的梯度就是旧参数下的值。# 每步训练核心代码 for step in range(total_steps): # 1. 采动作 加噪 执行 存经验略 if len(buffer) batch_size: state, action, reward, next_state, done buffer.sample(batch_size) # 2. 更新Critic with torch.no_grad(): next_action target_actor(next_state) target_q target_critic(next_state, next_action) y reward gamma * (1 - done) * target_q q critic(state, action) critic_loss nn.MSELoss()(q, y) critic_optimizer.zero_grad() critic_loss.backward() critic_optimizer.step() # 3. 更新Actor actor_loss -critic(state, actor(state)).mean() actor_optimizer.zero_grad() actor_loss.backward() actor_optimizer.step() # 4. 软更新目标网络 soft_update(target_actor, actor, tau) soft_update(target_critic, critic, tau)critic_loss用的是MSE回归损失目标是让Q值预测逼近y。actor_loss取负号是因为PyTorch做梯度下降要让Q值最大化就得最小化负Q值。done乘以(1 - done)是为了处理终止状态——终止状态没有后续累积奖励。4.6 关键超参数速查参数常见取值说明lr_actor1e-4 ~ 1e-3Actor学习率通常比Critic低lr_critic1e-3Critic学习率tau0.005目标网络软更新系数gamma0.99折扣因子越接近1越考虑长远回报batch_size64采样batch大小buffer_size50000 ~ 100000经验回放容量noise_sigma0.2 → 0.05探索噪声训练后期衰减这些参数跑起来不一定是最优但作为起点能保证训练不崩。调参的方向是reward曲线震荡就降学习率或降tau训练太慢就升学习率或减小buffer每个episode太长就检查gamma是否让折扣后回报衰减太快。5. 避坑笔记不收敛、NaN、维度错位的五个典型翻车现场5.1 训练震荡不收敛现象reward曲线剧烈波动几千步训练之后还在横跳看不出上升趋势。原因学习率偏高、目标网络更新过快tau太大、经验回放缓冲区太小这三个因素经常一起出现。学习率大了Critic一步迈太远tau大了目标网络追着在线网络跑失去了「稳定靶子」的作用缓冲区太小采样分布太窄训练数据缺乏多样性。解决把lr_actor降到1e-4量级tau设为0.005buffer容量至少5万条。如果还震荡先冻结Actor只训练Critic几百步让价值网络先稳定下来再联合训练。5.2 Critic的loss直接变NaN现象训练到一半critic_loss变成nan奖励也变成inf训练直接报废。原因梯度爆炸。Q值目标y里的奖励项数值过大或者网络权重初始值不当反向传播梯度数值溢出。检查reward计算里是否有除零、是否在compute_reward中传入了None导致np.linalg.norm报错。解决对Critic和Actor都做梯度裁剪PyTorch里一行代码torch.nn.utils.clip_grad_norm_(critic.parameters(), max_norm1.0)另外把输入特征做标准化让每个维度均值为0、方差为1能大幅降低梯度爆炸概率。max_norm1.0表示梯度范数超过1就被裁剪到1防止单步更新过大。5.3 Actor输出没有被映射回关节限位现象仿真里机械臂姿态乱飞关节角度超出物理允许范围甚至出现末端执行器穿过地面的情况。原因Actor输出[-1, 1]的tanh值环境却直接把输出当作真实关节角度或角速度。六自由度机械臂每个关节的限位不同有的关节能转180度有的只有90度不做映射就会越界。解决在环境模块里对Actor输出做一次线性映射map_action_to_joint(action, joint_low, joint_high)把[-1, 1]放大到每个关节的实际范围。这个映射函数必须在训练和评估时保持一致否则训练时的策略在评估时就没法复用。5.4 维度对不上mat1 and mat2 shapes cannot be multiplied现象一运行训练脚本就报维度错误提示两个矩阵无法相乘。原因最典型的三个——环境返回的state是list类型torch.FloatTensor把它转成了整个list推导的张量Critic拼接时torch.cat的维度不对state和action分别在dim0和dim1上拼接或者是状态向量里混入了int型数据导致类型不匹配。解决在push进buffer之前先打印state.shape和action.shape确认维度一致后再往前推。我一般的做法是在build_state里就用torch.from_numpy(state)把状态统一成torch.Tensor后续所有环节都基于Tensor操作不再混用list和numpy。5.5 奖励一直在涨目标到达率却为零现象reward曲线稳步上升看起来训练一切顺利但跑100个测试episode末端到达目标点的成功率是0。原因奖励函数里的距离项把智能体带到了「靠近目标但不精确」的局部最优。持续靠近目标已经拿到了不错的负奖励精确对准目标需要付出额外的控制代价智能体觉得不划算。没有稀疏的到达奖励网络就学不到「精确到达」这个关键动作。解决在compute_reward里加一个阈值判断末端与目标的距离小于容差如2cm时给一个大的正奖励比如100。除了看奖励曲线还要在训练过程中定期跑评估统计目标到达率这两个指标一起看才不会自欺欺人。6. 训练完怎么验证奖励曲线、轨迹可视化与三个评估指标6.1 奖励曲线与损失曲线的判读训练结束后的第一件事是画曲线。奖励曲线看三样东西整体趋势是否上升、波动幅度是否在可接受范围、有没有突然崩塌。损失曲线看Critic是否收敛——如果Critic loss持续居高不下通常是Q值估计和目标值之间存在系统性偏差。import matplotlib.pyplot as plt fig, ax plt.subplots(2, 1, figsize(10, 8)) ax[0].plot(reward_history, labelepisode reward) ax[0].set_xlabel(episode) ax[0].set_ylabel(total reward) ax[1].plot(critic_loss_history, labelcritic loss, colororange) ax[1].set_xlabel(training step) ax[1].set_ylabel(loss) plt.tight_layout() plt.savefig(training_curves.png, dpi150)奖励曲线可以做滑动平均窗口10~50原始曲线太抖会看不清趋势。如果滑动平均后还在震荡就要回头检查第5章的参数问题。6.2 轨迹可视化与目标到达率轨迹可视化能让机械臂的行为直观呈现在眼前。用matplotlib画3D轨迹把末端执行器每个时间步的位置连成线对比规划的轨迹和目标点from mpl_toolkits.mplot3d import Axes3D fig plt.figure(figsize(8, 8)) ax fig.add_subplot(111, projection3d) ax.plot(traj_x, traj_y, traj_z, b-, linewidth2, labelDDPG trajectory) ax.scatter(*target_pos, colorr, marker*, s200, labeltarget) ax.set_xlabel(X (m)) ax.set_ylabel(Y (m)) ax.set_zlabel(Z (m)) ax.legend() plt.savefig(trajectory_3d.png, dpi150)图里如果轨迹是一条平滑的曲线、末端精确落在目标点上说明策略学到了。轨迹如果在某个点反复打转说明噪声过大或奖励设计有问题。评估指标里我认定三个目标到达率100次随机初始位置的测试中末端与目标距离小于阈值的比例阈值取2cm、轨迹长度末端位移的累计值越短说明路径越高效、平均速度波动相邻两步速度差的均值越小说明运动越平滑。这三个指标各有侧重到达率看任务完成度轨迹长度看效率速度波动看平稳性。6.3 部署前把模型导出固定下来如果这套策略要接到实际控制器或仿真软件里模型导出是个绕不开的环节。PyTorch的torch.save(actor.state_dict(), actor.pth)保存权重是最低要求但部署场景更常见的是导出ONNX把Actor网络固化成计算图。导出前有个细节要留意ONNX要求输入维度固定所以导出时要把状态向量的维度确定好我之前就因为状态里多了一个没用的常量维度导出的模型在目标设备上输入尺寸对不上白白查了半天。没有后悔药导出前先检查输入输出的形状。从那以后我每次跑DDPG都先花十分钟检查四个地方state的尺度、reward的量级、tau的大小、动作有没有映射回物理限位。这几件事检查完训练基本就是等曲线走平的事。评估阶段一定跑成功率而不是只看reward这两者差十万八千里。希望帮到你。本文还有配套的精品资源点击获取