-
-
Save TigerHix/d5de6fef0cf889fbbd4b7fdedc93d20c to your computer and use it in GitHub Desktop.
Simple Kalman filtering in Unity.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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