Newton 执行器(Actuator)API 深度指南:基于 Warp 的 GPU 加速关节力矩计算模型
【免费下载链接】newtonAn open-source, GPU-accelerated physics simulation engine built upon NVIDIA Warp, specifically targeting roboticists and simulation researchers.项目地址: https://gitcode.com/GitHub_Trending/newton9/newton
导读
newton.actuators是 Newton 物理仿真引擎中负责从仿真状态与控制目标计算关节力矩的模块,为机器人与仿真研究者提供一套模块化的执行器组件库:驱动(Drives)、限幅(Clamping)与延迟(Delay)。本文基于 docs/api/newton_actuators.rst 展开,结合模块源码(newton/_src/actuators/)与测试用例,完整讲解执行器的组成方式、Actuator的构造与单步执行流程、五种驱动与三种限幅的计算模型、延迟缓冲机制、显式/隐式两种力矩求解模式,以及 1.6 版本中从Controller系列到Drive系列的 API 迁移注意事项。读完本文,你将能够直接使用Actuator组装自定义关节控制器,并通过ModelBuilder.add_actuator将其接入仿真模型。
模块定位与设计思想
该模块在文档中被明确定位为:"GPU-accelerated actuator models for physics simulations",即面向物理仿真的 GPU 加速执行器模型库。其核心设计是一个模块化组合管线——由驱动(drive)、限幅(clamping)与延迟(delay)三类组件共同计算关节力矩:
- 组件被组合进一个
Actuator实例; - 在模型构建阶段通过
ModelBuilder.add_actuator注册(见 newton/_src/sim/builder.py); - 所有计算以 Warp kernel 形式在 GPU 上批量执行。
newton.actuators的公共导出(newton/actuators.py)与模块文档中的 autosummary 列表完全一致,共 14 个类、2 个函数:
Classes:Actuator、ActuatorParsed、ClampingBase、ClampingDCMotor、ClampingMaxEffort、ClampingPositionBased、ComponentKind、Delay、DriveBase、DriveNeuralLSTM、DriveNeuralMLP、DrivePD、DrivePID、JointSpaceResponse、SchemaNames
Functions:parse_actuator_prim、register_actuator_component
⚠️ 该 API 在文档中标记为
experimental(实验性):API 可能在没有事先通知的情况下变化,欢迎通过 issue 或讨论区反馈。这是使用前需要知晓的重要前提。
核心组件Actuator:延迟 → 驱动 → 限幅 的组合管线
Actuator是模块的枢纽类,源码位于 newton/_src/actuators/actuator.py。它的工作流程文档总结为:读取仿真状态/控制数组 → (可选)延迟指令输入 → 驱动计算力矩 → 限幅(力矩上限、饱和等)→ 将结果累加(scatter-add)进输出数组。调用方在步进执行器前必须先清零输出数组。
构造参数详解
Actuator.__init__的完整签名与语义如下(依据 actuator.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
indices | 必填 | 指向速度形态数组(速度、速度目标、前馈、力矩输出)的 DOF 索引,形状(N,) |
drive | 必填 | 计算原始力矩的驱动(DriveBase子类) |
delay | None | 可选的输入延迟组件(Delay实例) |
clamping | None | 限幅对象列表(后置力矩边界) |
pos_indices | indices | 指向坐标形态数组(state.joint_q)的索引;当位置与速度数组布局不同(如浮动基座或球关节)时与indices不同 |
target_pos_indices | 依newton.use_coord_layout_targets而定 | 指向control.joint_target_q的索引;该标志在构造时读取一次,构造后切换不再生效 |
effort_indices | indices | 指向力矩输出数组的 DOF 索引;用于耦合传动或肌腱驱动关节 |
state_pos_attr | "joint_q" | sim_state上的位置属性名 |
state_vel_attr | "joint_qd" | sim_state上的速度属性名 |
control_target_pos_attr | "joint_target_q" | 控制结构上的目标位置属性 |
control_target_vel_attr | "joint_target_qd" | 控制结构上的目标速度属性 |
control_feedforward_attr | "joint_act" | 前馈力矩属性,传None跳过 |
control_output_attr | "joint_f" | 限幅后的输出力矩属性 |
control_computed_output_attr | None | 限幅前原始力矩属性,None表示不写 |
requires_grad | False | 为可微仿真分配带梯度支持的中间数组 |
源码中的典型用法:
actuator = Actuator( indices=indices, drive=DrivePD(kp=kp, kd=kd), delay=Delay(delay_steps=wp.array([5, 5], dtype=wp.int32), max_delay=5), clamping=[ClampingMaxEffort(max_effort=max_effort)], ) # 仿真循环 actuator.step(sim_state, sim_control, state_a, state_b, dt=0.01)step()的五步执行流程
Actuator.step的流程(actuator.py)可以精确拆解为:
- 延迟读取:从
current_state读取每个 DOF 的延迟目标(缓冲为空时回退到当前目标); - 力矩计算:将原始力矩写入
_computed_forces(显式控制律,或隐式的端步求解); - 限幅:将受限力矩写入
_applied_forces。显式模式在驱动律之后限幅;隐式模式在求解内部强制执行; - Scatter-add 累加:将施加力矩(可选地含计算力矩)累加进输出数组——调用方必须在遍历执行器前清零输出(如
control.joint_f.zero_()); - 状态更新:先更新驱动状态,再写入延迟缓冲(将当前目标推入
next_state)。
该流程通过_scatter_add_kernel(actuator.py)实现第 4 步的 GPU 并行累加。
有状态组件与 CUDA Graph 支持
is_stateful():若延迟或驱动维护内部状态则返回True;state():返回新的组合状态Actuator.State(含delay_state与drive_state);若完全无状态则返回None。有状态执行器在step时必须同时提供current_act_state与next_act_state;is_graphable():所有组件是否可被 CUDA Graph 捕获;State.reset(mask):按掩码(长度 N 的布尔数组,True项重置)或整体重置组合状态;State.assign(other):将另一状态的数值复制进来。assign对 Warp 数组使用dst.assign(src),对 Torch 张量在torch.inference_mode()下用copy_,并严格校验类型、形状与声明字段(见 actuator.py)。
assign的一个重要使用场景是 CUDA Graph:Graph 记录的是缓冲地址而非 Python 变量名,在奇数长度捕获区域的边界处,用state_0.assign(state_1)替代最终的状态交换,可为下一次回放保留推进后的状态:
for i in range(steps): control.joint_f.zero_() actuator.step(state, control, state_0, state_1, dt=0.01) if steps % 2 == 1 and i == steps - 1: state_0.assign(state_1) else: state_0, state_1 = state_1, state_0驱动(Drive):力矩控制律
DriveBase(newton/_src/actuators/drives/base.py)是所有驱动的基类,定义了组件的"验证契约":resolve_arguments负责在参数批量打包进 Warp 数组之前校验标量参数值(如kp >= 0);__init__只接收预构建数组并校验形状——回读数组内容做值校验会强制每次构造都发生同步的 device-to-host 拷贝。子类必须实现compute(计算力矩写入forces[i])与resolve_arguments,并可覆写finalize、is_stateful、is_graphable、state、update_state等钩子。
DrivePD:无状态 PD 驱动
力矩律(newton/_src/actuators/drives/drive_pd.py):
effort = const_effort + feedforward + kp * (target_pos - current_pos) + kd * (target_vel - current_vel)kp:比例增益 [N/m 或 N·m/rad],形状(N,),resolve_arguments要求非负;kd:微分增益 [N·s/m 或 N·m·s/rad];const_effort:常数偏置力矩 [N 或 N·m],可为None跳过。
该驱动无状态(is_stateful() == False),可被 CUDA Graph 捕获,是默认的显式控制律主力。
DrivePID:带抗饱和积分的有状态 PID 驱动
力矩律(newton/_src/actuators/drives/drive_pid.py):
effort = const_effort + feedforward + kp * (target_pos - current_pos) + ki * integral(target_pos - current_pos) + kd * (target_vel - current_vel)关键点:
ki:积分增益 [N/(m·s) 或 N·m/(rad·s)];integral_max:抗饱和(anti-windup)上限 [m·s 或 rad·s],resolve_arguments默认math.inf,要求非负;- 维护
State.integral(位置误差的积分累加),State.reset支持按掩码在 GPU 上清零(_masked_zero_1dkernel); - 显式模式下,
_pid_effort_kernel在单 kernel 内完成积分累加、wp.clamp限幅与力矩计算; - 隐式模式下,
prepare_implicit通过_pid_prepare_kernel将ki * integral折入参数包的常数列,求解时积分项作为该步常数处理。
DriveNeuralMLP / DriveNeuralLSTM:神经网络驱动
DriveNeuralMLP与DriveNeuralLSTM允许用训练好的神经网络(多层感知机 / LSTM 循环网络)替代解析控制律,直接从状态与目标计算力矩,适用于学习型控制器(如模仿学习、强化学习策略部署)场景。它们是有状态的(LSTM 需维护隐状态),且与ImplicitOptions配合时通过prepare_implicit做逐步线性化(见下文隐式模式)。
限幅(Clamping):力矩边界与饱和特性
ClampingBase(newton/_src/actuators/clamping/base.py)限幅组件堆叠在驱动之上,用于约束输出力矩:对称限幅、速度相关饱和、位置相关曲线等。它们从源力矩缓冲读取、向目标缓冲写入有界值;当src与dst是同一数组时即为原地更新——Actuator对第一个限幅使用不同数组(保留原始驱动输出),后续限幅使用同一数组。
模块内置三种限幅:
ClampingMaxEffort:对称最大力矩限幅
最简单的对称硬限幅|effort| <= max_effort,是最常用的保护性边界(如 add_actuator 文档示例中的{'max_effort': 50.0})。
ClampingDCMotor:直流电机四象限力-速饱和
该限幅复现直流电机的力-速特性曲线(newton/_src/actuators/clamping/clamping_dc_motor.py):
effort_max(vel) = min(saturation_effort * (1 - vel / velocity_limit), max_motor_effort) effort_min(vel) = max(saturation_effort * (-1 - vel / velocity_limit), -max_motor_effort)- 零速时电机可输出至多 ±
saturation_effort(被max_motor_effort封顶); - 速度接近
velocity_limit时,运动方向上的可用力矩降至零; - 参数校验约束:
saturation_effort >= 0、velocity_limit > 0、max_motor_effort >= 0;当velocity_limit有限时saturation_effort必须有限,否则在v == v_lim处会产生inf * 0 = NaN。
一个实现细节:角速度(corner velocity,即包络达到max_motor_effort的速度)不再缓存,而是在 kernel 内从实时参数推导(corner = velocity_limit * (1 + max_motor_effort / saturation_effort)),以避免用户重调参数后缓存过期。因此ClampingDCMotor.corner_velocity属性已在 1.6 中标记为 deprecated,仅用于兼容。
ClampingPositionBased:位置相关限幅
依据关节位置改变力矩边界,用于模拟关节运动范围相关的力矩限制(如软限位、随姿态变化的驱动能力)。
延迟(Delay):逐 DOF 的指令输入延迟
Delay(newton/_src/actuators/delay.py)使用深度为max_delay的环形缓冲延迟指令输入(控制目标与前馈项):
- 每个 DOF 拥有独立的滞后步数
delay_steps(形状(N,),单位为"执行器时间步"); - 缓冲按所有 DOF 的最大滞后值统一分配深度,因此不同延迟步数的 DOF 可以共享同一执行器组;
- 缓冲状态
State包含三个(buf_depth, N)的二维数组(位置/速度/前馈目标)与逐 DOF 的num_pushes计数、设备侧的write_idx(write_idx放在设备侧以支持 Graph 捕获); reset(mask)支持按掩码清零指定 DOF 的缓冲列并重置计数。
延迟始终产生输出:缓冲为空(如刚复位)或某 DOFdelay_steps == 0时直接使用当前指令;缓冲未填满时,滞后被钳制到可用历史深度,返回最旧的可用条目(见_delay_read_kernel中lag = min(delays[i] - 1, n - 1)与read_idx = (write_idx - lag + buf_depth) % buf_depth)。
显式与隐式两种力矩求解模式
显式模式(默认)
控制律在当前状态上求值,步进内采用零阶保持(zero-order hold)。这是Actuator的默认行为(_EffortModeExplicit),也是step()流程第 2 步的标准路径。
隐式模式:set_effort_mode_implicit
Actuator.set_effort_mode_implicit(response, options)(actuator.py)将力矩计算切换到隐式模式:控制律针对预测的步末状态求解,然后再进入物理求解器。其数学形式(newton/_src/actuators/effort_mode_implicit.py):
r(p) = p - h * g(q(p), qd(p)) = 0 qd(p) = qd + A * p q(p) = q + h * qd(p)其中h为时间步长,g为带限幅的驱动力律,A为JointSpaceResponse提供的耦合逆质量响应。注意预测中只计入该执行器自身的冲量——重力、其他外力、同关节组上的其他执行器以及未走执行器路径的关节驱动都不参与预测。
参数说明:
response:JointSpaceResponse,提供耦合有效逆质量 [1/kg 或 1/(kg·m²)],需每步在step前刷新一次;options:Actuator.ImplicitOptions,默认值如下(effort_mode_implicit.py):
| 选项 | 默认值 | 含义 |
|---|---|---|
max_iters | 4 | 每个关节组的最大 Newton 迭代次数 |
residual_tol | 1.0e-5 | 残差向量范数低于该值即停止 [N·s 或 N·m·s] |
update_tol | 1.0e-5 | 冲量更新向量范数低于该值即停止 [N·s 或 N·m·s] |
fd_epsilon | 1.0e-4 | 速度空间上的相对前向有限差分步长(无量纲) |
derivative_floor | 1.0e-8 | 消元与回代时使用的最小 Jacobian 主元(无量纲) |
warm_start | WarmStart.EXPLICIT | 初猜:从钳制后的显式力矩冲量(EXPLICIT)或零冲量(ZERO)出发 |
重要限制:隐式求解不可微。若执行器以requires_grad=True构建,调用set_effort_mode_implicit会抛出NotImplementedError——Newton 求解没有伴随(adjoint),且神经驱动会打开自己的wp.Tape,无法嵌套在外层 tape 内。文档明确建议:需要可微仿真时构建Actuator(..., requires_grad=False)(指需要隐式模式时)。
隐式模式的支持通过DriveBase.evaluate_force/ClampingBase.evaluate_clamp这两个@wp.func入口实现:驱动与限幅将自身参数打包进(N, P)的二维参数数组(bind_params),求解 kernel 以float64精度调用;prepare_implicit允许驱动在每步求解前就地重写参数包(如 PID 折入积分项、神经网络按当前状态线性化)。限幅按列表顺序由内向外组合(_compose_clamps),只有可隐式化的限幅才进入链;ClampingDCMotor在隐式求解内使用预测的步末速度qd,使力-速包络自洽。
USD 解析:从场景描述构建执行器
ActuatorParsed、ComponentKind、SchemaNames、parse_actuator_prim、register_actuator_component共同构成从 USD 场景描述自动解析执行器配置的子系统(newton/_src/actuators/usd_parser.py)。
ComponentKind:执行器组件模式的分类枚举——DRIVE(驱动)、CLAMPING(限幅)、DELAY(延迟)三个成员;ActuatorParsed:解析一个 USD 执行器 prim 的结果,drive_class+drive_kwargs描述驱动,component_specs存放其余组件(延迟、限幅)的(类, kwargs)列表,target_path为被驱动关节的 USD prim 路径;register_actuator_component:注册自定义执行器组件,使parse_actuator_prim能识别用户扩展的 schema;SchemaNames:集中管理各组件 schema 的命名常量。
这使得执行器定义可以声明式地写在 USD 资产中,与代码构建方式(ModelBuilder.add_actuator)互补。
通过ModelBuilder.add_actuator注册执行器
在模型构建阶段,逐 DOF 注册外部执行器的入口是ModelBuilder.add_actuator(newton/_src/sim/builder.py):
model_builder.add_actuator( drive_class=DrivePD, index=dof_index, # 指向 joint_qd 形态数组的 DOF 索引 clamping=[(ClampingMaxEffort, {"max_effort": 50.0})], # 后置限幅 delay_steps=3, # 可选:输入延迟时间步数 pos_index=None, # 可选:joint_q 形态索引,默认等于 index kp=100.0, kd=5.0, # 透传给 drive_class 的逐 DOF 参数 )源码层面的行为要点:
- 对相同的
drive_class、clamping类型与一致的共享参数,多次调用会在finalize阶段合并成一个Actuator实例;同一组内支持不同的delay_steps值,缓冲深度按max(delay_step_values)分配; drive_class.resolve_arguments(kwargs)负责解析参数并填充默认值,未识别的参数会被add_actuator发出警告并忽略;- 共享参数(
SHARED_PARAMS)与逐 DOF 数组参数被分开处理,逐 DOF 参数打包为(N,)数组; pos_index在浮动基座或球关节等joint_q与joint_qd布局不同的关节上必须显式指定。
1.6 版本 API 迁移:Controller→Drive
模块文档用专门的 Deprecated 表格列出了 1.6 版废弃的旧名称与迁移指引:
| 废弃名称 | 迁移指引 |
|---|---|
Clamping | Deprecated in 1.6; useClampingBaseinstead |
Controller | Deprecated in 1.6; useDriveBaseinstead |
ControllerNeuralLSTM | Deprecated in 1.6; useDriveNeuralLSTMinstead |
ControllerNeuralMLP | Deprecated in 1.6; useDriveNeuralMLPinstead |
ControllerPD | Deprecated in 1.6; useDrivePDinstead |
ControllerPID | Deprecated in 1.6; useDrivePIDinstead |
代码层面的实现事实(newton/actuators.py):
- 新名称全部进入
__all__,旧名称不再出现在__all__中; - 旧名称通过
__getattr__提供兼容别名,访问时发出DeprecationWarning(如newton.actuators.Controller→newton.actuators.DriveBase); - 迁移涉及的不止模块级符号:
Actuator(controller=...)关键字、Actuator.controller属性、Actuator.State.controller_state、ActuatorParsed.controller_class/controller_kwargs、ModelBuilder.add_actuator(controller_class=...)、ComponentKind.CONTROLLER全部保留兼容但触发DeprecationWarning; - 同时指定新旧参数(如
drive=与controller=一起传)会抛出TypeError("Specify only one of ..."),防止歧义。
该迁移行为由 newton/tests/test_actuator_drive_api.py 全面覆盖:测试断言旧名称访问必然伴随 DeprecationWarning、新旧别名指向同一对象、混用新旧参数抛TypeError,以及JointSpaceResponse.__init__与refresh的公开方法类型标注保持不变({"model": newton.Model}与{"state": newton.State})。
总结
newton.actuators以"驱动 → 限幅 → 延迟"的模块化组合为核心,把关节力矩计算从物理求解器中解耦出来,形成一套完全在 GPU 上批量执行的执行器系统:DrivePD/DrivePID覆盖经典解析控制律,DriveNeuralMLP/DriveNeuralLSTM支持学习型策略,三种Clamping提供从对称限幅到电机力-速饱和的边界控制,Delay提供逐 DOF 指令延迟,而显式/隐式双模式让用户可在控制带宽与端步一致性之间权衡。无论是通过 Python 逐 DOF 注册(ModelBuilder.add_actuator)还是通过 USD 场景描述(parse_actuator_prim)声明,都能快速接入仿真管线;迁移到 1.6 时只需将Controller*家族替换为Drive*家族即可。
【免费下载链接】newtonAn open-source, GPU-accelerated physics simulation engine built upon NVIDIA Warp, specifically targeting roboticists and simulation researchers.项目地址: https://gitcode.com/GitHub_Trending/newton9/newton
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考