MotionSensorFilter.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System.Numerics;
  2. namespace Ryujinx.Input.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. 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. /// <summary>
  41. /// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
  42. /// </summary>
  43. /// <param name="samplePeriod">
  44. /// Sample period.
  45. /// </param>
  46. /// <param name="kp">
  47. /// Algorithm proportional gain.
  48. /// </param>
  49. public MotionSensorFilter(float samplePeriod, float kp) : this(samplePeriod, kp, 0f) { }
  50. /// <summary>
  51. /// Initializes a new instance of the <see cref="MotionSensorFilter"/> class.
  52. /// </summary>
  53. /// <param name="samplePeriod">
  54. /// Sample period.
  55. /// </param>
  56. /// <param name="kp">
  57. /// Algorithm proportional gain.
  58. /// </param>
  59. /// <param name="ki">
  60. /// Algorithm integral gain.
  61. /// </param>
  62. public MotionSensorFilter(float samplePeriod, float kp, float ki)
  63. {
  64. SamplePeriod = samplePeriod;
  65. Kp = kp;
  66. Ki = ki;
  67. Reset();
  68. _intergralError = new Vector3();
  69. }
  70. /// <summary>
  71. /// Algorithm IMU update method. Requires only gyroscope and accelerometer data.
  72. /// </summary>
  73. /// <param name="accel">
  74. /// Accelerometer measurement in any calibrated units.
  75. /// </param>
  76. /// <param name="gyro">
  77. /// Gyroscope measurement in radians.
  78. /// </param>
  79. public void Update(Vector3 accel, Vector3 gyro)
  80. {
  81. // Normalise accelerometer measurement.
  82. float norm = 1f / accel.Length();
  83. if (!float.IsFinite(norm))
  84. {
  85. return;
  86. }
  87. accel *= norm;
  88. float q2 = Quaternion.X;
  89. float q3 = Quaternion.Y;
  90. float q4 = Quaternion.Z;
  91. float q1 = Quaternion.W;
  92. // Estimated direction of gravity.
  93. Vector3 gravity = new Vector3()
  94. {
  95. X = 2f * (q2 * q4 - q1 * q3),
  96. Y = 2f * (q1 * q2 + q3 * q4),
  97. Z = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4
  98. };
  99. // Error is cross product between estimated direction and measured direction of gravity.
  100. Vector3 error = new Vector3()
  101. {
  102. X = accel.Y * gravity.Z - accel.Z * gravity.Y,
  103. Y = accel.Z * gravity.X - accel.X * gravity.Z,
  104. Z = accel.X * gravity.Y - accel.Y * gravity.X
  105. };
  106. if (Ki > 0f)
  107. {
  108. _intergralError += error; // Accumulate integral error.
  109. }
  110. else
  111. {
  112. _intergralError = Vector3.Zero; // Prevent integral wind up.
  113. }
  114. // Apply feedback terms.
  115. gyro += (Kp * error) + (Ki * _intergralError);
  116. // Integrate rate of change of quaternion.
  117. Vector3 delta = new Vector3(q2, q3, q4);
  118. q1 += (-q2 * gyro.X - q3 * gyro.Y - q4 * gyro.Z) * (SampleRateCoefficient * SamplePeriod);
  119. q2 += (q1 * gyro.X + delta.Y * gyro.Z - delta.Z * gyro.Y) * (SampleRateCoefficient * SamplePeriod);
  120. q3 += (q1 * gyro.Y - delta.X * gyro.Z + delta.Z * gyro.X) * (SampleRateCoefficient * SamplePeriod);
  121. q4 += (q1 * gyro.Z + delta.X * gyro.Y - delta.Y * gyro.X) * (SampleRateCoefficient * SamplePeriod);
  122. // Normalise quaternion.
  123. Quaternion quaternion = new Quaternion(q2, q3, q4, q1);
  124. norm = 1f / quaternion.Length();
  125. if (!float.IsFinite(norm))
  126. {
  127. return;
  128. }
  129. Quaternion = quaternion * norm;
  130. }
  131. public void Reset()
  132. {
  133. Quaternion = Quaternion.Identity;
  134. }
  135. }
  136. }