From ea363212adc789e47a96fc29ca900e824f46a4f2 Mon Sep 17 00:00:00 2001 From: Israel Barros Date: Sun, 22 Jun 2025 02:25:54 -0300 Subject: [PATCH] feat: add DistanceNoiseStd and FlipHitProbability to RayPerceptionSensor --- com.unity.ml-agents/CHANGELOG.md | 14 ++++++ .../RayPerceptionSensorComponentBaseEditor.cs | 4 ++ .../Runtime/Sensors/RayPerceptionSensor.cs | 50 ++++++++++++++++++- .../RayPerceptionSensorComponentBase.cs | 29 ++++++++++- docs/Learning-Environment-Design-Agents.md | 2 + 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/com.unity.ml-agents/CHANGELOG.md b/com.unity.ml-agents/CHANGELOG.md index 876608252c..8a69850f0b 100755 --- a/com.unity.ml-agents/CHANGELOG.md +++ b/com.unity.ml-agents/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Added +- **Sensors** – Two new noise parameters are now available on RayPerceptionSensorComponent: + + | Parameter | Default | Description | + |-----------|---------|-------------| + | `DistanceNoiseStd` | `0` | Standard deviation (σ) of Gaussian noise added to HitFraction, which represents the normalized hit distance (ranging from 0 to 1). | + | `FlipHitProbability` | `0` | The probability of randomly flipping the HasHit boolean value (0 → 1 or 1 → 0). A value of 0 means no flipping occurs. | + + Leaving both parameters at `0` reproduces the exact behaviour of previous releases. + +### Fixed +- n/a + ### Major Changes #### com.unity.ml-agents / com.unity.ml-agents.extensions (C#) - Upgraded to Inference Engine 2.2.1 (#6212) diff --git a/com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs b/com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs index 7fdfbc7c52..91de96a676 100644 --- a/com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs +++ b/com.unity.ml-agents/Editor/RayPerceptionSensorComponentBaseEditor.cs @@ -72,6 +72,10 @@ protected void OnRayPerceptionInspectorGUI(bool is3d) EditorGUILayout.PropertyField(so.FindProperty("rayHitColor"), true); EditorGUILayout.PropertyField(so.FindProperty("rayMissColor"), true); + // ───────── Parameters Noise ───────── + EditorGUILayout.PropertyField(so.FindProperty("m_DistanceNoiseStd"), true); + EditorGUILayout.PropertyField(so.FindProperty("m_FlipHitProbability"), true); + EditorGUI.indentLevel--; if (EditorGUI.EndChangeCheck()) { diff --git a/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs b/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs index 332b40ad56..f865de604e 100644 --- a/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs +++ b/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensor.cs @@ -254,6 +254,10 @@ public class RayPerceptionSensor : ISensor, IBuiltInSensor bool m_UseBatchedRaycasts; + // ───────── Parameters Noise ───────── + float m_DistanceNoiseStd; + float m_FlipHitProb; + /// /// Time.frameCount at the last time Update() was called. This is only used for display in gizmos. /// @@ -269,12 +273,16 @@ internal int DebugLastFrameCount /// /// The name of the sensor. /// The inputs for the sensor. - public RayPerceptionSensor(string name, RayPerceptionInput rayInput) + public RayPerceptionSensor(string name, RayPerceptionInput rayInput, float distanceNoiseStd, float flipHitProb) { m_Name = name; m_RayPerceptionInput = rayInput; m_UseBatchedRaycasts = rayInput.UseBatchedRaycasts; + // ───────── Parameters Noise ───────── + m_DistanceNoiseStd = distanceNoiseStd; + m_FlipHitProb = flipHitProb; + SetNumObservations(rayInput.OutputSize()); m_DebugLastFrameCount = Time.frameCount; @@ -329,7 +337,20 @@ public int Write(ObservationWriter writer) // For each ray, write the information to the observation buffer for (var rayIndex = 0; rayIndex < numRays; rayIndex++) { - m_RayPerceptionOutput.RayOutputs?[rayIndex].ToFloatArray(numDetectableTags, rayIndex, m_Observations); + var ro = m_RayPerceptionOutput.RayOutputs[rayIndex]; + + // Used to debug values + float before = ro.HitFraction; + bool hit0 = ro.HasHit; + + // Aplly noise + ApplyNoise(ref ro); + + // Debug Value + //if (before != ro.HitFraction) Debug.Log($"Ray {rayIndex} | dist {before} → {ro.HitFraction} | hit {hit0}→{ro.HasHit}"); + + m_RayPerceptionOutput.RayOutputs[rayIndex] = ro; + ro.ToFloatArray(numDetectableTags, rayIndex, m_Observations); } // Finally, add the observations to the ObservationWriter @@ -666,5 +687,30 @@ int rayIndex return rayOutput; } + + /// + /// Applies simulated noise to ray hit data to mimic real-world sensor or environmental conditions. + /// Includes Gaussian-like noise on hit distance and random flipping of the hit detection state. + /// + /// Reference to the RayOutput object to modify with applied noise. + void ApplyNoise(ref RayPerceptionOutput.RayOutput ro) + { + // Apply noise to the normalized hit distance if m_DistanceNoiseStd > 0 + if (m_DistanceNoiseStd > 0f) + { + // Generate a random value in the range [-m_DistanceNoiseStd, m_DistanceNoiseStd] + // Add it to the original HitFraction and clamp the result between 0 and 1 + ro.HitFraction = Mathf.Clamp01( + ro.HitFraction + + UnityEngine.Random.Range(-m_DistanceNoiseStd, m_DistanceNoiseStd)); + } + + // Randomly flip the hit detection state based on m_FlipHitProb + if (m_FlipHitProb > 0f && UnityEngine.Random.value < m_FlipHitProb) + { + // Invert the boolean HasHit value: true becomes false, and vice versa + ro.HasHit = !ro.HasHit; + } + } } } diff --git a/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs b/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs index dbb93a2ea0..f059fcc35e 100644 --- a/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs +++ b/com.unity.ml-agents/Runtime/Sensors/RayPerceptionSensorComponentBase.cs @@ -180,6 +180,28 @@ public bool UseBatchedRaycasts [SerializeField] internal Color rayMissColor = Color.white; + // ───────── Parametes Noise ───────── + /// + /// Standard deviation of Gaussian noise added to the normalized ray distance. + /// A value of 0 means no noise is applied. + /// + [HideInInspector] + [SerializeField, Range(0f, 0.5f)] + [Header("Noise", order = 999)] + [Tooltip("Standard deviation of Gaussian noise added to the normalized ray distance (HitFraction varies from 0 to 1)")] + internal float m_DistanceNoiseStd; + + /// + /// Probability of randomly flipping the HasHit bit (0 → 1 or 1 → 0). + /// This simulates sensor or detection errors in raycasting. + /// A value of 0 means no flipping occurs. + /// + [HideInInspector] + [SerializeField, Range(0f, 0.5f)] + [Tooltip("Probability of flipping the HasHit bit (0 → 1 or 1 → 0).")] + float m_FlipHitProbability; + + [NonSerialized] RayPerceptionSensor m_RaySensor; @@ -223,7 +245,12 @@ public override ISensor[] CreateSensors() { var rayPerceptionInput = GetRayPerceptionInput(); - m_RaySensor = new RayPerceptionSensor(m_SensorName, rayPerceptionInput); + m_RaySensor = new RayPerceptionSensor( + m_SensorName, + rayPerceptionInput, + m_DistanceNoiseStd, + m_FlipHitProbability + ); if (ObservationStacks != 1) { diff --git a/docs/Learning-Environment-Design-Agents.md b/docs/Learning-Environment-Design-Agents.md index 8000a88465..d006300bd6 100644 --- a/docs/Learning-Environment-Design-Agents.md +++ b/docs/Learning-Environment-Design-Agents.md @@ -509,6 +509,8 @@ Both sensor components have several settings: but if using custom models the left-to-right layout that matches the spatial structuring can be preferred (e.g. for processing with conv nets). - _Use Batched Raycasts_ (3D only) Whether to use batched raycasts. Enable to use batched raycasts and the jobs system. +- _Distance Noise Std_ This parameter controls the standard deviation (σ) of Gaussian noise added to HitFraction, a value representing the normalized hit distance along the ray. A value of 0 means no noise is applied. HitFraction ranges from 0 (no hit) to 1 (hit at maximum ray distance). +- _FlipHitProbability_ Sets the probability that the HasHit result will be flipped (e.g., hit becomes no hit, or vice versa). Useful for simulating sensor noise or unreliable detection.. In the example image above, the Agent has two `RayPerceptionSensorComponent3D`s. Both use 3 Rays Per Direction and 90 Max Ray Degrees. One of the components had