ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ML-Agents 自定义网格传感器(Custom Grid Sensors)完全指南:从 GridSensorBase 派生到自定义观测

2026/9/20 18:53:59 拓冰建站 浏览量
ML-Agents 自定义网格传感器(Custom Grid Sensors)完全指南:从 GridSensorBase 派生到自定义观测 ML-Agents 自定义网格传感器Custom Grid Sensors完全指南从 GridSensorBase 派生到自定义观测【免费下载链接】ml-agentsThe Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinforcement learning and imitation learning.项目地址: https://gitcode.com/gh_mirrors/ml/ml-agents网格传感器Grid Sensor为 ML-Agents 强化学习环境提供了一种基于俯视 2D 网格的观测方式可无死角地感知智能体周围区域内的物体。本文基于 Unity ML-Agents 官方文档《Custom Grid Sensors》展开结合仓库中的 GridSensorBase.cs、GridSensorComponent.cs、OneHotGridSensor.cs 与 CountingGridSensor.cs 源码系统讲解如何通过派生GridSensorBase与GridSensorComponent收集自定义属性作为观测以及包内置的两种网格传感器实现原理。读完本文你将掌握自定义网格传感器的完整开发流程、四个核心可重写方法的语义以及数据归一化与压缩之间的约束关系。Grid Sensor 与 RayCast 观测的取舍在自定义网格传感器之前先理解它解决什么问题。官方文档指出Grid Sensor 提供一种自顶向下视角的 2D 观测用于检测智能体周围的物体。与射线投射RayCasts相比它有两个关键差异无盲区射线只能探测有限数量的方向网格传感器则在整片网格区域内获得完整观测不存在间隙不被遮挡物体检测不会被智能体周边的其他物体阻断每个格子独立查询检测结果互不干扰。代价是更高的计算资源开销——网格越大、每格观测维度越高物理查询与数据编码的开销越大。因此 Grid Sensor 适合需要细粒度空间感知的场景例如需要区分某方向存在多个目标目标在网格中的精确位置等射线难以表达的信息。需要注意的适用前提根据 Learning-Environment-Design-Agents.md 的说明Grid Sensor 仅适用于 3D 环境在 2D 环境中不会正常工作。网格传感器的额外能力在于你可以从GridSensorBase基类派生在识别物体 Tag 之外收集自定义属性作为观测从而极大扩展 GridSensor 的用途。给 Agent 挂载 GridSensorComponent 后网格以俯视视角覆盖智能体周围区域图片来源Learning-Environment-Design-Agents.md创建自定义网格传感器的整体结构创建自定义网格传感器需要从两个类派生GridSensorBase传感器的实现本体定义传感器如何处理检测到的 Collider、数据长什么样、如何从检测对象构造观测GridSensorComponentUnity 组件层负责将传感器挂到 GameObject 上、管理参数并接入Agent的传感器系统。这种实现 组件的双层结构意味着GridSensorBase负责纯数据处理逻辑可在单元测试中独立验证GridSensorComponent负责与 Unity 场景、Inspector 交互二者通过CreateSensors()桥接见下文源码分析。派生自 GridSensorBase四个核心可重写方法GridSensorBase是网格传感器全部数据处理逻辑所在它在构造时完成网格布局初始化并在每帧感知时把检测结果写入观测缓冲区。根据用途你可能需要重写以下方法GetCellObservationSize()定义每格观测维度protected virtual int GetCellObservationSize() { return 1; }该方法返回每个格子的观测大小即写入dataBuffer的 float 数量默认值为1。从源码看它决定了整个传感器的观测规格在 GridSensorBase 构造函数 中m_NumCells m_GridSize.x * m_GridSize.z随后以ObservationSpec.Visual(m_CellObservationSize, m_GridSize.x, m_GridSize.z)构造观测规格因此总观测维度为每格维度 × 网格宽度 × 网格深度。重写GetObjectData时务必同步重写此方法否则写入缓冲区的数据量与实际分配的缓冲区不一致。GetObjectData()从检测对象构造观测protected virtual void GetObjectData(GameObject detectedObject, int tagIndex, float[] dataBuffer) { dataBuffer[0] tagIndex 1; }这是自定义网格传感器的核心入口。输入参数为detectedObject当前格子内检测到的 GameObjecttagIndex该对象 Tag 在DetectableTags列表中的索引从 0 开始dataBuffer待写入观测值的缓冲区其大小由GetCellObservationSize()决定。写入后的数据会从每个格子汇总作为观测发送给训练器。基类默认实现写入tagIndex 1注意基类默认编码不是 one-hot而是索引偏移而包内置的 OneHot 与 Counting 传感器则各自覆盖了此方法见下文。GridSensorBase.cs 的 XML 文档给出了一个覆盖示例——读取检测对象的 Rigidbody 速度作为观测protected override void GetObjectData(GameObject detectedObject, int tagIndex, float[] dataBuffer) { if (tagIndex Array.IndexOf(DetectableTags, RigidBodyObject)) { Rigidbody rigidbody detectedObject.GetComponentRigidbody(); dataBuffer[0] rigidbody.velocity.x; dataBuffer[1] rigidbody.velocity.y; dataBuffer[2] rigidbody.velocity.z; } }IsDataNormalized()声明数据是否归一化protected virtual bool IsDataNormalized() { return false; }返回观测数据是否归一化到 0~1 区间默认false。这个返回值直接决定了是否可以使用压缩观测PNG 压缩——压缩只支持归一化数据因为压缩本质上把观测值映射为图像像素通道。从 GridSensorBase.cs 的CompressionType属性 setter 可见底层约束set { if (!IsDataNormalized() value SensorCompressionType.PNG) { Debug.LogWarning($Compression type {value} is only supported with normalized data. The sensor will not compress the data.); return; } m_CompressionType value; }即当IsDataNormalized()返回false而请求 PNG 压缩时压缩设置会被静默忽略并给出警告。此外ValidateValues 方法 在启用 PNG 压缩时会对每次写入的数据逐值校验若出现0或1的数值会直接抛出UnityAgentsException提示数据必须归一化到 0-1 之间。当数据不在 0~1 区间但希望用压缩加速训练时应先在GetObjectData中自行归一化。官方文档给出的实践建议数据天然有界时归一化到可能范围即可。例如旋转角有界于0 ~ 360记录角度x时存x/360而非x数据无界时如位置、速度设置合理的 min/max 范围并据此归一化。GetProcessCollidersMethod()单元格内多对象的处理策略protected internal virtual ProcessCollidersMethod GetProcessCollidersMethod() { return ProcessCollidersMethod.ProcessClosestColliders; }当某个格子内检测到多个带有可检测 Tag 的对象时该方法决定传感器行为。ProcessCollidersMethod枚举定义于 GridSensorBase.cs目前提供两种枚举值语义适用场景ProcessClosestColliders默认只处理离智能体最近的 Collider每个格子的数据只由一个对象表示需要每格一个物体的空间表达ProcessAllColliders处理格子内所有检测到的 Collider每格数据具有可加性例如统计格子内的物体数量使用ProcessAllColliders时传入GetObjectData()的dataBuffer会包含同一格子内其他 Collider 已处理的数据因此你更可能对缓冲区做加减而不是整体覆盖。这一行为在 ProcessDetectedObject 方法 中有明确实现if (GetProcessCollidersMethod() ProcessCollidersMethod.ProcessAllColliders) { // 先把该格当前累计数据拷入 cellDataBuffer再做增量修改 Array.Copy(m_PerceptionBuffer, cellIndex * m_CellObservationSize, m_CellDataBuffer, 0, m_CellObservationSize); } else { // 只取最近 Collider先清空缓冲区再写入 Array.Clear(m_CellDataBuffer, 0, m_CellDataBuffer.Length); } GetObjectData(detectedObject, i, m_CellDataBuffer);这段源码清楚地展示了两种模式的本质区别ProcessAllColliders是累积式写入ProcessClosestColliders是覆盖式写入。派生自 GridSensorComponent把传感器接入 UnityGridSensorComponent继承自SensorComponent在 GridSensorComponent.cs 中定义。要创建你的传感器需要重写GetGridSensors()并返回想要使用的网格传感器数组public class CustomGridSensorComponent : GridSensorComponent { protected override GridSensorBase[] GetGridSensors() { return new GridSensorBase[] { new CustomGridSensor(...)}; } }它既可以返回多个不同的自定义网格传感器也可以混入包内自带的传感器。基类默认实现返回一个OneHotGridSensor见 GetGridSensors 默认实现。CreateSensors() 的内部装配流程理解组件如何工作有助于正确使用它。从 CreateSensors 方法 可以看到完整的装配链路创建BoxOverlapChecker实现自IGridPerception传入m_CellScale、m_GridSize、m_RotateWithAgent、m_ColliderMask、AgentGameObject、m_DetectableTags以及两个 Collider 缓冲区大小参数——这是物理查询的执行者创建调试用GridSensorBase(DebugGridSensor, ...)其压缩类型固定为SensorCompressionType.None因为 gizmo 数据是正整数压缩会触发校验异常调用GetGridSensors()获得自定义传感器数组若为空数组或null会抛出UnityAgentsException提示至少返回一个传感器只把m_GridPerception赋给第一个传感器确保物理查询每帧只更新一次其余传感器通过RegisterSensor注册若ObservationStacks ! 1每个传感器会用StackingSensor包裹以实现多帧观测堆叠。组件的 Inspector 参数基于 GridSensorComponentEditor.cs 的 Inspector 布局和组件的序列化字段各参数说明如下Sensor Name生成的网格传感器名称影响传感器排序运行时修改不生效Grid SettingsCell Scale / Grid Size每格大小与网格尺寸。GridSize只支持 2D 网格构造函数与属性 setter 都会强制y为 1源码中若m_GridSize.y ! 1会抛出UnityAgentsExceptionGridSensor only supports 2D grids.Editor 中也将 y 锁定为 1二者创建传感器后不可运行时修改Agent GameObject智能体根对象引用用于消歧与智能体同 Tag 的对象默认为当前 GameObjectRotate With Agent网格是否随智能体朝向旋转Detectable Tags可检测的 Tag 列表其长度直接决定 OneHot 每格观测维度Collider Mask物理层遮罩控制哪些层级的 Collider 参与检测Sensor SettingsObservation Stacks观测堆叠帧数范围 1~501 表示不堆叠Compression Type压缩类型默认 PNG仅在数据归一化时生效Collider and BufferInitial / Max Collider Buffer Size预分配缓冲区大小默认 4与绝对上限默认 500。源码注释说明使用OverlapBoxNonAlloc物理 API 预分配数组若发现 Collider 数达到InitialColliderBufferSize则数组翻倍扩容硬上限为 500Debug GizmoShow Gizmos / Gizmo Y Offset / Debug Colors是否显示网格 gizmo、gizmo 高度偏移、每个 Tag 对应的调试颜色。包内置的两种网格传感器仓库提供了两个开箱即用的网格传感器它们的实现也是编写自定义传感器的最佳参考。OneHotGridSensor默认的 one-hot 传感器这是GridSensorComponent使用的默认传感器检测带可检测 Tag 的对象观测为检测到的 Tag 索引的one-hot 表示。完整实现见 OneHotGridSensor.cs其四个方法的行为为GetCellObservationSize()detectableTags.LengthIsDataNormalized()trueGetProcessCollidersMethod()ProcessCollidersMethod.ProcessClosestCollidersGetObjectData()protected override void GetObjectData(GameObject detectedObject, int tagIndex, float[] dataBuffer) { dataBuffer[tagIndex] 1; }即每个格子用一个长度等于可检测 Tag 数的向量表示命中的 Tag 位置为 1其余为 0。由于数据天然归一化OneHot 传感器可直接使用 PNG 压缩。CountingGridSensor格子内物体计数CountingGridSensor是使用格子内所有 Collider的示例统计每种可检测 Tag 在格子中检测到的对象数量。完整实现见 CountingGridSensor.cs注意该文件带有MovedFrom(Unity.MLAgents.Extensions.Sensors)特性表明其命名空间历史上从 Extensions 迁移而来。其行为为GetCellObservationSize()detectableTags.LengthIsDataNormalized()falseGetProcessCollidersMethod()ProcessCollidersMethod.ProcessAllCollidersGetObjectData()protected override void GetObjectData(GameObject detectedObject, int tagIndex, float[] dataBuffer) { dataBuffer[tagIndex] 1; }注意这里用的是累加而非覆盖——与ProcessAllColliders累积语义严格对应。因为计数无上界、不满足 0~1 归一化约束该传感器不能使用数据压缩其构造函数中强制CompressionType SensorCompressionType.None见 CountingGridSensor.cs即便在组件上设置了 PNG 压缩也会被覆盖为 None。测试与验证仓库在 com.unity.ml-agents.tests 下提供了网格传感器的运行时测试其中 GridSensorTests.cs 覆盖了GridSensorBase、OneHotGridSensor、CountingGridSensor的观测规格、数据写入与压缩行为。编写自定义传感器时可以参考这些测试的断言方式验证GetCellObservationSize()与GetObjectData()写入的数据量是否匹配、归一化声明与压缩设置是否冲突。完整自定义示例流程综合以上内容一个自定义网格传感器的标准流程为派生GridSensorBase重写GetCellObservationSize()定义每格维度重写GetObjectData()从detectedObject提取自定义属性如速度、朝向、生命值写入dataBuffer重写IsDataNormalized()如实声明数据是否归一化若希望压缩训练则先归一化再返回true需要统计型观测时重写GetProcessCollidersMethod()返回ProcessAllColliders并在GetObjectData中做累加式写入派生GridSensorComponent重写GetGridSensors()返回上述传感器实例可多个在 Unity 中为 Agent 挂载自定义组件配置Detectable Tags、Collider Mask、网格尺寸与压缩类型运行时可开启Show Gizmos借助调试颜色检查各格子的检测结果是否符合预期。通过这一套扩展机制你可以把网格传感器从Tag 的 one-hot 地图升级为携带任意物理属性、游戏状态的自定义观测源在保持网格观测无盲区、抗遮挡优势的同时让强化学习智能体获得更丰富的空间特征输入。【免费下载链接】ml-agentsThe Unity Machine Learning Agents Toolkit (ML-Agents) is an open-source project that enables games and simulations to serve as environments for training intelligent agents using deep reinforcement learning and imitation learning.项目地址: https://gitcode.com/gh_mirrors/ml/ml-agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考