🧰 实现自定义传感器#
本页面面向希望添加自定义传感器类型的高级用户。它是 传感器管线 的作者视角配套文档:管线页面解释运行时如何执行,本页关注需要重写哪些 hook、它们必须满足什么 shape/dtype 契约,以及自动插件注册如何工作。
大多数情况下,从 SimpleSensor 派生,并且只重写你需要的 hook。直接从 Sensor 派生只适合完全绕过标准管线的传感器,内置相机就是这种情况。
添加传感器需要写什么#
Artifact |
位置 |
作用 |
|---|---|---|
|
|
面向用户的 dataclass,携带每个传感器实例的参数。继承 |
|
传感器实现旁边 |
该传感器类所有实例共享的运行时状态。继承 |
|
传感器实现旁边 |
传感器类本身。通常继承 |
可选的 |
传感器实现旁边 |
如果传感器返回多个张量,例如 IMU 返回 |
可选的 |
传感器实现旁边 |
只有当多个不同传感器类型需要共享昂贵资源时才需要,例如共享碰撞 BVH。 |
只要选项类和传感器类所在模块都已导入,Genesis 会自动把二者配对。用户只需要创建选项实例并传给 scene.add_sensor(...)。
自动注册#
传感器不需要手动注册。定义一个用选项类参数化的 Sensor 子类就足够了;类体执行时,框架会记录这组配对。
支持两种放置方式:
内置传感器:选项在
genesis/options/sensors/*.py,传感器在genesis/engine/sensors/*.py,包的__init__已负责导入。第三方插件:把
MyOptions和MySensor放在同一个 Python 包的兄弟子模块中:my_sensor_plugin/ __init__.py options.py # class MyOptions(SimpleSensorOptions["MySensor"]): ... sensor.py # class MySensor(SimpleSensor[MyOptions, MyContext, MyMetadata]): ...
只要构造
MyOptions()之前导入过my_sensor_plugin.options,Genesis 在第一次scene.add_sensor(MyOptions(...))时会惰性导入兄弟模块my_sensor_plugin.sensor并解析配对。
选择基类#
Base |
何时使用 |
|---|---|
|
绝大多数情况。标准逐步管线:raw -> physics imperfections -> transform -> hardware imperfections -> post-process -> delay sampling。 |
|
与上面相同,但 |
|
相机式传感器,在 |
|
只有当标准管线都不适用时使用。你需要自己实现 |
第二个泛型参数是 shared context。没有上下文时写 None;多个不同传感器类型需要共享同一资源时,声明 SharedSensorContext 子类。
常用 mixin:
KinematicSensorOptionsMixin:用于连接到KinematicEntity或只需要运动学信息的传感器。RigidSensorOptionsMixin:用于依赖刚体物理的传感器,例如接触、IMU、触觉。通常与SimpleSensorOptions多继承。传感器侧的
RigidSensorMixin/RigidSensorMetadataMixin会提供 typedsolver字段和常用 link bookkeeping。
SimpleSensor 的 hook#
所有 hook 都是 @classmethod,会接收 shared_metadata 以及需要填充的缓冲区。产生数据的 hook 还会收到 shared_context,没有上下文时为 None。hook 每步按传感器类调用一次,不会逐实例、逐环境调用。
必须重写#
_get_return_format(self) -> tuple[...]#
实例方法,返回 read() 的 shape。shape 按实例确定,因为传感器选项可能决定返回形状。
def _get_return_format(self) -> tuple[int, ...]:
return (3,)
约定:
单个张量返回
(N,)。多张量返回使用 tuple of tuples,例如 IMU 的
((3,), (3,), (3,)),必须与DataT的字段匹配。
_get_cache_dtype(cls) -> torch.dtype#
类方法,返回 read() 的 dtype。dtype 按类统一,不支持同一传感器类的不同实例返回不同 dtype。
@classmethod
def _get_cache_dtype(cls) -> torch.dtype:
return gs.tc_float
可选 hook#
_get_intermediate_format(self) -> tuple[...]#
返回管线内部缓冲区的 shape。默认与 _get_return_format() 相同。只要 _post_process 改变 shape,或需要显式声明 intermediate space,都应重写。
def _get_intermediate_format(self) -> tuple[int, ...]:
return self._get_return_format()
_get_intermediate_dtype(cls) -> torch.dtype#
返回管线内部缓冲区的 dtype。默认与 _get_cache_dtype() 相同。ContactSensor 这类“float 中间值,bool 返回值”的传感器应重写它。
@classmethod
def _get_intermediate_dtype(cls) -> torch.dtype:
return gs.tc_float
_update_current_timestep_data(...)#
默认行为是调用 _update_raw_data 生成 GT,镜像到 GT/measured timeline slot 0,然后对 measured slot 调用 _apply_physics_imperfections。如果某个传感器的物理噪声必须在同一个 kernel 内参与分支或命中计算,可以重写这个方法,同时写入 GT 和 measured 槽位。
uses_ring_pipeline#
类级标志,声明该类是否参与 ring-based per-step pipeline。默认 True。直接继承 Sensor 且完全绕过 rings 的子类,例如相机,应设置:
class MyCustomSensor(Sensor[MyOptions, None, MyMetadata]):
uses_ring_pipeline: ClassVar[bool] = False
Camera-style sensors via BaseCameraSensor#
BaseCameraSensor 是直接继承 Sensor 的基类,封装了 Genesis 相机共享的“读取时懒渲染”模式。自定义图像渲染传感器应优先使用它。
优点:
每步懒渲染并缓存;同一步多次
read()共享一次渲染。支持用
pos/lookat/up或显式offset_T连接到 link。默认 RGB 输出形状为
((h, w, 3),),dtype 为torch.uint8,read()返回CameraReturnType(rgb=...)。设置
uses_ring_pipeline = False,并拒绝delay > 0、jitter > 0、history_length > 0,避免用户请求该类型无法支持的功能。
需要实现两个 hook:
class MyCameraSensor(BaseCameraSensor[MyCameraOptions]):
def _apply_camera_transform(self, camera_T: torch.Tensor) -> None:
# camera_T 是 (4, 4) 世界坐标变换。把它应用到你的渲染器相机表示上。
...
def _render_current_state(self) -> None:
# 从当前姿态渲染场景,并写入该传感器在每类图像缓存中的槽位。
# 每个仿真步、每个相机最多调用一次。
...
完整示例可参考 RasterizerCameraSensor。
限制:默认返回固定为 RGB torch.uint8。如果需要深度、分割、法线或非 RGB 输出,需要重写 _get_return_format / _get_cache_dtype 并调整缓存,或退回到直接继承 Sensor。