| name | add-noise-model |
| description | Add a custom noise model for sensor data simulation. Use when: implementing a non-Gaussian noise type, adding distance-dependent noise, creating a new sensor-specific noise profile. |
Add a New Noise Model
Procedure for creating a custom noise model and integrating it with sensor devices.
When to Use
- Implementing a new noise type beyond Gaussian (e.g., salt-and-pepper, Perlin, structured)
- Adding distance-dependent or range-binned noise for lidar/depth sensors
- Creating a sensor-specific noise profile from real-world calibration data
Architecture
Noise (facade)
└── NoiseModel (abstract base)
├── GaussianNoiseModel (standard + dynamic bias)
└── CustomNoiseModel (extends Gaussian, range-binned, XML-parameterized)
Noise is the public API used by devices — handles threading via Parallel.For
NoiseModel is the abstract base with bias sampling, clamping, quantization
GaussianNoiseModel implements standard Gaussian with dynamic bias correlation
CustomNoiseModel extends Gaussian with range-binned noise from XML parameters
Procedure
1. Create the Noise Model Class
Create Assets/Scripts/Devices/Modules/NoiseModel/MyNoiseModel.cs:
using System;
public class MyNoiseModel : NoiseModel
{
private readonly double _myParam;
public MyNoiseModel(in SDFormat.Noise parameter)
: base(parameter)
{
_myParam = parameter.StdDev;
}
public override T Generate<T>(T data, float deltaTime)
{
var value = Convert.ToDouble(data);
var noise = ComputeNoise(value, deltaTime);
var output = value + bias + noise;
if (_quantized)
{
if (Math.Abs(_parameter.Precision - 0d) > Epsilon)
{
output = Math.Round(output / _parameter.Precision) * _parameter.Precision;
}
}
return (T)Convert.ChangeType(Clamp(output), typeof(T));
}
private double ComputeNoise(double value, float deltaTime)
{
var random = RandomNumberGenerator.GetNormal(0, _myParam);
return random;
}
}
2. Register in Noise Facade
Edit Assets/Scripts/Devices/Modules/Noise.cs — add a case in the constructor:
public Noise(in SDFormat.Noise noise)
{
switch (noise.Type)
{
case SDFormat.NoiseType.Gaussian:
case SDFormat.NoiseType.GaussianQuantized:
_noiseModel = new GaussianNoiseModel(noise);
if (noise.Type == SDFormat.NoiseType.GaussianQuantized)
_noiseModel.SetQuantization(true);
break;
case SDFormat.NoiseType.MyType:
_noiseModel = new MyNoiseModel(noise);
break;
default:
_noiseModel = null;
break;
}
}
If using a custom noise type string, add it to the SDFormat.NoiseType enum in the SDFormat package.
3. Use in a Device
private Noise _noise;
public void SetupNoise(in SDFormat.Noise noise)
{
_noise = new Noise(noise);
_noise.SetClampMin(0);
_noise.SetClampMax(maxRange);
}
protected override void GenerateMessage()
{
float value = ReadSensorValue();
_noise.Apply<float>(ref value, Time.fixedDeltaTime);
_msg.Value = value;
}
protected override void GenerateMessage()
{
float[] data = ReadSensorArray();
_noise.Apply<float>(data, Time.fixedDeltaTime);
}
NoiseModel Base Class API
protected readonly SDFormat.Noise _parameter;
protected double bias;
protected bool _quantized;
protected double clampMin, clampMax;
protected double Clamp(in double value);
protected double Expm1(in double value);
RandomNumberGenerator.GetNormal(mean, stddev);
RandomNumberGenerator.GetNormal();
RandomNumberGenerator.GetUniform();
Dynamic Bias (from GaussianNoiseModel)
If your model needs time-correlated bias drift:
protected double ComputeDynamicBias(in float deltaTime)
{
}
GPU Noise (Shader-Based)
For camera sensors, noise is applied on the GPU via AddGaussianNoise.shader:
_noiseMaterial = new Material(Shader.Find("Sensor/Camera/GaussianNoise"));
_noiseMaterial.SetFloat("_Mean", mean);
_noiseMaterial.SetFloat("_StdDev", stddev);
Threading
The Noise class uses Parallel.For with adaptive parallelism:
Parallel.For(0, data.Length, _parallelOptions, i =>
{
data[i] = _noiseModel.Generate(data[i], deltaTime);
});
Each Generate() call must be thread-safe — avoid shared mutable state. RandomNumberGenerator uses ThreadLocal<Random> internally.
Checklist