-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace ControllerCommon | ||
{ | ||
/// <summary> | ||
/// MadgwickAHRS class. Implementation of Madgwick's IMU and AHRS algorithms. | ||
/// </summary> | ||
/// <remarks> | ||
/// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms | ||
/// </remarks> | ||
public class MadgwickAHRS | ||
{ | ||
/// <summary> | ||
/// Gets or sets the sample period. | ||
/// </summary> | ||
public float SamplePeriod { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the algorithm gain beta. | ||
/// </summary> | ||
public float Beta { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the Quaternion output. | ||
/// </summary> | ||
public float[] Quaternion { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class. | ||
/// </summary> | ||
/// <param name="samplePeriod"> | ||
/// Sample period. | ||
/// </param> | ||
public MadgwickAHRS(float samplePeriod) | ||
: this(samplePeriod, 1f) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class. | ||
/// </summary> | ||
/// <param name="samplePeriod"> | ||
/// Sample period. | ||
/// </param> | ||
/// <param name="beta"> | ||
/// Algorithm gain beta. | ||
/// </param> | ||
public MadgwickAHRS(float samplePeriod, float beta) | ||
{ | ||
SamplePeriod = samplePeriod; | ||
Beta = beta; | ||
Quaternion = new float[] { 1f, 0f, 0f, 0f }; | ||
} | ||
|
||
/// <summary> | ||
/// Algorithm AHRS update method. Requires only gyroscope and accelerometer data. | ||
/// </summary> | ||
/// <param name="gx"> | ||
/// Gyroscope x axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="gy"> | ||
/// Gyroscope y axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="gz"> | ||
/// Gyroscope z axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="ax"> | ||
/// Accelerometer x axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="ay"> | ||
/// Accelerometer y axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="az"> | ||
/// Accelerometer z axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="mx"> | ||
/// Magnetometer x axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="my"> | ||
/// Magnetometer y axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="mz"> | ||
/// Magnetometer z axis measurement in any calibrated units. | ||
/// </param> | ||
/// <remarks> | ||
/// Optimised for minimal arithmetic. | ||
/// Total ±: 160 | ||
/// Total *: 172 | ||
/// Total /: 5 | ||
/// Total sqrt: 5 | ||
/// </remarks> | ||
public void Update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) | ||
{ | ||
float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3]; // short name local variable for readability | ||
float norm; | ||
float hx, hy, _2bx, _2bz; | ||
float s1, s2, s3, s4; | ||
float qDot1, qDot2, qDot3, qDot4; | ||
|
||
// Auxiliary variables to avoid repeated arithmetic | ||
float _2q1mx; | ||
float _2q1my; | ||
float _2q1mz; | ||
float _2q2mx; | ||
float _4bx; | ||
float _4bz; | ||
float _2q1 = 2f * q1; | ||
float _2q2 = 2f * q2; | ||
float _2q3 = 2f * q3; | ||
float _2q4 = 2f * q4; | ||
float _2q1q3 = 2f * q1 * q3; | ||
float _2q3q4 = 2f * q3 * q4; | ||
float q1q1 = q1 * q1; | ||
float q1q2 = q1 * q2; | ||
float q1q3 = q1 * q3; | ||
float q1q4 = q1 * q4; | ||
float q2q2 = q2 * q2; | ||
float q2q3 = q2 * q3; | ||
float q2q4 = q2 * q4; | ||
float q3q3 = q3 * q3; | ||
float q3q4 = q3 * q4; | ||
float q4q4 = q4 * q4; | ||
|
||
// Normalise accelerometer measurement | ||
norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az); | ||
if (norm == 0f) return; // handle NaN | ||
norm = 1 / norm; // use reciprocal for division | ||
ax *= norm; | ||
ay *= norm; | ||
az *= norm; | ||
|
||
// Normalise magnetometer measurement | ||
norm = (float)Math.Sqrt(mx * mx + my * my + mz * mz); | ||
if (norm == 0f) return; // handle NaN | ||
norm = 1 / norm; // use reciprocal for division | ||
mx *= norm; | ||
my *= norm; | ||
mz *= norm; | ||
|
||
// Reference direction of Earth's magnetic field | ||
_2q1mx = 2f * q1 * mx; | ||
_2q1my = 2f * q1 * my; | ||
_2q1mz = 2f * q1 * mz; | ||
_2q2mx = 2f * q2 * mx; | ||
hx = mx * q1q1 - _2q1my * q4 + _2q1mz * q3 + mx * q2q2 + _2q2 * my * q3 + _2q2 * mz * q4 - mx * q3q3 - mx * q4q4; | ||
hy = _2q1mx * q4 + my * q1q1 - _2q1mz * q2 + _2q2mx * q3 - my * q2q2 + my * q3q3 + _2q3 * mz * q4 - my * q4q4; | ||
_2bx = (float)Math.Sqrt(hx * hx + hy * hy); | ||
_2bz = -_2q1mx * q3 + _2q1my * q2 + mz * q1q1 + _2q2mx * q4 - mz * q2q2 + _2q3 * my * q4 - mz * q3q3 + mz * q4q4; | ||
_4bx = 2f * _2bx; | ||
_4bz = 2f * _2bz; | ||
|
||
// Gradient decent algorithm corrective step | ||
s1 = -_2q3 * (2f * q2q4 - _2q1q3 - ax) + _2q2 * (2f * q1q2 + _2q3q4 - ay) - _2bz * q3 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q4 + _2bz * q2) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q3 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); | ||
s2 = _2q4 * (2f * q2q4 - _2q1q3 - ax) + _2q1 * (2f * q1q2 + _2q3q4 - ay) - 4f * q2 * (1 - 2f * q2q2 - 2f * q3q3 - az) + _2bz * q4 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q3 + _2bz * q1) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q4 - _4bz * q2) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); | ||
s3 = -_2q1 * (2f * q2q4 - _2q1q3 - ax) + _2q4 * (2f * q1q2 + _2q3q4 - ay) - 4f * q3 * (1 - 2f * q2q2 - 2f * q3q3 - az) + (-_4bx * q3 - _2bz * q1) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q2 + _2bz * q4) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q1 - _4bz * q3) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); | ||
s4 = _2q2 * (2f * q2q4 - _2q1q3 - ax) + _2q3 * (2f * q1q2 + _2q3q4 - ay) + (-_4bx * q4 + _2bz * q2) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q1 + _2bz * q3) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q2 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); | ||
norm = 1f / (float)Math.Sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4); // normalise step magnitude | ||
s1 *= norm; | ||
s2 *= norm; | ||
s3 *= norm; | ||
s4 *= norm; | ||
|
||
// Compute rate of change of quaternion | ||
qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - Beta * s1; | ||
qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - Beta * s2; | ||
qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - Beta * s3; | ||
qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - Beta * s4; | ||
|
||
// Integrate to yield quaternion | ||
q1 += qDot1 * SamplePeriod; | ||
q2 += qDot2 * SamplePeriod; | ||
q3 += qDot3 * SamplePeriod; | ||
q4 += qDot4 * SamplePeriod; | ||
norm = 1f / (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion | ||
Quaternion[0] = q1 * norm; | ||
Quaternion[1] = q2 * norm; | ||
Quaternion[2] = q3 * norm; | ||
Quaternion[3] = q4 * norm; | ||
} | ||
|
||
/// <summary> | ||
/// Algorithm IMU update method. Requires only gyroscope and accelerometer data. | ||
/// </summary> | ||
/// <param name="gx"> | ||
/// Gyroscope x axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="gy"> | ||
/// Gyroscope y axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="gz"> | ||
/// Gyroscope z axis measurement in radians/s. | ||
/// </param> | ||
/// <param name="ax"> | ||
/// Accelerometer x axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="ay"> | ||
/// Accelerometer y axis measurement in any calibrated units. | ||
/// </param> | ||
/// <param name="az"> | ||
/// Accelerometer z axis measurement in any calibrated units. | ||
/// </param> | ||
/// <remarks> | ||
/// Optimised for minimal arithmetic. | ||
/// Total ±: 45 | ||
/// Total *: 85 | ||
/// Total /: 3 | ||
/// Total sqrt: 3 | ||
/// </remarks> | ||
public void Update(float gx, float gy, float gz, float ax, float ay, float az) | ||
{ | ||
float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3]; // short name local variable for readability | ||
float norm; | ||
float s1, s2, s3, s4; | ||
float qDot1, qDot2, qDot3, qDot4; | ||
|
||
// Auxiliary variables to avoid repeated arithmetic | ||
float _2q1 = 2f * q1; | ||
float _2q2 = 2f * q2; | ||
float _2q3 = 2f * q3; | ||
float _2q4 = 2f * q4; | ||
float _4q1 = 4f * q1; | ||
float _4q2 = 4f * q2; | ||
float _4q3 = 4f * q3; | ||
float _8q2 = 8f * q2; | ||
float _8q3 = 8f * q3; | ||
float q1q1 = q1 * q1; | ||
float q2q2 = q2 * q2; | ||
float q3q3 = q3 * q3; | ||
float q4q4 = q4 * q4; | ||
|
||
// Normalise accelerometer measurement | ||
norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az); | ||
if (norm == 0f) return; // handle NaN | ||
norm = 1 / norm; // use reciprocal for division | ||
ax *= norm; | ||
ay *= norm; | ||
az *= norm; | ||
|
||
// Gradient decent algorithm corrective step | ||
s1 = _4q1 * q3q3 + _2q3 * ax + _4q1 * q2q2 - _2q2 * ay; | ||
s2 = _4q2 * q4q4 - _2q4 * ax + 4f * q1q1 * q2 - _2q1 * ay - _4q2 + _8q2 * q2q2 + _8q2 * q3q3 + _4q2 * az; | ||
s3 = 4f * q1q1 * q3 + _2q1 * ax + _4q3 * q4q4 - _2q4 * ay - _4q3 + _8q3 * q2q2 + _8q3 * q3q3 + _4q3 * az; | ||
s4 = 4f * q2q2 * q4 - _2q2 * ax + 4f * q3q3 * q4 - _2q3 * ay; | ||
norm = 1f / (float)Math.Sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4); // normalise step magnitude | ||
s1 *= norm; | ||
s2 *= norm; | ||
s3 *= norm; | ||
s4 *= norm; | ||
|
||
// Compute rate of change of quaternion | ||
qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - Beta * s1; | ||
qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - Beta * s2; | ||
qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - Beta * s3; | ||
qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - Beta * s4; | ||
|
||
// Integrate to yield quaternion | ||
q1 += qDot1 * SamplePeriod; | ||
q2 += qDot2 * SamplePeriod; | ||
q3 += qDot3 * SamplePeriod; | ||
q4 += qDot4 * SamplePeriod; | ||
norm = 1f / (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion | ||
Quaternion[0] = q1 * norm; | ||
Quaternion[1] = q2 * norm; | ||
Quaternion[2] = q3 * norm; | ||
Quaternion[3] = q4 * norm; | ||
} | ||
} | ||
} |
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
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