资讯动态

MuJoCo与dm_control实战:从机械臂仿真到强化学习训练

发布时间:2026/9/8 18:53:27 来源:尧图企业网站定制
做具身智能的同行应该都经历过这么一段纠结机械臂、人形机器人在真实硬件上调试又贵又慢还得提心吊胆怕撞坏。所以大多数项目都会先在仿真器里完成原型验证、运动规划甚至强化学习训练。但仿真器选型这事真不是一句“用ROS和Gazebo”就能糊弄过去的。我最早是用Gazebo做机械臂抓取后来试过PyBullet最后在这两年把训练主力切到了MuJoCo和dm_control这套组合上稳定跑了不少PPO/TD3实验。这篇是这个系列的第一篇我会把为什么选MuJoCo、底层原理、安装避坑、机械臂IK实战、dm_control接入RL框架以及训练调优这些内容按我实际走通的路径一次讲清楚。1. 为什么具身智能需要MuJoCo而不是别的仿真器1.1 具身智能对仿真器的核心诉求具身智能和传统机器人仿真的差别在于“交互”和“学习”的重量级完全不同。传统仿真主要用来验证轨迹、检测碰撞跑得慢一点没关系只要轨迹算得出来就行。但具身智能强调感知-决策-执行闭环尤其是强化学习场景一个策略需要几百万步甚至上千万步的交互采样每一步都要计算接触力、关节动力学和渲染状态。这时候仿真的单步耗时就变成了决定性因素。我在早期用Gazebo做机械臂抓取时跑一个简单的PPO实验数据采样速度经常只有真实时间的1/10一个晚上跑完可能才积累几十万步完全不够模型收敛。换成MuJoCo之后同样的策略同数量级步数采样速度能提升一到两个数量级这才让我有底气把实验矩阵铺开。另一个关键诉求是接触计算的真实性。抓取、插孔、像人一样行走这类任务接触模型写得好不好直接决定学出来的策略能不能迁移到真机。MuJoCo的软接触模型在这种场景下的表现明显比传统刚体碰撞模型更平滑训练的收敛性也更好。1.2 与Gazebo、PyBullet、Isaac的分工对比不少初学者一上来就问“哪个仿真器最好”其实这是伪命题不同工具有明确的分工。仿真器强项弱项适合场景Gazebo与ROS生态深度绑定传感器模型丰富物理速度慢接触模型偏弱机器人在复杂环境中的SLAM、多传感器仿真、多机协同PyBullet简单易懂Python API友好接触计算不够稳定批量仿真性能一般入门教学、轻量算法验证MuJoCo接触模型细腻速度快数值稳定传感器仿真不如Gazebo丰富机械臂操作、足式机器人、强化学习训练Isaac Sim/Gym支持GPU并行上千环境学习曲线陡硬件要求高大规模并行RL、人形机器人训练我现在的习惯是如果项目主要做运动规划、SLAM或者要跟ROS节点深度联调那用Gazebo并不过时如果只是快速验证一个控制算法PyBullet也能用但一旦进入强化学习或者任务本身对接触力仿真要求很高比如灵巧手抓取、双足/四足行走MuJoCo基本是首选。Isaac的好处在GPU并行但成本也高单卡训练环境少的话优势发挥不出来。1.3 MuJoCo的“前世今生”以及为什么DeepMind愿意开源MuJoCo全称是Multi-Joint dynamics with Contact最初是华盛顿大学等团队在2015年左右推出的商业授权物理引擎。它一开始就是面向“带接触的多刚体系统优化和控制”来设计的所以底层算法跟游戏物理引擎不一样更照顾机器人学和控制领域的需要。2021年DeepMind宣布收购MuJoCo并在2022年把它开源采用Apache 2.0协议2023年发布了全面重写后的3.0版本把Python绑定、渲染和模型库都重新做了一遍。这段历史解释了一件事为什么MuJoCo的API一度有点“非主流”。它更像个研究工具不适合拿来做游戏特效但你真要算一个七自由度机械臂的雅可比、做逆运动学、甚至求动力学逆解MuJoCo的mj_jacSite、mj_comPos这类底层函数直接摆在那里比你要去别的引擎里翻半源代码方便得多。DeepMind开源MuJoCo本质上也是为了让整个具身智能社区有统一的高质量物理底座dm_control就相当于在这个底座上长出来的标准接口层。2. 从pip install到第一个能动的仿真环境准备与最小示例2.1 安装MuJoCo的现代姿势3.x版本如果你在搜索引擎里看到Old School安装教程让你去官网下压缩包、设置MUJOCO_PYTHON_INCLUDE_PATH之类的环境变量请先确认版本。MuJoCo 3.0之后的安装非常简单Python环境只需要一条命令pip install mujoco这条命令会同时装上底层的C库和Python绑定安装完成后就可以直接import mujoco。控制套件再加一条pip install dm_controldm_control依赖mujoco包所以它会拉取对应版本。我个人建议在虚拟环境里装避免跟其他项目的依赖互相污染。另外如果你准备接强化学习训练顺手把numpy、gymnasium、stable-baselines3装好下面用得着。安装时有个小细节MuJoCo在Linux上会用到OpenGL相关的渲染后端如果你用的是WSL或纯命令行的服务器需要确认libglfw3和libosmesa6这些系统库是否齐全。没有GUI的服务器也可以用mujoco.viewer的passive接口跑后台渲染但需要设置MUJOCO_GLegl或MUJOCO_GLosmesa否则会在创建渲染上下文时报错。2.2 Windows 11上的坑与解法网上搜“Windows 11安装mujoco”报错最多的两类第一类是ImportError: DLL load failed while importing mujoco第二类是GLFW error或者窗口一闪而过。先处理DLL问题MuJoCo的Python包自带预编译二进制但需要系统里装了Visual C Redistributable 2015-2022。很多新装系统的机器没装这个运行库就会在导入阶段失败装一下官方VC运行库基本能解决。再处理渲染窗口问题Windows环境下MuJoCo默认走OpenGL如果你的电脑显卡驱动太老或笔记本有双显卡launch_passive可能创建窗口失败或黑屏。解决办法有两条一是更新显卡驱动二是强制禁用独立显卡试试。我遇到过一台NVIDIA独显笔记本只要切到集成显卡跑窗口就正常了属于驱动层面的兼容问题。另外如果你用的是conda环境不要把别的深度学习包和mujoco混装在一个已经乱七八糟的环境里最好新建干净环境。2.3 一个只有十几行的最小仿真循环装完之后先别急着上复杂模型。跑通最小仿真循环是关键里程碑。下面这段代码可以直接保存成minimal.py运行import mujoco import mujoco.viewer import numpy as np # 加载自带的pendulum模型 xml mujoco option timestep0.002/ worldbody body namependulum pos0 0 1 joint namehinge typehinge axis0 1 0/ geom namepole typecapsule fromto0 0 0 0 0 -0.5 size0.02/ /body /worldbody /mujoco model mujoco.MjModel.from_xml_string(xml) data mujoco.MjData(model) with mujoco.viewer.launch_passive(model, data) as viewer: for _ in range(10000): mujoco.mj_step(model, data) viewer.sync() if not viewer.is_running(): breakmj_step每调用一次就走一个物理步长timestep这里是0.002秒也就是500Hz。viewer.launch_passive提供了一个可交互的观察窗口sync()负责把最新的仿真状态推送到渲染线程。这段代码跑起来你应该能看到一个在重力作用下摆动的杆子。这个最小示例虽然简单但它展示了MuJoCo的应用模式加载模型拿到model和data循环里调mj_step然后再决定要不要渲染、要不要采样。后面所有复杂的强化学习环境基本都是在mj_step外面包一层更高级的接口而已。3. 仿真器内部在算什么时间积分、约束求解与接触模型3.1 控制方程关节空间动力学MuJoCo用广义坐标来描述机械系统。以机械臂为例它内部的核心动力学方程可以写成[ M(q)\ddot{q} c(q,\dot{q}) \tau J^T f ]这里M(q)是质量矩阵c(q,dot q)包含科氏力和重力项tau是关节驱动力J^T f是关节空间上的接触/约束力贡献。MuJoCo的核心工作就是把质量矩阵、科氏力这些机器人学里繁琐的项全部在内部高效算好用户只需要调mj_step就行。理解这个方程很重要因为很多调试都归结为“动力学模型到底把哪些力算进去了”。比如你发现机械臂在仿真里模拟出来的下坠速度跟真实世界明显不一致那就该检查阻尼项damping、关节摩擦frictionloss和惯性参数inertia是不是设置得跟真机差距太大。3.2 Euler积分与RK4以及为什么默认步长是2ms量级MuJoCo里常用的积分器有Euler显式欧拉、RK4四阶龙格-库塔和implicitfast。默认情况下它用的是Euler。你可能会问Euler不是数值稳定性差吗为什么这样一个高级仿真器还默认用Euler关键原因是在带接触的机器人仿真里每步都需要求解约束/接触力RK4虽然精度高但代价是每步要多次评估动力学和约束求解速度会明显下降。而MuJoCo的Euler是半隐式的它能更自然地跟接触力求解耦合在很多dexterous manipulation场景里不会发散。实际经验是对绝大多数具身智能训练任务Euler加2-5毫秒的步长已经足够稳定。只有在做高精度轨迹规划验证或者碰到高频柔性问题时我才考虑把积分器改成RK4。步长选择也有讲究。timestep越小单步越接近真实连续动力学但同样的控制周期内需要的步数就越多仿真速度越慢。我做机械臂操作时常用timestep0.002配合每步的nsubsteps来达到控制频率200Hz-500Hz。四足和人形机器人行走任务我习惯用timestep0.005降低算力开销。3.3 软接触模型为什么MuJoCo这么快还这么稳MuJoCo最有辨识度的一个设计就是软接触模型。传统物理引擎经常会用完全非弹性碰撞加库仑摩擦来解决接触问题但要付出现象级的麻烦摩擦锥线性化、接触矩阵求解不稳定最后表现就是物体会抖、会弹跳、会穿透。MuJoCo用的是连续可微的软接触模型法向力通过一组“阻抗阻尼”的数学关系来计算接触力在接触深度和相对速度上是光滑的。这就带来了两个巨大优势一是在强化学习里策略梯度算法对奖励函数和状态转移的平滑性非常敏感软接触让动力学从“可能突然跳变”变成“连续过渡”大大提升训练稳定性二是数值上不再需要频繁重试求解器可以走固定迭代次数并保持不错的精度这让每步仿真快了很多。概念上可以类比一下传统接触像两块硬塑料用力怼上啪一下停住容易弹开MuJoCo的软接触更像中间垫了层橡胶既能传递力又不会突然脱手。代价是参数多核心是solref和solimp分别控制接触的刚度和阻尼特性。实调时如果发现物体容易陷进去或者接触力振荡优先检查这两个参数而不是盲目改mass。3.4 MJCF模型格式的核心结构MuJoCo原生模型格式是MJCF一个XML方言。第一次看到MJCF的人容易懵但它的结构其实非常直观mujoco option timestep0.002/ worldbody body namelink1 pos0 0 0 joint namejoint1 typehinge axis0 0 1/ geom typecapsule fromto0 0 0 0 0 0.3 size0.03/ body namelink2 pos0 0 0.3 !-- ... -- /body /body /worldbody /mujoco层级结构是worldbody下面是所有刚体body每个body下可以挂joint关节、geom几何外形、site参考坐标系和子body。关节描述自由度geom决定碰撞和视觉外形site则经常用来定义末端执行器位置。MJCF这种层级嵌套特别适合机械臂、人形机器人这种“父body套子body”的结构。如果你手里只有URDF也没关系MuJoCo模型编译工具会自动把URDF转换成MJCF转换后最好检查一下单位、惯性矩阵和坐标轴方向它们的默认约定跟ROS生态可能不一致。这个问题在真实项目中很常见我从URDF转过来后经常发现质量参数和轴的朝向要修正。4. 手把手写一个机械臂仿真模型构建、正向运动学与IK4.1 在MJCF里定义一只三连杆机械臂下面这个模型是我常用来说明机械臂基础用法的三连杆臂三个旋转关节绕Y轴转动末端在XZ平面内运动mujoco modelsimple_arm option timestep0.002/ worldbody body namebase pos0 0 0 joint nameshoulder typehinge axis0 1 0/ geom typecapsule fromto0 0 0 0 0 0.3 size0.03/ body namemid pos0 0 0.3 joint nameelbow typehinge axis0 1 0/ geom typecapsule fromto0 0 0 0 0 0.25 size0.025/ body nametop pos0 0 0.25 joint namewrist typehinge axis0 1 0/ geom typecapsule fromto0 0 0 0 0 0.15 size0.02/ site nameee pos0 0 0.15/ /body /body /body /worldbody /mujoco你看到fromto这个属性它表示这个几何体从哪一点延伸到哪一点对于胶囊和圆柱这类细长形状比用pos和size去拼坐标要直观很多。最末端的site我命名为eeend-effector后面算末端位置和雅可比都靠它。4.2 正向运动学从关节角到末端位置所谓正向运动学FK就是知道关节角度求末端位置。MuJoCo里不需要我们手推公式每次mj_forward之后末端位置直接放在data.site_xpos[site_id]里。代码是这样的import mujoco import numpy as np xml open(simple_arm.xml, encodingutf-8).read() model mujoco.MjModel.from_xml_string(xml) data mujoco.MjData(model) site_id model.site(ee).id data.qpos[:] np.array([0.5, -0.8, 0.3]) mujoco.mj_forward(model, data) ee_pos data.site_xpos[site_id].copy() print(末端位置:, ee_pos)这里有个新手容易踩的坑如果你只设置data.qpos然后直接读site_xpos读出来的是上一帧的结果。mj_forward负责根据当前位形重新计算运动学量包括site_xpos、雅可比、质量矩阵等但不推进仿真。mj_step则是在mj_forward基础上再算动力学并前进一个物理步。所以静态分析时用mj_forward需要动力学仿真时用mj_step。4.3 用雅可比做阻尼最小二乘逆运动学逆运动学IK是机械臂控制里绕不开的问题我已知目标末端位置反求各关节角。最朴素的Jacobian转置法就行但我在实际使用中更推荐阻尼最小二乘法DLS它在接近奇异位形时不会产生爆炸式的关节速度。核心步骤如下def ik_solve(model, data, site_id, target, max_iter300, tol1e-4, damping0.01): for _ in range(max_iter): mujoco.mj_forward(model, data) current_pos data.site_xpos[site_id].copy() error target - current_pos if np.linalg.norm(error) tol: break jacp np.zeros((3, model.nv)) jacr np.zeros((3, model.nv)) mujoco.mj_jacSite(model, data, jacp, jacr, site_id) # 阻尼最小二乘逆 (J J^T lambda^2 I)^-1 jjt jacp jacp.T j_inv jacp.T np.linalg.inv(jjt damping ** 2 * np.eye(3)) delta_q j_inv error data.qpos[:] delta_q这里mujoco.mj_jacSite一次性给出末端的平移雅可比jacp和旋转雅可比jacr。平移雅可比把关节速度映射为末端线速度旋转雅可比把关节速度映射为末端角速度。IK里我们只需要关心位置误差所以只用jacp。阻尼系数damping是关键。设成0就是纯Jacobian逆在奇异位形会出很大的关节速度设一个小值比如0.01相当于在求解时加了正则化换来了数值稳定性。实际调试时如果发现IK末端在某个区域抖得厉害把damping往上抬到0.05甚至0.1通常会好很多。代价是离目标点会有几毫米的静态误差对大多数抓取任务来说完全可接受。4.4 把IK封装成仿真环境中的控制器上面这个IK函数只能用于离线位形计算想把它用在实时控制里需要跟mj_step配合。一个常见的写法是每个控制周期开始调用IK得到目标关节角然后用一个简单的PD控制器跟踪关节角。kp, kv 100.0, 20.0 for _ in range(1000): ik_solve(model, data, site_id, target_pos) desired_qpos data.qpos.copy() # 这里用PD控制跟踪 data.ctrl[:] kp * (desired_qpos - data.qpos) - kv * data.qvel mujoco.mj_step(model, data)当然这个代码的前提是模型里有对应的执行器actuator否则data.ctrl不会有任何效果。在解析关节空间跟踪时我会在MJCF里加上三个motor actuator这样ctrl对应三个关节的力矩。5. 接入dm_control把环境喂给主流强化学习框架5.1 dm_control与MuJoCo有什么关系理解了MuJoCo之后再看dm_control就很清晰它是一层更高层的Python环境接口层基于MuJoCo实现。就像游戏里“物理引擎”和“游戏逻辑框架”的关系。dm_control里的suite模块封装了大量现成的连续控制任务包括倒立摆、步行、跑跳、操作等这些环境都遵循dm_env接口统一了reset和step的样式from dm_control import suite import numpy as np env suite.load(humanoid, run) timestep env.reset() action_spec env.action_spec() for _ in range(200): action np.random.uniform(action_spec.minimum, action_spec.maximum, sizeaction_spec.shape) timestep env.step(action) if timestep.last(): timestep env.reset()为什么我推荐dm_control而不是直接裸写MuJoCo当你的任务比较复杂时环境层面要处理观测定义、奖励函数、终止条件、重置逻辑这些琐事全用裸MuJoCo写很容易出bug而且代码难以复用。dm_control把这些约束放进dm_env框架里顶层还能用dm_control.composer来组合新的物体和任务省事很多。5.2 从suite加载现成环境DeepMind Control Suite是一套现成的基准任务集合。我的习惯是先跑一遍这些环境验证自己的强化学习框架是否正常工作再往自定义任务迁移。常用的几个环境包括cartpole: swingup倒立摆入门首选状态维度低适合调试算法。reacher: easy/hard两连杆机械臂触达目标接近真实机械臂操作。walker: walk/run双足行走接触丰富适合研究sim-to-real。humanoid: run全身人形运动reward shaping复杂度高。这些环境直接用suite.load(domain, task)就能拿到比如suite.load(cartpole, swingup)。5.3 自定义dm_env环境把之前的机械臂包装成RL环境如果你想跑自己的机械臂任务最简单的做法是实现一个dm_env.Environment子类。下面是基本骨架import dm_env from dm_env import specs import numpy as np class SimpleArmEnv(dm_env.Environment): def __init__(self, model_path): self.model mujoco.MjModel.from_xml_path(model_path) self.data mujoco.MjData(self.model) self.site_id self.model.site(ee).id def reset(self): self.data.qpos[:] np.random.uniform(-0.5, 0.5, self.model.nq) self.data.qvel[:] 0.0 mujoco.mj_forward(self.model, self.data) return dm_env.restart(self._get_obs()) def step(self, action): self.data.ctrl[:] action mujoco.mj_step(self.model, self.data) obs self._get_obs() reward self._get_reward(obs) return dm_env.transition(reward, obs) def _get_obs(self): return { qpos: self.data.qpos.copy(), qvel: self.data.qvel.copy(), ee_pos: self.data.site_xpos[self.site_id].copy(), } def observation_spec(self): return { qpos: specs.Array((self.model.nq,), float), qvel: specs.Array((self.model.nv,), float), ee_pos: specs.Array((3,), float), } def action_spec(self): return specs.BoundedArray((self.model.nu,), float, minimum-1.0, maximum1.0)这里有几个细节要提醒dm_env.restart表示环境从新回合开始dm_env.transition表示普通中间步timestep.last()对应终止步需要用dm_env.termination。连续控制任务里我一般让环境跑固定回合长度到时用termination结束。另外action_spec必须跟data.ctrl的维度一致否则dm_control内部校验会直接报错。5.4 与Stable-Baselines3/Gymnasium的桥接Stable-Baselines3本身不直接识别dm_env所以接入PPO时有两种常用路线要么把dm_env包装成Gymnasium接口要么直接用Gymnasium内置的MuJoCo环境。我实际训练时如果只是做实验优先用内置环境比如HalfCheetah-v4这种开箱即用import gymnasium as gym from stable_baselines3 import PPO env gym.make(HalfCheetah-v4, render_modeNone) model PPO(MlpPolicy, env, verbose1) model.learn(total_timesteps1_000_000)如果要跑自己的机器人就需要写一个从dm_env到 gymnasium.Env的wrapper重点是把reset的返回格式改成(obs, info)把step的返回改成(obs, reward, terminated, truncated, info)。这个wrapper几十行代码就能写完最值得注意的地方是dm_env终止时返回的观测可能只是过渡值Gymnasium的terminated语义要求这一步的观测是有意义的所以处理终止过渡时要么不取最后一步要么提前调用reset并返回新回合初始观测。6. 训练与部署中的性能调优和疑难杂症6.1 无头渲染加速与mujoco.viewer耗电之谜很多人在训练开始时习惯开着viewer看效果然后跑一晚上发现不仅GPU占用高训练速度还上不去。原因是viewer的实时渲染会不断消耗CPU/GPU资源而且为了跟仿真循环同步它在拖慢主循环。我现在的经验是训练过程中一律不渲染把环境里相关的render_mode设为None只在训练结束后用一段独立的回放脚本加载现成的轨迹逐帧渲染成视频。如果你必须在服务器上做可视化则要走无头渲染路线。MuJoCo支持EGL或OSMesa后端在命令行前设置MUJOCO_GLegl或MUJOCO_GLosmesa可以没有显示器的情况下渲染图像。EGL性能更好但要求GPU环境OSMesa是纯软件渲染慢很多但兼容性最好。远程调试的时候我一般用EGL加mujoco.viewer的launch_passive注意要确保本机能转发窗口或者直接把图像保存成文件再看。6.2 子步长、控制频率与训练稳定性在MuJoCo里物理步长timestep是一个仿真步的时间但RL环境通常要求固定控制频率。比如你希望策略每秒执行50次决策也就是控制周期0.02秒如果物理步长是0.002秒那么每个控制周期内部要执行10次mj_step。这个“次数”就是nsubsteps。我推荐在自定义环境里把控制频率和物理步长分开配置让用户可以调节。原因很简单如果控制频率太高策略需要更长的时间域输入才能学会合理的时序动作如果控制频率太低则每一段决策间隔里动力学演化可能太剧烈训练不稳定。机械臂操作任务我常用控制频率50Hz物理步长2msnsubsteps10人形机器人行走我会把物理步长降到5ms控制频率降到30-50Hz。调试技巧如果训练过程中出现“高频抖动”尤其人形机器人容易站立不稳多半是PD控制器的kp和kv没有配合好nsubsteps。减少nsubsteps会让单步决策间隔更短等价于提高了控制频率通常对稳定性有帮助但会显著增加计算量需要根据实验效果平衡。6.3 常见报错清单与修复方案我在各种机器上跑MuJoCo遇到的报错类型其实高度重复列一个最常见的表格给各位抄作业报错信息可能原因处理方式DLL load failed while importing mujoco缺少VC运行库或Python环境不干净安装Visual C Redistributable新建干净虚拟环境GLFW error/OpenGL context creation failed无显示环境或者显卡驱动问题设置MUJOCO_GLegl或osmesa更新显卡驱动XML parse errorMJCF文件路径或标签写错用from_xml_string先排除文件读取问题再逐标签检查Timestep must be greater than zerooption timestep未设置或为0显式设置timestep比如0.002KeyError: xxxin dm_control suite任务名或domain名拼写错误查阅suite.ALL_TASKS确认名称assert data.ctrl.size nuaction维度跟actuator数量不一致检查MJCF中.actuator数量以及action_spec().shape还有一个反复出现的诡异问题在Linux服务器上装了mujoco后import mujoco没问题但运行一段代码后直接段错误。这种情况我在老版NVIDIA驱动加专用GPU环境下遇过几次大概率跟OpenGL后端冲突有关建议先试试MUJOCO_GLosmesa能跑就说明是GPU渲染路径的问题。6.4 从仿真到真机的差距训练前就值得关注最后聊一个很多人忽略的点MuJoCo仿真调得再好也不可能100%复现真机。差距主要来自三方面接触参数、摩擦力、延迟。接触参数方面MuJoCo的solref、solimp在默认场景下表现很好但不同材质差异很大。比如我的机械爪抓金属工件默认接触参数会显得“太硬”摩擦也过于理想真机上一抓就滑。训练前最好做个简单校准用同一种材料分别做滑动和碰撞实验调整摩擦系数到仿真里的力传感器读数和真机接近。别小看这一步它对sim-to-real效果影响巨大。延迟方面MuJoCo仿真默认是零延迟决策但真机从感知到执行往往有几十毫秒的通讯延迟。一个比较实际的做法是训练时在环境动作里加入随机动作延迟比如随机延迟1-5个控制周期让策略学会在不确定延迟下做出稳健动作。我这套方案在机械臂抓取任务上实验过真机首次成功率比不做延迟训练大概提升了三成以上。这些调优经验都是从一个个跑崩的实验里攒下来的。MuJoCo和dm_control本身只是工具真正拉开计算效率差距的是你对模型参数和仿真环境的设计。这套组合最值得投入时间的地方不是玩法而是深入理解接触模型和动力学如何影响你的强化学习任务。熟悉之后你会发现很多训练不收敛的问题根子其实早在仿真环境设置阶段就埋下了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价