MotionSensorFilter.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. using System.Numerics;
  2. namespace Ryujinx.Motion
  3. {
  4. // MahonyAHRS class. Madgwick's implementation of Mayhony's AHRS algorithm.
  5. // See: https://x-io.co.uk/open-source-imu-and-ahrs-algorithms/
  6. // Based on: https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
  7. public class MotionSensorFilter
  8. {
  9. /// <summary>
  10. /// Sample rate coefficient.
  11. /// </summary>
  12. public const float SampleRateCoefficient = 0.45f;
  13. /// <summary>
  14. /// Gets or sets the sample period.
  15. /// </summary>
  16. public float SamplePeriod { get; set; }
  17. /// <summary>
  18. /// Gets or sets the algorithm proportional gain.
  19. /// </summary>
  20. public float Kp { get; set; }
  21. /// <summary>
  22. /// Gets or sets the algorithm integral gain.
  23. /// </summary>
  24. public float Ki { get; set; }
  25. /// <summary>
  26. /// Gets the Quaternion output.
  27. /// </summary>
  28. public Quaternion Quaternion { get; private set; }
  29. /// <summary>
  30. /// Integral error.
  31. /// </summary>
  32. private Vector3 _intergralError;
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
  35. /// </summary>
  36. /// <param name="samplePeriod">
  37. /// Sample period.
  38. /// </param>
  39. public MotionSensorFilter(float samplePeriod) : this(samplePeriod, 1f, 0f)
  40. {
  41. }
  42. /// <summary>
  43. /// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
  44. /// </summary>
  45. /// <param name="samplePeriod">
  46. /// Sample period.
  47. /// </param>
  48. /// <param name="kp">
  49. /// Algorithm proportional gain.
  50. /// </param>
  51. public MotionSensorFilter(float samplePeriod, float kp) : this(samplePeriod, kp, 0f)
  52. {
  53. }
  54. /// <summary>
  55. /// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
  56. /// </summary>
  57. /// <param name="samplePeriod">
  58. /// Sample period.
  59. /// </param>
  60. /// <param name="kp">
  61. /// Algorithm proportional gain.
  62. /// </param>
  63. /// <param name="ki">
  64. /// Algorithm integral gain.
  65. /// </param>
  66. public MotionSensorFilter(float samplePeriod, float kp, float ki)
  67. {
  68. SamplePeriod = samplePeriod;
  69. Kp = kp;
  70. Ki = ki;
  71. Reset();
  72. _intergralError = new Vector3();
  73. }
  74. /// <summary>
  75. /// Algorithm IMU update method. Requires only gyroscope and accelerometer data.
  76. /// </summary>
  77. /// <param name="accel">
  78. /// Accelerometer measurement in any calibrated units.
  79. /// </param>
  80. /// <param name="gyro">
  81. /// Gyroscope measurement in radians.
  82. /// </param>
  83. public void Update(Vector3 accel, Vector3 gyro)
  84. {
  85. // Normalise accelerometer measurement.
  86. float norm = 1f / accel.Length();
  87. if (!float.IsFinite(norm))
  88. {
  89. return;
  90. }
  91. accel *= norm;
  92. float q2 = Quaternion.X;
  93. float q3 = Quaternion.Y;
  94. float q4 = Quaternion.Z;
  95. float q1 = Quaternion.W;
  96. // Estimated direction of gravity.
  97. Vector3 gravity = new Vector3()
  98. {
  99. X = 2f * (q2 * q4 - q1 * q3),
  100. Y = 2f * (q1 * q2 + q3 * q4),
  101. Z = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4
  102. };
  103. // Error is cross product between estimated direction and measured direction of gravity.
  104. Vector3 error = new Vector3()
  105. {
  106. X = accel.Y * gravity.Z - accel.Z * gravity.Y,
  107. Y = accel.Z * gravity.X - accel.X * gravity.Z,
  108. Z = accel.X * gravity.Y - accel.Y * gravity.X
  109. };
  110. if (Ki > 0f)
  111. {
  112. _intergralError += error; // Accumulate integral error.
  113. }
  114. else
  115. {
  116. _intergralError = Vector3.Zero; // Prevent integral wind up.
  117. }
  118. // Apply feedback terms.
  119. gyro += (Kp * error) + (Ki * _intergralError);
  120. // Integrate rate of change of quaternion.
  121. Vector3 delta = new Vector3(q2, q3, q4);
  122. q1 += (-q2 * gyro.X - q3 * gyro.Y - q4 * gyro.Z) * (SampleRateCoefficient * SamplePeriod);
  123. q2 += (q1 * gyro.X + delta.Y * gyro.Z - delta.Z * gyro.Y) * (SampleRateCoefficient * SamplePeriod);
  124. q3 += (q1 * gyro.Y - delta.X * gyro.Z + delta.Z * gyro.X) * (SampleRateCoefficient * SamplePeriod);
  125. q4 += (q1 * gyro.Z + delta.X * gyro.Y - delta.Y * gyro.X) * (SampleRateCoefficient * SamplePeriod);
  126. // Normalise quaternion.
  127. Quaternion quaternion = new Quaternion(q2, q3, q4, q1);
  128. norm = 1f / quaternion.Length();
  129. if (!float.IsFinite(norm))
  130. {
  131. return;
  132. }
  133. Quaternion = quaternion * norm;
  134. }
  135. public void Reset()
  136. {
  137. Quaternion = Quaternion.Identity;
  138. }
  139. }
  140. }