madgwickahrs.cpp
Go to the documentation of this file.
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 
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 
36 typedef MadgwickAHRS EntityClassName;
37 
38 /* --- DG FACTORY ---------------------------------------------------- */
39 DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(MadgwickAHRS, "MadgwickAHRS");
40 
41 /* ------------------------------------------------------------------- */
42 /* --- CONSTRUCTION -------------------------------------------------- */
43 /* ------------------------------------------------------------------- */
44 MadgwickAHRS::MadgwickAHRS(const std::string &name)
45  : Entity(name),
46  CONSTRUCT_SIGNAL_IN(accelerometer, 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  m_sampleFreq(512.0) {
58 
59  /* Commands. */
60  addCommand("init",
62  docCommandVoid1("Initialize the entity.",
63  "Timestep in seconds (double)")));
64  addCommand("getBeta",
65  makeDirectGetter(*this, &m_beta,
66  docDirectGetter("Beta parameter", "double")));
67  addCommand("setBeta",
69  *this, &MadgwickAHRS::set_beta,
70  docCommandVoid1("Set the filter parameter beta", "double")));
71  addCommand("set_imu_quat",
74  docCommandVoid1("Set the quaternion as [w,x,y,z]", "vector")));
75 }
76 
77 void MadgwickAHRS::init(const double &dt) {
78  if (dt <= 0.0) return SEND_MSG("Timestep must be positive", MSG_TYPE_ERROR);
79  m_sampleFreq = 1.0 / dt;
80  m_initSucceeded = true;
81 }
82 
83 void MadgwickAHRS::set_beta(const double &beta) {
84  if (beta < 0.0 || beta > 1.0)
85  return SEND_MSG("Beta must be in [0,1]", MSG_TYPE_ERROR);
86  m_beta = beta;
87 }
88 
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 
102  if (!m_initSucceeded) {
104  "Cannot compute signal imu_quat before initialization!");
105  return s;
106  }
107  const dynamicgraph::Vector &accelerometer = m_accelerometerSIN(iter);
108  const dynamicgraph::Vector &gyroscope = m_gyroscopeSIN(iter);
109 
111  {
112  // Update state with new measurment
113  madgwickAHRSupdateIMU(gyroscope(0), gyroscope(1), gyroscope(2),
114  accelerometer(0), accelerometer(1), accelerometer(2));
115  if (s.size() != 4) s.resize(4);
116  s(0) = m_q0;
117  s(1) = m_q1;
118  s(2) = m_q2;
119  s(3) = m_q3;
120  }
122 
123  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 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  return (1.0 / sqrt(x)); // we're not in the 70's
144 }
145 
146 // IMU algorithm update
147 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  qDot1 = 0.5 * (-m_q1 * gx - m_q2 * gy - m_q3 * gz);
157  qDot2 = 0.5 * (m_q0 * gx + m_q2 * gz - m_q3 * gy);
158  qDot3 = 0.5 * (m_q0 * gy - m_q1 * gz + m_q3 * gx);
159  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  if (!((ax == 0.0) && (ay == 0.0) && (az == 0.0))) {
164  // Normalise accelerometer measurement
165  recipNorm = invSqrt(ax * ax + ay * ay + az * az);
166  ax *= recipNorm;
167  ay *= recipNorm;
168  az *= recipNorm;
169 
170  // Auxiliary variables to avoid repeated arithmetic
171  o2q0 = 2.0 * m_q0;
172  o2q1 = 2.0 * m_q1;
173  o2q2 = 2.0 * m_q2;
174  o2q3 = 2.0 * m_q3;
175  o4q0 = 4.0 * m_q0;
176  o4q1 = 4.0 * m_q1;
177  o4q2 = 4.0 * m_q2;
178  o8q1 = 8.0 * m_q1;
179  o8q2 = 8.0 * m_q2;
180  q0q0 = m_q0 * m_q0;
181  q1q1 = m_q1 * m_q1;
182  q2q2 = m_q2 * m_q2;
183  q3q3 = m_q3 * m_q3;
184 
185  // Gradient decent algorithm corrective step
186  s0 = o4q0 * q2q2 + o2q2 * ax + o4q0 * q1q1 - o2q1 * ay;
187  s1 = o4q1 * q3q3 - o2q3 * ax + 4.0 * q0q0 * m_q1 - o2q0 * ay - o4q1 +
188  o8q1 * q1q1 + o8q1 * q2q2 + o4q1 * az;
189  s2 = 4.0 * q0q0 * m_q2 + o2q0 * ax + o4q2 * q3q3 - o2q3 * ay - o4q2 +
190  o8q2 * q1q1 + o8q2 * q2q2 + o4q2 * az;
191  s3 = 4.0 * q1q1 * m_q3 - o2q1 * ax + 4.0 * q2q2 * m_q3 - o2q2 * ay;
192  if (!((s0 == 0.0) && (s1 == 0.0) && (s2 == 0.0) && (s3 == 0.0))) {
193  // normalise step magnitude
194  recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3);
195  s0 *= recipNorm;
196  s1 *= recipNorm;
197  s2 *= recipNorm;
198  s3 *= recipNorm;
199 
200  // Apply feedback step
201  qDot1 -= m_beta * s0;
202  qDot2 -= m_beta * s1;
203  qDot3 -= m_beta * s2;
204  qDot4 -= m_beta * s3;
205  }
206  }
207 
208  // Integrate rate of change of quaternion to yield quaternion
209  m_q0 += qDot1 * (1.0 / m_sampleFreq);
210  m_q1 += qDot2 * (1.0 / m_sampleFreq);
211  m_q2 += qDot3 * (1.0 / m_sampleFreq);
212  m_q3 += qDot4 * (1.0 / m_sampleFreq);
213 
214  // Normalise quaternion
215  recipNorm = invSqrt(m_q0 * m_q0 + m_q1 * m_q1 + m_q2 * m_q2 + m_q3 * m_q3);
216  m_q0 *= recipNorm;
217  m_q1 *= recipNorm;
218  m_q2 *= recipNorm;
219  m_q3 *= recipNorm;
220 }
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
std::string docDirectGetter(const std::string &name, const std::string &type)
void set_imu_quat(const dynamicgraph::Vector &imu_quat)
Set the quaternion as [w,x,y,z].
Eigen::VectorXd Vector
s
DirectGetter< E, T > * makeDirectGetter(E &entity, T *ptr, const std::string &docString)
bool m_initSucceeded
true if the entity has been successfully initialized
void stop(std::string perf_name)
Definition: stop-watch.cpp:123
void madgwickAHRSupdateIMU(double gx, double gy, double gz, double ax, double ay, double az)
void signalRegistration(const SignalArray< int > &signals)
#define CONSTRUCT_SIGNAL_IN(name, type)
DEFINE_SIGNAL_OUT_FUNCTION(x_filtered, dynamicgraph::Vector)
CommandVoid1< E, T > * makeCommandVoid1(E &entity, boost::function< void(const T &)> function, const std::string &docString)
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(FeaturePosture, "FeaturePosture")
std::string docCommandVoid1(const std::string &doc, const std::string &type)
double m_q0
quaternion of sensor frame
FilterDifferentiator EntityClassName
#define INPUT_SIGNALS
void set_beta(const double &beta)
#define betaDef
Definition: madgwickahrs.hh:58
#define SEND_MSG(msg, type)
double m_sampleFreq
sample frequency in Hz
#define CONSTRUCT_SIGNAL_OUT(name, type, dep)
Stopwatch & getProfiler()
Definition: stop-watch.cpp:46
#define PROFILE_MADGWICKAHRS_COMPUTATION
void init(const double &dt)
const std::string & getName() const
double m_beta
2 * proportional gain (Kp)
#define SEND_WARNING_STREAM_MSG(msg)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW MadgwickAHRS(const std::string &name)
void start(std::string perf_name)
Definition: stop-watch.cpp:105
virtual void display(std::ostream &os) const
void addCommand(const std::string &name, command::Command *command)
#define OUTPUT_SIGNALS
void report_all(int precision=2, std::ostream &output=std::cout)
Definition: stop-watch.cpp:185


sot-core
Author(s): Olivier Stasse, ostasse@laas.fr
autogenerated on Wed Jun 21 2023 02:51:26