域随机化#
在单一固定仿真中训练出的策略,学到的是那个确切仿真的特性。在真实硬件上,摩擦、连杆质量和执行器增益都与你的模型不同,并且会随时间漂移,这样的策略就会失效。域随机化(domain randomization) 弥合了这一差距:你为每个并行环境采样一组不同的物理与任务参数,于是策略在训练中看到的是动力学的分布,从而学到不依赖任何单一参数取值的行为。
Genesis World 正是为此而构建的。由于仿真本身已经按环境批量化,随机化逐环境参数只需传入一个首维为 n_envs 的张量。本页介绍可以随机化哪些内容、用于设置它们的批量 API,以及每类随机化应在何时施加。
强化学习中有两种反复出现的随机化模式,它们的区别在于何时施加:
每次运行的物理随机化: 描述机器人及其接触的属性,例如摩擦、质量和 PD 增益。这类参数应在
scene.build()之后立即设置一次,让每个环境在整个运行期间都仿真一个略有差异的机体。每回合的任务随机化: 随回合一起重置的量,例如速度指令、生成位姿和重置状态。在你的环境重置函数内部重新采样它们,并只作用于刚刚终止的那些环境。
逐环境随机化物理参数#
刚体实体为那些最影响 sim-to-real 迁移的属性提供了批量 setter。每个 setter 都接受首维为 n_envs 的张量,因此一次调用就能为每个环境指定不同的取值。下面的代码摘自 examples/rigid/domain_randomization.py,该示例加载一个 Go2 四足机器人,并在八个环境之间对它做随机化:
scene.build(n_envs=8)
# Scale each link's friction by a per-env, per-link factor in [0.5, 1.5).
robot.set_friction_ratio(
friction_ratio=0.5 + torch.rand(scene.n_envs, robot.n_links),
links_idx_local=np.arange(0, robot.n_links),
)
# Scale each link's mass by a per-env factor in [0.75, 1.25).
robot.set_links_mass(
mass=robot.get_links_mass() * (0.75 + 0.5 * torch.rand(scene.n_envs, robot.n_links, device=gs.device)),
)
# Shift each link's center of mass by up to 5 cm on each axis.
robot.set_links_COM(
com=robot.get_links_COM() + (-0.05 + 0.1 * torch.rand(scene.n_envs, robot.n_links, 3, device=gs.device)),
)
这三个方法修改的内容各不相同:
set_friction_ratio: 把每个几何体的基础摩擦系数乘以给定的因子,形状为(n_envs, n_links)。它是缩放而不是替换,因此比例1.0会保持模型的摩擦不变。如果想改为给整个实体设置一个绝对摩擦系数,请使用set_friction(friction),它接受单个浮点数,并且为保证稳定性要求该值位于[1e-2, 5.0]区间内。set_links_mass: 以千克为单位设置每个连杆的质量,形状为(n_envs, n_links),并保持其原始惯量不变。它接受的是质量本身,因此示例先用get_links_mass读回模型质量,再写入缩放后的质量。set_links_COM: 以米为单位设置每个连杆的质心,形状为(n_envs, n_links, 3),其含义是在连杆局部坐标系中的偏移。它同样接受位置本身,因此示例是在get_links_COM的返回值上做平移。
set_friction_ratio 写入的是逐环境状态,因此在任何场景中都可以使用。逐环境的质量或质心描述的是模型本身,只有场景以 batch_links_info=True 构建时(见下方警告),Genesis World 才会按环境分别存储这些信息。若所有环境共享同一个取值,则不需要该开关。对其中任何一个方法传入 envs_idx,即可把更新限制在部分环境上。
随机化执行器增益#
随机化 PD 控制器建模了一个事实:真实电机既不是完美整定的,彼此也不完全相同。增益 setter 接受逐 DOF 的取值,当你传入一个首维为 n_envs 的张量时,它们就会为每个环境指定一个独立的控制器:
# Per-env, per-dof position and velocity gains. Shapes: (n_envs, n_dofs).
robot.set_dofs_kp(4000 + 1000 * torch.rand(scene.n_envs, robot.n_dofs), motors_dof_idx)
robot.set_dofs_kv(400 + 100 * torch.rand(scene.n_envs, robot.n_dofs), motors_dof_idx)
robot.set_dofs_armature(0.01 + 0.02 * torch.rand(scene.n_envs, robot.n_dofs), motors_dof_idx)
set_dofs_kp 和 set_dofs_kv 设置 PD 控制器的位置增益与速度增益;set_dofs_armature 设置折算到关节侧的电机惯量,它能让刚性关节更稳定。第二个位置参数是 dofs_idx_local,即要更新的实体局部 DOF 索引,它与由关节名构建出的 motors_dof_idx 相对应。
Warning
逐环境的 DOF 增益需要批量的 DOF 信息,而逐环境的连杆质量、质心和惯量需要批量的连杆信息。两者都保存在模型的静态信息字段中,只有场景以对应选项构建时,这些字段才会按环境分别存储。请在 RigidOptions 中启用它们:
scene = gs.Scene(
rigid_options=gs.options.RigidOptions(
batch_dofs_info=True, # required for per-env kp / kv / armature
batch_links_info=True, # required for per-env static link info
),
)
如果没有 batch_dofs_info=True,带 n_envs 维度的增益张量就无处安放;同理,没有 batch_links_info=True 时,逐环境的质量、质心或惯量也无处安放。set_friction_ratio 两个开关都不需要。
逐回合随机化指令与状态#
任务随机化在每个回合都会变化,因此它属于重置路径,而不是 build() 之后。运动示例(见 使用 RL 训练运动策略,源码 examples/locomotion/go2_env.py)会在环境重置或重采样间隔到期时为该环境采样新的速度指令。它的均匀采样辅助函数值得复用:
def gs_rand(lower, upper, batch_shape):
# Uniform in [lower, upper), broadcast over the batch dimensions.
assert lower.shape == upper.shape
return (upper - lower) * torch.rand(size=(*batch_shape, *lower.shape), device=gs.device) + lower
随后,环境只为 envs_idx 中传入的那些环境重新采样指令,其余环境保持不动:
def _resample_commands(self, envs_idx):
commands = gs_rand(*self.commands_limits, (self.num_envs,)) # shape (n_envs, num_commands)
torch.where(envs_idx[:, None], commands, self.commands, out=self.commands)
同样的原则也适用于生成位姿和重置状态:先为需要重置的环境构造随机化张量,再用实体 setter 的 envs_idx 参数写入,从而只触碰这些环境。例如,entity.set_pos(random_pos, envs_idx=envs_idx) 可以随机化物体的初始位置,同时不影响其他仍在回合进行中的环境。
方法参考#
方法 |
形状 |
随机化内容 |
|---|---|---|
|
|
逐连杆摩擦,作为基础系数的乘数 |
|
|
逐连杆质量(kg) |
|
|
逐连杆质心(m),位于连杆坐标系 |
|
|
逐连杆惯量矩阵,位于惯性坐标系 |
|
|
PD 位置增益 |
|
|
PD 速度增益 |
|
|
折算到关节侧的电机惯量 |
全部七个方法都接受可选的 envs_idx,用于只更新部分环境。增益与 armature 相关的 setter 需要 batch_dofs_info=True,质量、质心和惯量相关的 setter 需要 batch_links_info=True,而 set_friction_ratio 两者都不需要。
指导原则#
物理随机化只在
build()之后施加一次。 这些属性描述的是机体,而不是回合。每步都重置它们既浪费时间,也可能让求解器失稳。指令与状态随机化在重置时施加, 并用
envs_idx限定作用范围,这样只触碰刚刚终止的环境。让首维与
n_envs对齐。 批量大小不对的张量是最常见的错误。请用scene.n_envs以及实体的n_links或n_dofs来确定张量尺寸,而不要硬编码。随机化你测不准的量,而不是你已经测准的量。 对已经准确知道的参数扩大分布,只会让任务更难学习。应当让每个区间以你的最佳估计为中心,并扩展到足以覆盖你的不确定性。
另见#
并行仿真:批量环境的工作方式,以及逐环境张量为何成本很低。
使用 RL 训练运动策略:使用这些技术的端到端策略。
编写高效的 RL 环境:让随机化后的环境保持高效。