GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/filters/madgwickahrs.cpp Lines: 91 105 86.7 %
Date: 2023-03-13 12:09:37 Branches: 63 160 39.4 %

Line Branch Exec Source
1
//=========================================================================
2
//
3
// Implementation of Madgwick's IMU and AHRS algorithms.
4
// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
5
//
6
// Date       Author       Notes
7
// 29/09/2011 SOH Madgwick Initial release
8
// 02/10/2011 SOH Madgwick Optimised for reduced CPU load
9
// 11/05/2017   T Flayols  Make it a dynamic-graph entity
10
//
11
//=========================================================================
12
13
#include <dynamic-graph/all-commands.h>
14
#include <dynamic-graph/factory.h>
15
16
#include <sot/core/debug.hh>
17
#include <sot/core/madgwickahrs.hh>
18
#include <sot/core/stop-watch.hh>
19
20
namespace dynamicgraph {
21
namespace sot {
22
namespace dg = ::dynamicgraph;
23
using namespace dg;
24
using namespace dg::command;
25
using namespace std;
26
27
typedef Vector6d Vector6;
28
29
#define PROFILE_MADGWICKAHRS_COMPUTATION "MadgwickAHRS computation"
30
31
#define INPUT_SIGNALS m_accelerometerSIN << m_gyroscopeSIN
32
#define OUTPUT_SIGNALS m_imu_quatSOUT
33
34
/// Define EntityClassName here rather than in the header file
35
/// so that it can be used by the macros DEFINE_SIGNAL_**_FUNCTION.
36
typedef MadgwickAHRS EntityClassName;
37
38
/* --- DG FACTORY ---------------------------------------------------- */
39
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(MadgwickAHRS, "MadgwickAHRS");
40
41
/* ------------------------------------------------------------------- */
42
/* --- CONSTRUCTION -------------------------------------------------- */
43
/* ------------------------------------------------------------------- */
44
1
MadgwickAHRS::MadgwickAHRS(const std::string &name)
45
    : Entity(name),
46



2
      CONSTRUCT_SIGNAL_IN(accelerometer, dynamicgraph::Vector),
47



2
      CONSTRUCT_SIGNAL_IN(gyroscope, dynamicgraph::Vector),
48



2
      CONSTRUCT_SIGNAL_OUT(imu_quat, dynamicgraph::Vector,
49
                           m_gyroscopeSIN << m_accelerometerSIN),
50
      m_initSucceeded(false),
51
      m_beta(betaDef),
52
      m_q0(1.0),
53
      m_q1(0.0),
54
      m_q2(0.0),
55
      m_q3(0.0),
56


6
      m_sampleFreq(512.0) {
57

1
  Entity::signalRegistration(INPUT_SIGNALS << OUTPUT_SIGNALS);
58
59
  /* Commands. */
60

1
  addCommand("init",
61
1
             makeCommandVoid1(*this, &MadgwickAHRS::init,
62

2
                              docCommandVoid1("Initialize the entity.",
63
                                              "Timestep in seconds (double)")));
64

1
  addCommand("getBeta",
65
1
             makeDirectGetter(*this, &m_beta,
66

2
                              docDirectGetter("Beta parameter", "double")));
67

1
  addCommand("setBeta",
68
1
             makeCommandVoid1(
69
                 *this, &MadgwickAHRS::set_beta,
70

2
                 docCommandVoid1("Set the filter parameter beta", "double")));
71

1
  addCommand("set_imu_quat",
72
1
             makeCommandVoid1(
73
                 *this, &MadgwickAHRS::set_imu_quat,
74

2
                 docCommandVoid1("Set the quaternion as [w,x,y,z]", "vector")));
75
1
}
76
77
1
void MadgwickAHRS::init(const double &dt) {
78


1
  if (dt <= 0.0) return SEND_MSG("Timestep must be positive", MSG_TYPE_ERROR);
79
1
  m_sampleFreq = 1.0 / dt;
80
1
  m_initSucceeded = true;
81
}
82
83
1
void MadgwickAHRS::set_beta(const double &beta) {
84

1
  if (beta < 0.0 || beta > 1.0)
85
    return SEND_MSG("Beta must be in [0,1]", MSG_TYPE_ERROR);
86
1
  m_beta = beta;
87
}
88
89
void MadgwickAHRS::set_imu_quat(const dynamicgraph::Vector &imu_quat) {
90
  assert(imu_quat.size() == 4);
91
  m_q0 = imu_quat[0];
92
  m_q1 = imu_quat[1];
93
  m_q2 = imu_quat[2];
94
  m_q3 = imu_quat[3];
95
}
96
97
/* ------------------------------------------------------------------- */
98
/* --- SIGNALS ------------------------------------------------------- */
99
/* ------------------------------------------------------------------- */
100
101
1
DEFINE_SIGNAL_OUT_FUNCTION(imu_quat, dynamicgraph::Vector) {
102
1
  if (!m_initSucceeded) {
103
    SEND_WARNING_STREAM_MSG(
104
        "Cannot compute signal imu_quat before initialization!");
105
    return s;
106
  }
107
1
  const dynamicgraph::Vector &accelerometer = m_accelerometerSIN(iter);
108
1
  const dynamicgraph::Vector &gyroscope = m_gyroscopeSIN(iter);
109
110

1
  getProfiler().start(PROFILE_MADGWICKAHRS_COMPUTATION);
111
  {
112
    // Update state with new measurment
113
1
    madgwickAHRSupdateIMU(gyroscope(0), gyroscope(1), gyroscope(2),
114
1
                          accelerometer(0), accelerometer(1), accelerometer(2));
115
1
    if (s.size() != 4) s.resize(4);
116
1
    s(0) = m_q0;
117
1
    s(1) = m_q1;
118
1
    s(2) = m_q2;
119
1
    s(3) = m_q3;
120
  }
121

1
  getProfiler().stop(PROFILE_MADGWICKAHRS_COMPUTATION);
122
123
1
  return s;
124
}
125
126
/* --- COMMANDS ------------------------------------------------------ */
127
128
/* ------------------------------------------------------------------- */
129
// ************************ PROTECTED MEMBER METHODS ********************
130
/* ------------------------------------------------------------------- */
131
132
// Fast inverse square-root
133
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root
134
3
double MadgwickAHRS::invSqrt(double x) {
135
  /*
136
    float halfx = 0.5f * x;
137
    float y = x;
138
    long i = *(long*)&y;
139
    i = 0x5f3759df - (i>>1);
140
    y = *(float*)&i;
141
    y = y * (1.5f - (halfx * y * y));
142
    return y;*/
143
3
  return (1.0 / sqrt(x));  // we're not in the 70's
144
}
145
146
// IMU algorithm update
147
1
void MadgwickAHRS::madgwickAHRSupdateIMU(double gx, double gy, double gz,
148
                                         double ax, double ay, double az) {
149
  double recipNorm;
150
  double s0, s1, s2, s3;
151
  double qDot1, qDot2, qDot3, qDot4;
152
  double o2q0, o2q1, o2q2, o2q3, o4q0, o4q1, o4q2, o8q1, o8q2;
153
  double q0q0, q1q1, q2q2, q3q3;
154
155
  // Rate of change of quaternion from gyroscope
156
1
  qDot1 = 0.5 * (-m_q1 * gx - m_q2 * gy - m_q3 * gz);
157
1
  qDot2 = 0.5 * (m_q0 * gx + m_q2 * gz - m_q3 * gy);
158
1
  qDot3 = 0.5 * (m_q0 * gy - m_q1 * gz + m_q3 * gx);
159
1
  qDot4 = 0.5 * (m_q0 * gz + m_q1 * gy - m_q2 * gx);
160
161
  // Compute feedback only if accelerometer measurement valid
162
  // (avoids NaN in accelerometer normalisation)
163

1
  if (!((ax == 0.0) && (ay == 0.0) && (az == 0.0))) {
164
    // Normalise accelerometer measurement
165
1
    recipNorm = invSqrt(ax * ax + ay * ay + az * az);
166
1
    ax *= recipNorm;
167
1
    ay *= recipNorm;
168
1
    az *= recipNorm;
169
170
    // Auxiliary variables to avoid repeated arithmetic
171
1
    o2q0 = 2.0 * m_q0;
172
1
    o2q1 = 2.0 * m_q1;
173
1
    o2q2 = 2.0 * m_q2;
174
1
    o2q3 = 2.0 * m_q3;
175
1
    o4q0 = 4.0 * m_q0;
176
1
    o4q1 = 4.0 * m_q1;
177
1
    o4q2 = 4.0 * m_q2;
178
1
    o8q1 = 8.0 * m_q1;
179
1
    o8q2 = 8.0 * m_q2;
180
1
    q0q0 = m_q0 * m_q0;
181
1
    q1q1 = m_q1 * m_q1;
182
1
    q2q2 = m_q2 * m_q2;
183
1
    q3q3 = m_q3 * m_q3;
184
185
    // Gradient decent algorithm corrective step
186
1
    s0 = o4q0 * q2q2 + o2q2 * ax + o4q0 * q1q1 - o2q1 * ay;
187
1
    s1 = o4q1 * q3q3 - o2q3 * ax + 4.0 * q0q0 * m_q1 - o2q0 * ay - o4q1 +
188
1
         o8q1 * q1q1 + o8q1 * q2q2 + o4q1 * az;
189
1
    s2 = 4.0 * q0q0 * m_q2 + o2q0 * ax + o4q2 * q3q3 - o2q3 * ay - o4q2 +
190
1
         o8q2 * q1q1 + o8q2 * q2q2 + o4q2 * az;
191
1
    s3 = 4.0 * q1q1 * m_q3 - o2q1 * ax + 4.0 * q2q2 * m_q3 - o2q2 * ay;
192


1
    if (!((s0 == 0.0) && (s1 == 0.0) && (s2 == 0.0) && (s3 == 0.0))) {
193
      // normalise step magnitude
194
1
      recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
195
1
      s0 *= recipNorm;
196
1
      s1 *= recipNorm;
197
1
      s2 *= recipNorm;
198
1
      s3 *= recipNorm;
199
200
      // Apply feedback step
201
1
      qDot1 -= m_beta * s0;
202
1
      qDot2 -= m_beta * s1;
203
1
      qDot3 -= m_beta * s2;
204
1
      qDot4 -= m_beta * s3;
205
    }
206
  }
207
208
  // Integrate rate of change of quaternion to yield quaternion
209
1
  m_q0 += qDot1 * (1.0 / m_sampleFreq);
210
1
  m_q1 += qDot2 * (1.0 / m_sampleFreq);
211
1
  m_q2 += qDot3 * (1.0 / m_sampleFreq);
212
1
  m_q3 += qDot4 * (1.0 / m_sampleFreq);
213
214
  // Normalise quaternion
215
1
  recipNorm = invSqrt(m_q0 * m_q0 + m_q1 * m_q1 + m_q2 * m_q2 + m_q3 * m_q3);
216
1
  m_q0 *= recipNorm;
217
1
  m_q1 *= recipNorm;
218
1
  m_q2 *= recipNorm;
219
1
  m_q3 *= recipNorm;
220
1
}
221
222
/* ------------------------------------------------------------------- */
223
/* --- ENTITY -------------------------------------------------------- */
224
/* ------------------------------------------------------------------- */
225
226
void MadgwickAHRS::display(std::ostream &os) const {
227
  os << "MadgwickAHRS " << getName();
228
  try {
229
    getProfiler().report_all(3, os);
230
  } catch (ExceptionSignal e) {
231
  }
232
}
233
}  // namespace sot
234
}  // namespace dynamicgraph