/******************************************************************************
IMU Fusion Library
Extended Kalman Filter
- Copyright (C) 2019 Walter Fetter Lages <w.fetter@ieee.org>
+ Copyright (C) 2019,2020 Walter Fetter Lages <w.fetter@ieee.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
class Ekf
{
private:
- const double Gz_; ///< Gravity acceleration
const double T_; ///< Sampling period
+ int initSamples_; ///< Number of samples to use for initialization
+ int nSamples_; ///< Number of initialization samples executed
Eigen::VectorXd x_; ///< Filter state
Eigen::MatrixXd P_; ///< Covariance a priori
Eigen::MatrixXd Pw_; ///< Covariance of process noise
Eigen::MatrixXd Pv_; ///< Covariance of observation noise
+ Eigen::Vector3d accel_; ///< Acceleration average while initializing
+ Eigen::Vector3d gyro_; ///< Gyroscope average while initializeing
+ Eigen::Vector3d mag_; ///< Magnetic field average while initializing
+ Eigen::Vector3d accel0_; ///< Accleration at rest in ENU frame
+ Eigen::Vector3d mag0_; ///< Magnetic field in ENU frame
/** @brief Quaternion transition function
\f${}^0\dot{Q}_g(t)=\frac{1}{2} \Xi\left({}^0Q_g(t)\right) {}^g\omega\f$
public:
+
/** @brief Constructs an extended Kalman filter.
- @param x0 Inital orientation.
- @param P0 Initial a priori covariance of estimation error
@param Pw Covariance of process noise
@param Pv Covariance of observation noise
@param T Sampling period
+ @param initSamples Number of samples to use to compute initial estimate and covariance
*/
- Ekf(const Eigen::Quaterniond &x0,const Eigen::MatrixXd &P0,const Eigen::MatrixXd &Pw,const Eigen::MatrixXd &Pv,double T);
+ Ekf(const Eigen::MatrixXd &Pw,const Eigen::MatrixXd &Pv,double T,int initSamples=50);
/** @brief Destructor of an extended Kalman filter.
*/
/******************************************************************************
IMU Fusion Library
Extended Kalman Filter
- Copyright (C) 2019 Walter Fetter Lages <w.fetter@ieee.org>
+ Copyright (C) 2019, 2020 Walter Fetter Lages <w.fetter@ieee.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
namespace imufusion
{
-Ekf::Ekf(const Eigen::Quaterniond &q0,const Eigen::MatrixXd &P0,const Eigen::MatrixXd &Pw,const Eigen::MatrixXd &Pv,double T): Gz_(-9.81), T_(T), x_(7), P_(P0), Pw_(Pw),Pv_(Pv)
+Ekf::Ekf(const Eigen::MatrixXd &Pw,const Eigen::MatrixXd &Pv,double T,int initSamples): T_(T), initSamples_(initSamples), nSamples_(1), x_(7), P_(7,7), Pw_(Pw),Pv_(Pv)
{
- Eigen::Vector3d gyroBias;
- gyroBias.setZero();
- x_ << q0.w(),
- q0.x(),
- q0.y(),
- q0.z(),
- gyroBias;
+ P_.setIdentity();
+ P_*=1e6;
+
+ x_.setZero();
+ x_[0]=1.0;
+
+ mag0_ << 0,
+ 1,
+ 0;
+
+ accel_.setZero();
+ gyro_.setZero();
+ mag_.setZero();
}
Ekf::~Ekf(void)
void Ekf::update(const Eigen::Vector3d &acc,const Eigen::Vector3d &mag,const Eigen::Vector3d &gyro)
{
+ if(nSamples_ <= initSamples_)
+ {
+ Eigen::Vector3d z=acc;
+ z.normalize();
+ Eigen::Vector3d x=mag.cross(z);
+ x.normalize();
+ Eigen::Vector3d y=z.cross(x);
+ y.normalize();
+
+ Eigen::Matrix3d R;
+ R << x.transpose(),
+ y.transpose(),
+ z.transpose();
+
+ Eigen::Quaterniond q=Eigen::Quaterniond(R);
+
+ Eigen::VectorXd xi(7);
+
+ xi << q.w(),
+ q.x(),
+ q.y(),
+ q.z(),
+ gyro;
+
+ if(nSamples_ >=2) P_=(nSamples_-2.0)/(nSamples_-1.0)*P_+1.0/nSamples_*(xi-x_)*(xi-x_).transpose();
+
+ x_=(nSamples_-1.0)/nSamples_*x_+1.0/nSamples_*xi;
+
+ accel_=(nSamples_-1.0)/nSamples_*accel_+1.0/nSamples_*acc;
+ gyro_=(nSamples_-1.0)/nSamples_*gyro_+1.0/nSamples_*gyro;
+ mag_=(nSamples_-1.0)/nSamples_*mag_+1.0/nSamples_*mag;
+
+ accel0_=R*accel_;
+ mag0_=R*mag_;
+
+ if(nSamples_==initSamples_)
+ {
+ z=accel_;
+ z.normalize();
+ x=mag_.cross(z);
+ x.normalize();
+ y=z.cross(x);
+ y.normalize();
+
+ R << x.transpose(),
+ y.transpose(),
+ z.transpose();
+
+ q=Eigen::Quaterniond(R);
+
+ x_ << q.w(),
+ q.x(),
+ q.y(),
+ q.z(),
+ gyro_;
+
+ mag0_=R*mag_;
+ accel0_=R*accel_;
+ }
+
+ nSamples_++;
+ return;
+ }
+
// Observation
Eigen::VectorXd y(acc.rows()+mag.rows());
y << acc,
// State estimation
Eigen::VectorXd x=x_+K*(y-h());
+ x.head<4>().normalize();
// Covariance a posteriori
Eigen::MatrixXd P=(Eigen::MatrixXd::Identity(7,7)-K*H())*P_;
// State prediction
x_=f(u); // Should be the last operation because it changes x_
+ x_.head<4>().normalize();
}
Eigen::Quaterniond Ekf::quaternion(void)
{
- Eigen::Quaterniond q(x_(0),x_(1),x_(2),x_(3));
+ Eigen::Quaterniond q(x_[0],x_[1],x_[2],x_[3]); // Do not use x_.head<>(). Internal order of elements is different.
q.normalize();
return q;
}
Eigen::VectorXd Ekf::h(void)
{
+ Eigen::Matrix3d R=Eigen::Quaterniond(x_[0],x_[1],x_[2],x_[3]).matrix(); // Do not use x_.head<>(). Internal order of elements is different.
+ Eigen::Vector3d ya=R.transpose()*accel0_;
+ Eigen::Vector3d ym=R.transpose()*mag0_;
+
Eigen::VectorXd h(6);
- h << -2*Gz_*(x_(1)*x_(3)-x_(2)*x_(0)),
- -2*Gz_*(x_(2)*x_(3)+x_(1)*x_(0)),
- -Gz_*(x_(0)*x_(0)-x_(1)*x_(1)-x_(2)*x_(2)+x_(3)*x_(3)),
- 2*(x_(1)*x_(2)+x_(3)*x_(0)),
- x_(0)*x_(0)-x_(1)*x_(1)+x_(2)*x_(2)-x_(3)*x_(3),
- 2*(x_(2)*x_(3)-x_(1)*x_(0));
+ h << ya,
+ ym;
+
return h;
}
Eigen::MatrixXd Ekf::H(void)
{
Eigen::MatrixXd H(6,7);
- H << 2*Gz_*x_(2), -2*Gz_*x_(3), 2*Gz_*x_(0), -2*Gz_*x_(1), Eigen::MatrixXd::Zero(1,3),
- -2*Gz_*x_(1), -2*Gz_*x_(0), -2*Gz_*x_(3), -2*Gz_*x_(2), Eigen::MatrixXd::Zero(1,3),
- -2*Gz_*x_(0), 2*Gz_*x_(1), 2*Gz_*x_(2), -2*Gz_*x_(3), Eigen::MatrixXd::Zero(1,3),
- 2*x_(3), 2*x_(2), 2*x_(1), 2*x_(0), Eigen::MatrixXd::Zero(1,3),
- 2*x_(0), -2*x_(1), 2*x_(2), -2*x_(3), Eigen::MatrixXd::Zero(1,3),
- -2*x_(1), -2*x_(0), 2*x_(3), 2*x_(2), Eigen::MatrixXd::Zero(1,3);
+
+ H << 2*x_(0)*accel0_(0)+2*x_(3)*accel0_(1)-2*x_(2)*accel0_(2),
+ 2*x_(1)*accel0_(0)+2*x_(2)*accel0_(1)+2*x_(3)*accel0_(2),
+ -2*x_(2)*accel0_(0)+2*x_(1)*accel0_(1)-2*x_(0)*accel0_(2),
+ -2*x_(3)*accel0_(0)+2*x_(0)*accel0_(1)+2*x_(1)*accel0_(2),
+ Eigen::MatrixXd::Zero(1,3),
+
+ -2*x_(3)*accel0_(0)+2*x_(0)*accel0_(1)+2*x_(1)*accel0_(2),
+ 2*x_(2)*accel0_(0)-2*x_(1)*accel0_(1)+2*x_(0)*accel0_(2),
+ 2*x_(1)*accel0_(0)+2*x_(2)*accel0_(1)+2*x_(3)*accel0_(2),
+ -2*x_(0)*accel0_(0)-2*x_(3)*accel0_(1)+2*x_(2)*accel0_(2),
+ Eigen::MatrixXd::Zero(1,3),
+
+ 2*x_(2)*accel0_(0)-2*x_(1)*accel0_(1)+2*x_(0)*accel0_(2),
+ 2*x_(3)*accel0_(0)-2*x_(0)*accel0_(1)-2*x_(1)*accel0_(2),
+ 2*x_(0)*accel0_(0)+2*x_(3)*accel0_(1)-2*x_(2)*accel0_(2),
+ 2*x_(1)*accel0_(0)+2*x_(2)*accel0_(1)+2*x_(3)*accel0_(2),
+ Eigen::MatrixXd::Zero(1,3),
+
+
+ 2*x_(0)*mag0_(0)+2*x_(3)*mag0_(1)-2*x_(2)*mag0_(2),
+ 2*x_(1)*mag0_(0)+2*x_(2)*mag0_(1)+2*x_(3)*mag0_(2),
+ -2*x_(2)*mag0_(0)+2*x_(1)*mag0_(1)-2*x_(0)*mag0_(2),
+ -2*x_(3)*mag0_(0)+2*x_(0)*mag0_(1)+2*x_(1)*mag0_(2),
+ Eigen::MatrixXd::Zero(1,3),
+
+ -2*x_(3)*mag0_(0)+2*x_(0)*mag0_(1)+2*x_(1)*mag0_(2),
+ 2*x_(2)*mag0_(0)-2*x_(1)*mag0_(1)+2*x_(0)*mag0_(2),
+ 2*x_(1)*mag0_(0)+2*x_(2)*mag0_(1)+2*x_(3)*mag0_(2),
+ -2*x_(0)*mag0_(0)-2*x_(3)*mag0_(1)+2*x_(2)*mag0_(2),
+ Eigen::MatrixXd::Zero(1,3),
+
+ 2*x_(2)*mag0_(0)-2*x_(1)*mag0_(1)+2*x_(0)*mag0_(2),
+ 2*x_(3)*mag0_(0)-2*x_(0)*mag0_(1)-2*x_(1)*mag0_(2),
+ 2*x_(0)*mag0_(0)+2*x_(3)*mag0_(1)-2*x_(2)*mag0_(2),
+ 2*x_(1)*mag0_(0)+2*x_(2)*mag0_(1)+2*x_(3)*mag0_(2),
+ Eigen::MatrixXd::Zero(1,3);
+
return H;
}