Skip to content

Instantly share code, notes, and snippets.

@TigerHix
Forked from davidfoster/KalmanFilterFloat.cs
Created November 8, 2022 07:09
Show Gist options
  • Save TigerHix/d5de6fef0cf889fbbd4b7fdedc93d20c to your computer and use it in GitHub Desktop.
Save TigerHix/d5de6fef0cf889fbbd4b7fdedc93d20c to your computer and use it in GitHub Desktop.
Simple Kalman filtering in Unity.
using System.Collections.Generic;
/// <summary>A Kalman filter implementation for <c>float</c> values.</summary>
public class KalmanFilterFloat {
//-----------------------------------------------------------------------------------------
// Constants:
//-----------------------------------------------------------------------------------------
public const float DEFAULT_Q = 0.000001f;
public const float DEFAULT_R = 0.01f;
public const float DEFAULT_P = 1;
//-----------------------------------------------------------------------------------------
// Private Fields:
//-----------------------------------------------------------------------------------------
private float q;
private float r;
private float p = DEFAULT_P;
private float x;
private float k;
//-----------------------------------------------------------------------------------------
// Constructors:
//-----------------------------------------------------------------------------------------
// N.B. passing in DEFAULT_Q is necessary, even though we have the same value (as an optional parameter), because this
// defines a parameterless constructor, allowing us to be new()'d in generics contexts.
public KalmanFilterFloat() : this(DEFAULT_Q) { }
public KalmanFilterFloat(float aQ = DEFAULT_Q, float aR = DEFAULT_R) {
q = aQ;
r = aR;
}
//-----------------------------------------------------------------------------------------
// Public Methods:
//-----------------------------------------------------------------------------------------
public float Update(float measurement, float? newQ = null, float? newR = null) {
// update values if supplied.
if (newQ != null && q != newQ) {
q = (float)newQ;
}
if (newR != null && r != newR) {
r = (float)newR;
}
// update measurement.
{
k = (p + q) / (p + q + r);
p = r * (p + q) / (r + p + q);
}
// filter result back into calculation.
float result = x + (measurement - x) * k;
x = result;
return result;
}
public float Update(List<float> measurements, bool areMeasurementsNewestFirst = false, float? newQ = null, float? newR = null) {
float result = 0;
int i = (areMeasurementsNewestFirst) ? measurements.Count - 1 : 0;
while (i < measurements.Count && i >= 0) {
// decrement or increment the counter.
if (areMeasurementsNewestFirst) {
--i;
}
else {
++i;
}
result = Update(measurements[i], newQ, newR);
}
return result;
}
public void Reset() {
p = 1;
x = 0;
k = 0;
}
}
using System.Collections.Generic;
/// <summary>A Kalman filter implementation for any type of value which can added and multiplied.</summary>
/// <remarks>
/// Determining whether the type can be added and multiplied occurs at runtime via the <c>dynamic</c> keyword.
/// Note that if you use this with the incorrect data type (such as a <c>Quaternion</c>, which cannot be added),
/// the error will occur at runtime.
///
/// <c>dynamic</c> also incurs a runtime cost, so if performance is crucial, it is suggested a concrete Kalman
/// filter implementation be used such as <c>KalmanFilterFloat</c> or <c>KalmanFilterVector3</c>.
/// </remarks>
public class KalmanFilter<T> {
//-----------------------------------------------------------------------------------------
// Constants:
//-----------------------------------------------------------------------------------------
public const float DEFAULT_Q = 0.000001f;
public const float DEFAULT_R = 0.01f;
public const float DEFAULT_P = 1;
//-----------------------------------------------------------------------------------------
// Private Fields:
//-----------------------------------------------------------------------------------------
private float q;
private float r;
private float p = DEFAULT_P;
private T x;
private float k;
//-----------------------------------------------------------------------------------------
// Constructors:
//-----------------------------------------------------------------------------------------
// N.B. passing in DEFAULT_Q is necessary, even though we have the same value (as an optional parameter), because this
// defines a parameterless constructor, allowing us to be new()'d in generics contexts.
public KalmanFilter() : this(DEFAULT_Q) { }
public KalmanFilter(float aQ = DEFAULT_Q, float aR = DEFAULT_R) {
q = aQ;
r = aR;
}
//-----------------------------------------------------------------------------------------
// Public Methods:
//-----------------------------------------------------------------------------------------
public T Update(T measurement, float? newQ = null, float? newR = null) {
// update values if supplied.
if (newQ != null && q != newQ) {
q = (float)newQ;
}
if (newR != null && r != newR) {
r = (float)newR;
}
// update measurement.
{
k = (p + q) / (p + q + r);
p = r * (p + q) / (r + p + q);
}
// filter result back into calculation.
dynamic dynamicMeasurement = measurement;
dynamic result = x + (dynamicMeasurement - x) * k;
x = result;
return result;
}
public T Update(List<T> measurements, bool areMeasurementsNewestFirst = false, float? newQ = null, float? newR = null) {
T result = default(T);
int i = (areMeasurementsNewestFirst) ? measurements.Count - 1 : 0;
while (i < measurements.Count && i >= 0) {
// decrement or increment the counter.
if (areMeasurementsNewestFirst) {
--i;
}
else {
++i;
}
result = Update(measurements[i], newQ, newR);
}
return result;
}
public void Reset() {
p = 1;
x = default(T);
k = 0;
}
}
using System.Collections.Generic;
using UnityEngine;
/// <summary>A Kalman filter implementation for <c>Vector3</c> values.</summary>
public class KalmanFilterVector3 {
//-----------------------------------------------------------------------------------------
// Constants:
//-----------------------------------------------------------------------------------------
public const float DEFAULT_Q = 0.000001f;
public const float DEFAULT_R = 0.01f;
public const float DEFAULT_P = 1;
//-----------------------------------------------------------------------------------------
// Private Fields:
//-----------------------------------------------------------------------------------------
private float q;
private float r;
private float p = DEFAULT_P;
private Vector3 x;
private float k;
//-----------------------------------------------------------------------------------------
// Constructors:
//-----------------------------------------------------------------------------------------
// N.B. passing in DEFAULT_Q is necessary, even though we have the same value (as an optional parameter), because this
// defines a parameterless constructor, allowing us to be new()'d in generics contexts.
public KalmanFilterVector3() : this(DEFAULT_Q) { }
public KalmanFilterVector3(float aQ = DEFAULT_Q, float aR = DEFAULT_R) {
q = aQ;
r = aR;
}
//-----------------------------------------------------------------------------------------
// Public Methods:
//-----------------------------------------------------------------------------------------
public Vector3 Update(Vector3 measurement, float? newQ = null, float? newR = null) {
// update values if supplied.
if (newQ != null && q != newQ) {
q = (float)newQ;
}
if (newR != null && r != newR) {
r = (float)newR;
}
// update measurement.
{
k = (p + q) / (p + q + r);
p = r * (p + q) / (r + p + q);
}
// filter result back into calculation.
Vector3 result = x + (measurement - x) * k;
x = result;
return result;
}
public Vector3 Update(List<Vector3> measurements, bool areMeasurementsNewestFirst = false, float? newQ = null, float? newR = null) {
Vector3 result = Vector3.zero;
int i = (areMeasurementsNewestFirst) ? measurements.Count - 1 : 0;
while (i < measurements.Count && i >= 0) {
// decrement or increment the counter.
if (areMeasurementsNewestFirst) {
--i;
}
else {
++i;
}
result = Update(measurements[i], newQ, newR);
}
return result;
}
public void Reset() {
p = 1;
x = Vector3.zero;
k = 0;
}
}
using System.Collections.Generic;
using UnityEngine;
/// <summary>A Kalman filter implementation for <c>Vector4</c> values.</summary>
public class KalmanFilterVector4 {
//-----------------------------------------------------------------------------------------
// Constants:
//-----------------------------------------------------------------------------------------
public const float DEFAULT_Q = 0.000001f;
public const float DEFAULT_R = 0.01f;
public const float DEFAULT_P = 1;
//-----------------------------------------------------------------------------------------
// Private Fields:
//-----------------------------------------------------------------------------------------
private float q;
private float r;
private float p = DEFAULT_P;
private Vector4 x;
private float k;
//-----------------------------------------------------------------------------------------
// Constructors:
//-----------------------------------------------------------------------------------------
// N.B. passing in DEFAULT_Q is necessary, even though we have the same value (as an optional parameter), because this
// defines a parameterless constructor, allowing us to be new()'d in generics contexts.
public KalmanFilterVector4() : this(DEFAULT_Q) { }
public KalmanFilterVector4(float aQ = DEFAULT_Q, float aR = DEFAULT_R) {
q = aQ;
r = aR;
}
//-----------------------------------------------------------------------------------------
// Public Methods:
//-----------------------------------------------------------------------------------------
public Vector4 Update(Vector4 measurement, float? newQ = null, float? newR = null) {
// update values if supplied.
if (newQ != null && q != newQ) {
q = (float)newQ;
}
if (newR != null && r != newR) {
r = (float)newR;
}
// update measurement.
{
k = (p + q) / (p + q + r);
p = r * (p + q) / (r + p + q);
}
// filter result back into calculation.
Vector4 result = x + (measurement - x) * k;
x = result;
return result;
}
public Vector4 Update(List<Vector4> measurements, bool areMeasurementsNewestFirst = false, float? newQ = null, float? newR = null) {
Vector4 result = Vector4.zero;
int i = (areMeasurementsNewestFirst) ? measurements.Count - 1 : 0;
while (i < measurements.Count && i >= 0) {
// decrement or increment the counter.
if (areMeasurementsNewestFirst) {
--i;
}
else {
++i;
}
result = Update(measurements[i], newQ, newR);
}
return result;
}
public void Reset() {
p = 1;
x = Vector4.zero;
k = 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment