From cf855882c50ed84df149cdb7b18a256221632194 Mon Sep 17 00:00:00 2001
From: Fanny Risbourg <frisbourg@laas.fr>
Date: Fri, 12 Aug 2022 14:10:56 +0200
Subject: [PATCH] add Estimator

---
 CMakeLists.txt                                |   6 +
 config/walk_parameters.yaml                   |   3 +-
 include/bindings/python.hpp                   |   2 +
 include/qrw/ComplementaryFilter.hpp           |  80 +++++
 include/qrw/Estimator.hpp                     | 266 ++++++++++++++++
 include/qrw/Filter.hpp                        |  77 +++++
 python/CMakeLists.txt                         |   2 +
 python/Estimator.cpp                          |  51 ++++
 python/Filter.cpp                             |  16 +
 python/main.cpp                               |   2 +
 .../quadruped_reactive_walking/Controller.py  |  52 +++-
 .../WB_MPC/ProblemData.py                     |  20 +-
 .../WB_MPC/Target.py                          |   5 +-
 .../quadruped_reactive_walking/tools/Utils.py |  33 ++
 src/ComplementaryFilter.cpp                   |  37 +++
 src/Estimator.cpp                             | 283 ++++++++++++++++++
 src/Filter.cpp                                |  73 +++++
 17 files changed, 991 insertions(+), 17 deletions(-)
 create mode 100644 include/qrw/ComplementaryFilter.hpp
 create mode 100644 include/qrw/Estimator.hpp
 create mode 100644 include/qrw/Filter.hpp
 create mode 100644 python/Estimator.cpp
 create mode 100644 python/Filter.cpp
 create mode 100644 src/ComplementaryFilter.cpp
 create mode 100644 src/Estimator.cpp
 create mode 100644 src/Filter.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 68c04dde..4800cc0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,11 +51,17 @@ set(${PROJECT_NAME}_HEADERS
   include/qrw/Types.h
   include/qrw/Params.hpp
   include/qrw/Joystick.hpp
+  include/qrw/Estimator.hpp
+  include/qrw/Filter.hpp
+  include/qrw/ComplementaryFilter.hpp
   )
 
 set(${PROJECT_NAME}_SOURCES
   src/Params.cpp
   src/Joystick.cpp
+  src/Estimator.cpp
+  src/Filter.cpp
+  src/ComplementaryFilter.cpp
   )
 
 add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
diff --git a/config/walk_parameters.yaml b/config/walk_parameters.yaml
index b11e42aa..afd1cb81 100644
--- a/config/walk_parameters.yaml
+++ b/config/walk_parameters.yaml
@@ -37,8 +37,7 @@ robot:
 
     # Parameters of Gait
     N_periods: 1
-    gait: [8, 1, 0, 0, 1,
-           8, 0, 1, 1, 0]  # Initial gait matrix
+    gait: [100, 0, 0, 0, 0]  # Initial gait matrix
 
     # Parameters of Joystick
     gp_alpha_vel: 0.003  # Coefficient of the low pass filter applied to gamepad velocity
diff --git a/include/bindings/python.hpp b/include/bindings/python.hpp
index 9e6d9c95..cfd713cd 100644
--- a/include/bindings/python.hpp
+++ b/include/bindings/python.hpp
@@ -13,5 +13,7 @@ namespace bp = boost::python;
 
 void exposeJoystick();
 void exposeParams();
+void exposeEstimator();
+void exposeFilter();
 
 #endif
\ No newline at end of file
diff --git a/include/qrw/ComplementaryFilter.hpp b/include/qrw/ComplementaryFilter.hpp
new file mode 100644
index 00000000..b12fb47c
--- /dev/null
+++ b/include/qrw/ComplementaryFilter.hpp
@@ -0,0 +1,80 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///
+/// \brief This is the header for Estimator and ComplementaryFilter classes
+///
+/// \details These classes estimate the state of the robot based on sensor measurements
+///
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef COMPLEMENTARY_FILTER_H_INCLUDED
+#define COMPLEMENTARY_FILTER_H_INCLUDED
+
+#include "qrw/Types.h"
+
+class ComplementaryFilter {
+ public:
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Constructor
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ComplementaryFilter();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Constructor witht initialization
+  ///
+  /// \param[in] dt Time step of the complementary filter
+  /// \param[in] HighPass Initial value for the high pass filter
+  /// \param[in] LowPass Initial value for the low pass filter
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ComplementaryFilter(double dt, Vector3 HighPass, Vector3 LowPass);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Destructor
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ~ComplementaryFilter() {}  // Empty destructor
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Initialize
+  ///
+  /// \param[in] dt Time step of the complementary filter
+  /// \param[in] HighPass Initial value for the high pass filter
+  /// \param[in] LowPass Initial value for the low pass filter
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void initialize(double dt, Vector3 HighPass, Vector3 LowPass);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Compute the filtered output of the complementary filter
+  ///
+  /// \param[in] x Quantity handled by the filter
+  /// \param[in] dx Derivative of the quantity
+  /// \param[in] alpha Filtering coefficient between x and dx quantities
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  Vector3 compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha);
+
+  Vector3 getX() { return x_; }              // Get the input quantity
+  Vector3 getDx() { return dx_; }            // Get the derivative of the input quantity
+  Vector3 getHighPass() { return HighPass_; }     // Get the high-passed internal quantity
+  Vector3 getLowPass() { return LowPass_; }      // Get the low-passed internal quantity
+  Vector3 getAlpha() { return alpha_; }      // Get the alpha coefficient of the filter
+  Vector3 getFilteredX() { return filteredX_; }  // Get the filtered output
+
+ private:
+  double dt_;          // Time step of the complementary filter
+  Vector3 HighPass_;   // Initial value for the high pass filter
+  Vector3 LowPass_;    // Initial value for the low pass filter
+  Vector3 alpha_;      // Filtering coefficient between x and dx quantities
+  Vector3 x_;          // Quantity to filter
+  Vector3 dx_;         // Quantity to filter derivative's
+  Vector3 filteredX_;  // Filtered output
+};
+
+#endif  // COMPLEMENTARY_FILTER_H_INCLUDED
diff --git a/include/qrw/Estimator.hpp b/include/qrw/Estimator.hpp
new file mode 100644
index 00000000..2e35fbaa
--- /dev/null
+++ b/include/qrw/Estimator.hpp
@@ -0,0 +1,266 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///
+/// \brief This is the header for Estimator and ComplementaryFilter classes
+///
+/// \details These classes estimate the state of the robot based on sensor measurements
+///
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef ESTIMATOR_H_INCLUDED
+#define ESTIMATOR_H_INCLUDED
+
+#include <deque>
+
+#include "pinocchio/multibody/data.hpp"
+#include "pinocchio/multibody/model.hpp"
+#include "pinocchio/spatial/se3.hpp"
+#include "qrw/ComplementaryFilter.hpp"
+#include "qrw/Params.hpp"
+
+class Estimator {
+ public:
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Constructor
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  Estimator();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Destructor.
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ~Estimator() {}  // Empty destructor
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Initialize with given data
+  ///
+  /// \param[in] params Object that stores parameters
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void initialize(Params& params);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Run one iteration of the estimator to get the position and velocity states of the robot
+  ///
+  /// \param[in] gait Gait matrix that stores current and future contact status of the feet
+  /// \param[in] goals Target positions of the four feet
+  /// \param[in] baseLinearAcceleration Linear acceleration of the IMU (gravity compensated)
+  /// \param[in] baseAngularVelocity Angular velocity of the IMU
+  /// \param[in] baseOrientation Quaternion orientation of the IMU
+  /// \param[in] q_mes Position of the 12 actuators
+  /// \param[in] v_mes Velocity of the 12 actuators
+  /// \param[in] perfectPosition Position of the robot in world frame
+  /// \param[in] b_perfectVelocity Velocity of the robot in base frame
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void run(MatrixN const& gait, MatrixN const& goals, VectorN const& baseLinearAcceleration,
+           VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q_mes,
+           VectorN const& v_mes, VectorN const& perfectPosition, Vector3 const& b_perfectVelocity);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Update state vectors of the robot (q and v)
+  ///        Update transformation matrices between world and horizontal frames
+  ///
+  /// \param[in] joystick_v_ref Reference velocity from the joystick
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void updateReferenceState(VectorN const& vRef);
+
+  VectorN getQEstimate() { return qEstimate_; }
+  VectorN getVEstimate() { return vEstimate_; }
+  VectorN getVSecurity() { return vSecurity_; }
+  VectorN getFeetStatus() { return feetStatus_; }
+  MatrixN getFeetTargets() { return feetTargets_; }
+  Vector3 getBaseVelocityFK() { return baseVelocityFK_; }
+  Vector3 getBasePositionFK() { return basePositionFK_; }
+  Vector3 getFeetPositionBarycenter() { return feetPositionBarycenter_; }
+  Vector3 getBBaseVelocity() { return b_baseVelocity_; }
+
+  Vector3 getFilterVelX() { return velocityFilter_.getX(); }
+  Vector3 getFilterVelDX() { return velocityFilter_.getDx(); }
+  Vector3 getFilterVelAlpha() { return velocityFilter_.getAlpha(); }
+  Vector3 getFilterVelFiltX() { return velocityFilter_.getFilteredX(); }
+
+  Vector3 getFilterPosX() { return positionFilter_.getX(); }
+  Vector3 getFilterPosDX() { return positionFilter_.getDx(); }
+  Vector3 getFilterPosAlpha() { return positionFilter_.getAlpha(); }
+  Vector3 getFilterPosFiltX() { return positionFilter_.getFilteredX(); }
+
+  VectorN getQReference() { return qRef_; }
+  VectorN getVReference() { return vRef_; }
+  VectorN getBaseVelRef() { return baseVelRef_; }
+  VectorN getBaseAccRef() { return baseAccRef_; }
+  VectorN getHV() { return h_v_; }
+  VectorN getVFiltered() { return vFiltered_; }
+  VectorN getHVFiltered() { return h_vFiltered_; }
+  Matrix3 getoRh() { return oRh_; }
+  Matrix3 gethRb() { return hRb_; }
+  Vector3 getoTh() { return oTh_; }
+
+ private:
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Retrieve and update IMU data
+  ///
+  /// \param[in] baseLinearAcceleration Linear acceleration of the IMU (gravity compensated)
+  /// \param[in] baseAngularVelocity Angular velocity of the IMU
+  /// \param[in] baseOrientation Euler orientation of the IMU
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void updateIMUData(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity,
+                     Vector3 const& baseOrientation, VectorN const& perfectPosition);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Retrieve and update position and velocity of the 12 actuators
+  ///
+  /// \param[in] q Position of the 12 actuators
+  /// \param[in] v Velocity of the 12 actuators
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void updateJointData(Vector12 const& q, Vector12 const& v);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Update the feet relative data
+  /// \details update feetStatus_, feetTargets_, feetStancePhaseDuration_ and phaseRemainingDuration_
+  ///
+  /// \param[in] gait Gait matrix that stores current and future contact status of the feet
+  /// \param[in] feetTargets Target positions of the four feet
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void updatFeetStatus(MatrixN const& gait, MatrixN const& feetTargets);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Estimate base position and velocity using Forward Kinematics
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void updateForwardKinematics();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Compute barycenter of feet in contact
+  ///
+  /// \param[in] feet_status Contact status of the four feet
+  /// \param[in] goals Target positions of the four feet
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void computeFeetPositionBarycenter();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Estimate the velocity of the base with forward kinematics using a contact point that
+  ///        is supposed immobile in world frame
+  ///
+  /// \param[in] contactFrameId Frame ID of the contact foot
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  Vector3 computeBaseVelocityFromFoot(int footId);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Estimate the position of the base with forward kinematics using a contact point that
+  ///        is supposed immobile in world frame
+  ///
+  /// \param[in] footId Frame ID of the contact foot
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  Vector3 computeBasePositionFromFoot(int footId);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Compute the alpha coefficient for the complementary filter
+  /// \return alpha
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  double computeAlphaVelocity();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Estimates the velocity vector
+  /// \details The complementary filter combines data from the FK and the IMU acceleration data
+  ///
+  /// \param[in] b_perfectVelocity Perfect velocity of the base in the base frame
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void estimateVelocity(Vector3 const& b_perfectVelocity);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Estimates the configuration vector
+  /// \details The complementary filter combines data from the FK and the estimated velocity
+  ///
+  /// \param[in] perfectPosition Perfect position of the base in the world frame
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void estimatePosition(Vector3 const& perfectPosition);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Filter the estimated velocity over a moving window
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void filterVelocity();
+
+  bool perfectEstimator_;  // Enable perfect estimator (directly from the PyBullet simulation)
+  bool solo3D_;            // Perfect estimator including yaw angle
+  double dt_;              // Time step of the estimator
+  bool initialized_;       // Is intiialized after the first update of the IMU
+  Vector4 feetFrames_;     // Frame indexes of the four feet
+  double footRadius_;      // radius of a foot
+  Vector3 alphaPos_;       // Alpha coeefficient for the position complementary filter
+  double alphaVelMax_;     // Maximum alpha value for the velocity complementary filter
+  double alphaVelMin_;     // Minimum alpha value for the velocity complementary filter
+  double alphaSecurity_;   // Low pass coefficient for the outputted filtered velocity for security check
+
+  pinocchio::SE3 b_M_IMU_;              // Transform between the base frame and the IMU frame
+  double IMUYawOffset_;                 // Yaw orientation of the IMU at startup
+  Vector3 IMULinearAcceleration_;       // Linear acceleration of the IMU (gravity compensated)
+  Vector3 IMUAngularVelocity_;          // Angular velocity of the IMU
+  Vector3 IMURpy_;                      // Roll Pitch Yaw orientation of the IMU
+  pinocchio::SE3::Quaternion IMUQuat_;  // Quaternion orientation of the IMU
+
+  Vector12 qActuators_;  // Measured positions of actuators
+  Vector12 vActuators_;  // Measured velocities of actuators
+
+  int phaseRemainingDuration_;       // Number of iterations left for the current gait phase
+  Vector4 feetStancePhaseDuration_;  // Number of loops during which each foot has been in contact
+  Vector4 feetStatus_;               // Contact status of the four feet
+  Matrix34 feetTargets_;             // Target positions of the four feet
+
+  pinocchio::Model velocityModel_, positionModel_;  // Pinocchio models for frame computations and forward kinematics
+  pinocchio::Data velocityData_, positionData_;     // Pinocchio datas for frame computations and forward kinematics
+  Vector19 q_FK_;                                   // Configuration vector for Forward Kinematics
+  Vector18 v_FK_;                                   // Velocity vector for Forward Kinematics
+  Vector3 baseVelocityFK_;                          // Base linear velocity estimated by Forward Kinematics
+  Vector3 basePositionFK_;                          // Base position estimated by Forward Kinematics
+  Vector3 b_baseVelocity_;                          // Filtered estimated velocity at center base (base frame)
+  Vector3 feetPositionBarycenter_;                  // Barycenter of feet in contact
+
+  ComplementaryFilter positionFilter_;  // Complementary filter for base position
+  ComplementaryFilter velocityFilter_;  // Complementary filter for base velocity
+  Vector19 qEstimate_;                  // Filtered output configuration
+  Vector18 vEstimate_;                  // Filtered output velocity
+  Vector12 vSecurity_;                  // Filtered output velocity for security check
+
+  int windowSize_;                                     // Number of samples in the averaging window
+  Vector6 vFiltered_;                                  // Base velocity (in base frame) filtered by averaging window
+  std::deque<double> vx_queue_, vy_queue_, vz_queue_;  // Queues that hold samples
+
+  Vector18 qRef_;        // Configuration vector in ideal world frame
+  Vector18 vRef_;        // Velocity vector in ideal world frame
+  Vector6 baseVelRef_;   // Reference velocity vector
+  Vector6 baseAccRef_;   // Reference acceleration vector
+  Matrix3 oRh_;          // Rotation between horizontal and world frame
+  Matrix3 hRb_;          // Rotation between base and horizontal frame
+  Vector3 oTh_;          // Translation between horizontal and world frame
+  Vector6 h_v_;          // Velocity vector in horizontal frame
+  Vector6 h_vFiltered_;  // Base velocity (in horizontal frame) filtered by averaging window
+};
+#endif  // ESTIMATOR_H_INCLUDED
diff --git a/include/qrw/Filter.hpp b/include/qrw/Filter.hpp
new file mode 100644
index 00000000..65af7687
--- /dev/null
+++ b/include/qrw/Filter.hpp
@@ -0,0 +1,77 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///
+/// \brief This is the header for Filter class
+///
+/// \details This class applies a low pass filter to estimated data to avoid keeping high frequency components into
+///          what is given to the "low frequency" model predictive control
+///
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef FILTER_H_INCLUDED
+#define FILTER_H_INCLUDED
+
+#include <deque>
+
+#include "qrw/Params.hpp"
+
+class Filter {
+ public:
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Constructor
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  Filter();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Initialize with given data
+  ///
+  /// \param[in] params Object that stores parameters
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void initialize(Params& params);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Destructor.
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ~Filter() {}  // Empty destructor
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Run one iteration of the filter and return the filtered measurement
+  ///
+  /// \param[in] x Quantity to filter
+  /// \param[in] check_modulo Check for the +-pi modulo of orientation if true
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  VectorN filter(Vector6 const& x, bool check_modulo);
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Add or remove 2 PI to all elements in the queues to handle +- pi modulo
+  ///
+  /// \param[in] a Angle that needs change (3, 4, 5 for Roll, Pitch, Yaw respectively)
+  /// \param[in] dir Direction of the change (+pi to -pi or -pi to +pi)
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  void handle_modulo(int a, bool dir);
+
+  VectorN getFilt() { return y_; }
+
+ private:
+  double b_;       // Denominator coefficients of the filter transfer function
+  Vector2 a_;      // Numerator coefficients of the filter transfer function
+  Vector6 x_;      // Latest measurement
+  VectorN y_;      // Latest result
+  Vector6 accum_;  // Used to compute the result (accumulation)
+
+  std::deque<Vector6> x_queue_;  // Store the last measurements for product with denominator coefficients
+  std::deque<Vector6> y_queue_;  // Store the last results for product with numerator coefficients
+
+  bool init_;  // Initialisation flag
+};
+
+#endif  // FILTER_H_INCLUDED
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 8739abe9..1128124a 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -2,6 +2,8 @@ set(${PY_NAME}_SOURCES
   main.cpp
   Joystick.cpp
   Params.cpp
+  Estimator.cpp
+  Filter.cpp
 )
 
 set(PY_LIB ${${PY_NAME}_LIB})
diff --git a/python/Estimator.cpp b/python/Estimator.cpp
new file mode 100644
index 00000000..116688a6
--- /dev/null
+++ b/python/Estimator.cpp
@@ -0,0 +1,51 @@
+#include "qrw/Estimator.hpp"
+
+#include "bindings/python.hpp"
+
+template <typename Estimator>
+struct EstimatorVisitor : public bp::def_visitor<EstimatorVisitor<Estimator>> {
+  template <class PyClassEstimator>
+  void visit(PyClassEstimator& cl) const {
+    cl.def(bp::init<>(bp::arg(""), "Default constructor."))
+
+        .def("initialize", &Estimator::initialize, bp::args("params"), "Initialize Estimator from Python.\n")
+        .def("update_reference_state", &Estimator::updateReferenceState, bp::args("v_ref"), "Update robot state.\n")
+
+        .def("get_q_estimate", &Estimator::getQEstimate, "Get filtered configuration.\n")
+        .def("get_v_estimate", &Estimator::getVEstimate, "Get filtered velocity.\n")
+        .def("get_v_security", &Estimator::getVSecurity, "Get filtered velocity for security check.\n")
+        .def("get_feet_status", &Estimator::getFeetStatus, "")
+        .def("get_feet_targets", &Estimator::getFeetTargets, "")
+        .def("get_base_velocity_FK", &Estimator::getBaseVelocityFK, "")
+        .def("get_base_position_FK", &Estimator::getBasePositionFK, "")
+        .def("get_feet_position_barycenter", &Estimator::getFeetPositionBarycenter, "")
+        .def("get_b_base_velocity", &Estimator::getBBaseVelocity, "")
+        .def("get_filter_vel_X", &Estimator::getFilterVelX, "")
+        .def("get_filter_vel_DX", &Estimator::getFilterVelDX, "")
+        .def("get_filter_vel_Alpha", &Estimator::getFilterVelAlpha, "")
+        .def("get_filter_vel_FiltX", &Estimator::getFilterVelFiltX, "")
+        .def("get_filter_pos_X", &Estimator::getFilterPosX, "")
+        .def("get_filter_pos_DX", &Estimator::getFilterPosDX, "")
+        .def("get_filter_pos_Alpha", &Estimator::getFilterPosAlpha, "")
+        .def("get_filter_pos_FiltX", &Estimator::getFilterPosFiltX, "")
+        .def("get_q_reference", &Estimator::getQReference, "")
+        .def("get_v_reference", &Estimator::getVReference, "")
+        .def("get_base_vel_ref", &Estimator::getBaseVelRef, "")
+        .def("get_base_acc_ref", &Estimator::getBaseAccRef, "")
+        .def("get_h_v", &Estimator::getHV, "")
+        .def("get_v_filtered", &Estimator::getVFiltered, "Get filtered velocity.\n")
+        .def("get_h_v_filtered", &Estimator::getHVFiltered, "")
+        .def("get_oRh", &Estimator::getoRh, "")
+        .def("get_hRb", &Estimator::gethRb, "")
+        .def("get_oTh", &Estimator::getoTh, "")
+
+        .def("run", &Estimator::run,
+             bp::args("gait", "goals", "baseLinearAcceleration", "baseAngularVelocity", "baseOrientation", "q_mes",
+                      "v_mes", "base_position", "b_base_velocity"),
+             "Run Estimator from Python.\n");
+  }
+
+  static void expose() { bp::class_<Estimator>("Estimator", bp::no_init).def(EstimatorVisitor<Estimator>()); }
+};
+
+void exposeEstimator() { EstimatorVisitor<Estimator>::expose(); }
diff --git a/python/Filter.cpp b/python/Filter.cpp
new file mode 100644
index 00000000..00fc7e81
--- /dev/null
+++ b/python/Filter.cpp
@@ -0,0 +1,16 @@
+#include "qrw/Filter.hpp"
+
+#include "bindings/python.hpp"
+template <typename Filter>
+struct FilterVisitor : public bp::def_visitor<FilterVisitor<Filter>> {
+  template <class PyClassFilter>
+  void visit(PyClassFilter& cl) const {
+    cl.def(bp::init<>(bp::arg(""), "Default constructor."))
+        .def("initialize", &Filter::initialize, bp::args("params"), "Initialize Filter from Python.\n")
+        .def("filter", &Filter::filter, bp::args("x", "check_modulo"), "Run Filter from Python.\n");
+  }
+
+  static void expose() { bp::class_<Filter>("Filter", bp::no_init).def(FilterVisitor<Filter>()); }
+};
+
+void exposeFilter() { FilterVisitor<Filter>::expose(); }
\ No newline at end of file
diff --git a/python/main.cpp b/python/main.cpp
index be09c5e7..a74a5454 100644
--- a/python/main.cpp
+++ b/python/main.cpp
@@ -3,4 +3,6 @@
 BOOST_PYTHON_MODULE(quadruped_reactive_walking_pywrap) {
   exposeJoystick();
   exposeParams();
+  exposeEstimator();
+  exposeFilter();
 }
\ No newline at end of file
diff --git a/python/quadruped_reactive_walking/Controller.py b/python/quadruped_reactive_walking/Controller.py
index 32c81be0..8db3642d 100644
--- a/python/quadruped_reactive_walking/Controller.py
+++ b/python/quadruped_reactive_walking/Controller.py
@@ -4,9 +4,10 @@ import numpy as np
 import pinocchio as pin
 import pybullet as pyb
 
+import quadruped_reactive_walking as qrw
 from . import WB_MPC_Wrapper
 from .WB_MPC.Target import Target
-from .tools.Utils import init_robot
+from .tools.Utils import init_robot, quaternionToRPY
 
 COLORS = ["#1f77b4", "#ff7f0e", "#2ca02c"]
 
@@ -161,6 +162,10 @@ class Controller:
         self.error = False
         self.initialized = False
 
+        self.estimator = qrw.Estimator()
+        self.estimator.initialize(params)
+        self.q = np.zeros(18)
+
         self.result = Result(params)
         self.result.q_des = self.pd.q0[7:].copy()
         self.result.v_des = self.pd.v0[6:].copy()
@@ -205,6 +210,9 @@ class Controller:
         t_start = time.time()
 
         m = self.read_state(device)
+
+        oRh, hRb, oTh = self.run_estimator(device)
+
         t_measures = time.time()
         self.t_measures = t_measures - t_start
 
@@ -417,6 +425,48 @@ class Controller:
         )
         print("Initial guess saved")
 
+    def run_estimator(
+        self, device, q_perfect=np.zeros(6), b_baseVel_perfect=np.zeros(3)
+    ):
+        """
+        Call the estimator and retrieve the reference and estimated quantities.
+        Run a filter on q, h_v and v_ref.
+
+        @param device device structure holding simulation data
+        @param q_perfect 6D perfect position of the base in world frame
+        @param v_baseVel_perfect 3D perfect linear velocity of the base in base frame
+        """
+
+        self.estimator.run(
+            self.gait,
+            self.target.compute(self.k),
+            device.imu.linear_acceleration,
+            device.imu.gyroscope,
+            device.imu.attitude_euler,
+            device.joints.positions,
+            device.joints.velocities,
+            q_perfect,
+            b_baseVel_perfect,
+        )
+
+        self.estimator.update_reference_state(np.zeros(6))
+        # Add joystck reference velocity when needed
+
+        oRh = self.estimator.get_oRh()
+        hRb = self.estimator.get_hRb()
+        oTh = self.estimator.get_oTh().reshape((3, 1))
+
+        self.v_ref = self.estimator.get_base_vel_ref()
+        self.h_v = self.estimator.get_h_v()
+        self.h_v_windowed = self.estimator.get_h_v_filtered()
+
+        self.q[:3] = self.estimator.get_q_estimate()[:3]
+        self.q[6:] = self.estimator.get_q_estimate()[7:]
+        self.q[3:6] = quaternionToRPY(self.estimator.get_q_estimate()[3:7]).ravel()
+        self.v = self.estimator.get_v_reference()
+
+        return oRh, hRb, oTh
+
     def read_state(self, device):
         qj_m = device.joints.positions
         vj_m = device.joints.velocities
diff --git a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
index 56541ea0..568a58ae 100644
--- a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
+++ b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
@@ -9,8 +9,8 @@ class problemDataAbstract:
         self.dt_wbc = param.dt_wbc
         self.mpc_wbc_ratio = int(self.dt / self.dt_wbc)
         self.init_steps = 0
-        self.target_steps = 100
-        self.T = self.init_steps + self.target_steps - 1
+        self.target_steps = param.gait.shape[0]
+        self.T = param.gait.shape[0] - 1
 
         self.robot = erd.load("solo12")
         self.q0 = self.robot.q0
@@ -23,8 +23,7 @@ class problemDataAbstract:
 
         self.frozen_names = frozen_names
         if frozen_names:
-            self.frozen_idxs = [self.model.getJointId(
-                id) for id in frozen_names]
+            self.frozen_idxs = [self.model.getJointId(id) for id in frozen_names]
             self.freeze()
 
         self.nq = self.model.nq
@@ -162,8 +161,7 @@ class ProblemData(problemDataAbstract):
 
 class ProblemDataFull(problemDataAbstract):
     def __init__(self, param):
-        frozen_names = [
-            "root_joint"]
+        frozen_names = ["root_joint"]
 
         super().__init__(param, frozen_names)
 
@@ -176,13 +174,9 @@ class ProblemDataFull(problemDataAbstract):
         # self.friction_cone_w = 1e3 * 0
         self.control_bound_w = 1e3
         self.control_reg_w = 1e-1
-        self.state_reg_w = np.array([1e1] * 3
-                                    + [1e-2] * 3
-                                    + [1e1] * 6
-                                    + [1e1] * 3
-                                    + [3*1e-1] * 3
-                                    + [1e1] * 6
-                                    )
+        self.state_reg_w = np.array(    
+            [1e1] * 3 + [1e-2] * 3 + [1e1] * 6 + [1e1] * 3 + [3 * 1e-1] * 3 + [1e1] * 6
+        )
         self.terminal_velocity_w = np.array([0] * 12 + [1e3] * 12)
 
         self.q0_reduced = self.q0[7:]
diff --git a/python/quadruped_reactive_walking/WB_MPC/Target.py b/python/quadruped_reactive_walking/WB_MPC/Target.py
index a5b84bd6..fbbf86a4 100644
--- a/python/quadruped_reactive_walking/WB_MPC/Target.py
+++ b/python/quadruped_reactive_walking/WB_MPC/Target.py
@@ -27,6 +27,8 @@ class Target:
             self.max_height = params.max_height
             self.T = self.k_per_step * self.dt_wbc
             self.A = np.zeros((6, 3))
+
+            self.update_time = -1
         else:
             self.target_footstep = self.position + np.array([0.0, 0.0, 0.10])
 
@@ -61,8 +63,9 @@ class Target:
             target = self.initial
 
         k_step = k % self.k_per_step
-        if k_step == 0:
+        if n_step != self.update_time:
             self.update_polynomial(initial, target)
+            self.update_time = n_step
 
         t = k_step * self.dt_wbc
         return self.compute_position(j, t)
diff --git a/python/quadruped_reactive_walking/tools/Utils.py b/python/quadruped_reactive_walking/tools/Utils.py
index 1af8310e..c58f23a0 100644
--- a/python/quadruped_reactive_walking/tools/Utils.py
+++ b/python/quadruped_reactive_walking/tools/Utils.py
@@ -68,3 +68,36 @@ def init_robot(q_init, params):
             params.shoulders[3 * i + j] = shoulders_init[j, i]
             params.footsteps_init[3 * i + j] = fsteps_init[j, i]
             params.footsteps_under_shoulders[3 * i + j] = fsteps_init[j, i]
+
+
+def quaternionToRPY(quat):
+    """Quaternion (4 x 0) to Roll Pitch Yaw (3 x 1)"""
+
+    qx = quat[0]
+    qy = quat[1]
+    qz = quat[2]
+    qw = quat[3]
+
+    rotateXa0 = 2.0 * (qy * qz + qw * qx)
+    rotateXa1 = qw * qw - qx * qx - qy * qy + qz * qz
+    rotateX = 0.0
+
+    if (rotateXa0 != 0.0) and (rotateXa1 != 0.0):
+        rotateX = np.arctan2(rotateXa0, rotateXa1)
+
+    rotateYa0 = -2.0 * (qx * qz - qw * qy)
+    rotateY = 0.0
+    if rotateYa0 >= 1.0:
+        rotateY = np.pi / 2.0
+    elif rotateYa0 <= -1.0:
+        rotateY = -np.pi / 2.0
+    else:
+        rotateY = np.arcsin(rotateYa0)
+
+    rotateZa0 = 2.0 * (qx * qy + qw * qz)
+    rotateZa1 = qw * qw + qx * qx - qy * qy - qz * qz
+    rotateZ = 0.0
+    if (rotateZa0 != 0.0) and (rotateZa1 != 0.0):
+        rotateZ = np.arctan2(rotateZa0, rotateZa1)
+
+    return np.array([[rotateX], [rotateY], [rotateZ]])
diff --git a/src/ComplementaryFilter.cpp b/src/ComplementaryFilter.cpp
new file mode 100644
index 00000000..64190ddd
--- /dev/null
+++ b/src/ComplementaryFilter.cpp
@@ -0,0 +1,37 @@
+#include "qrw/ComplementaryFilter.hpp"
+
+ComplementaryFilter::ComplementaryFilter()
+    : dt_(0.),
+      HighPass_(Vector3::Zero()),
+      LowPass_(Vector3::Zero()),
+      alpha_(Vector3::Zero()),
+      x_(Vector3::Zero()),
+      dx_(Vector3::Zero()),
+      filteredX_(Vector3::Zero()) {}
+
+ComplementaryFilter::ComplementaryFilter(double dt, Vector3 HighPass, Vector3 LowPass)
+    : dt_(dt),
+      HighPass_(HighPass),
+      LowPass_(LowPass),
+      alpha_(Vector3::Zero()),
+      x_(Vector3::Zero()),
+      dx_(Vector3::Zero()),
+      filteredX_(Vector3::Zero()) {}
+
+void ComplementaryFilter::initialize(double dt, Vector3 HighPass, Vector3 LowPass) {
+  dt_ = dt;
+  HighPass_ = HighPass;
+  LowPass_ = LowPass;
+}
+
+Vector3 ComplementaryFilter::compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha) {
+  alpha_ = alpha;
+  x_ = x;
+  dx_ = dx;
+
+  HighPass_ = alpha.cwiseProduct(HighPass_ + dx_ * dt_);
+  LowPass_ = alpha.cwiseProduct(LowPass_) + (Vector3::Ones() - alpha).cwiseProduct(x_);
+  filteredX_ = HighPass_ + LowPass_;
+
+  return filteredX_;
+}
\ No newline at end of file
diff --git a/src/Estimator.cpp b/src/Estimator.cpp
new file mode 100644
index 00000000..7485d184
--- /dev/null
+++ b/src/Estimator.cpp
@@ -0,0 +1,283 @@
+#include "qrw/Estimator.hpp"
+
+#include <example-robot-data/path.hpp>
+
+#include "pinocchio/algorithm/compute-all-terms.hpp"
+#include "pinocchio/algorithm/frames.hpp"
+#include "pinocchio/math/rpy.hpp"
+#include "pinocchio/parsers/urdf.hpp"
+
+Estimator::Estimator()
+    : perfectEstimator_(false),
+      solo3D_(false),
+      dt_(0.0),
+      initialized_(false),
+      feetFrames_(Vector4::Zero()),
+      footRadius_(0.155),
+      alphaPos_({0.995, 0.995, 0.9}),
+      alphaVelMax_(1.),
+      alphaVelMin_(0.97),
+      alphaSecurity_(0.),
+      IMUYawOffset_(0.),
+      IMULinearAcceleration_(Vector3::Zero()),
+      IMUAngularVelocity_(Vector3::Zero()),
+      IMURpy_(Vector3::Zero()),
+      IMUQuat_(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0)),
+      qActuators_(Vector12::Zero()),
+      vActuators_(Vector12::Zero()),
+      phaseRemainingDuration_(0),
+      feetStancePhaseDuration_(Vector4::Zero()),
+      feetStatus_(Vector4::Zero()),
+      feetTargets_(Matrix34::Zero()),
+      q_FK_(Vector19::Zero()),
+      v_FK_(Vector18::Zero()),
+      baseVelocityFK_(Vector3::Zero()),
+      basePositionFK_(Vector3::Zero()),
+      b_baseVelocity_(Vector3::Zero()),
+      feetPositionBarycenter_(Vector3::Zero()),
+      qEstimate_(Vector19::Zero()),
+      vEstimate_(Vector18::Zero()),
+      vSecurity_(Vector12::Zero()),
+      windowSize_(0),
+      vFiltered_(Vector6::Zero()),
+      qRef_(Vector18::Zero()),
+      vRef_(Vector18::Zero()),
+      baseVelRef_(Vector6::Zero()),
+      baseAccRef_(Vector6::Zero()),
+      oRh_(Matrix3::Identity()),
+      hRb_(Matrix3::Identity()),
+      oTh_(Vector3::Zero()),
+      h_v_(Vector6::Zero()),
+      h_vFiltered_(Vector6::Zero()) {
+  b_M_IMU_ = pinocchio::SE3(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0), Vector3(0.1163, 0.0, 0.02));
+  q_FK_(6) = 1.0;
+  qEstimate_(6) = 1.0;
+}
+
+void Estimator::initialize(Params& params) {
+  dt_ = params.dt_wbc;
+  perfectEstimator_ = params.perfect_estimator;
+  solo3D_ = params.solo3D;
+
+  // Filtering estimated linear velocity
+  int k_mpc = (int)(std::round(params.dt_mpc / params.dt_wbc));
+  windowSize_ = (int)(k_mpc * params.gait.rows() / params.N_periods);
+  vx_queue_.resize(windowSize_, 0.0);  // List full of 0.0
+  vy_queue_.resize(windowSize_, 0.0);  // List full of 0.0
+  vz_queue_.resize(windowSize_, 0.0);  // List full of 0.0
+
+  // Filtering velocities used for security checks
+  double fc = 6.0;
+  double y = 1 - std::cos(2 * M_PI * 6. * dt_);
+  alphaSecurity_ = -y + std::sqrt(y * y + 2 * y);
+
+  // Initialize Quantities
+  basePositionFK_(2) = params.h_ref;
+  velocityFilter_.initialize(dt_, Vector3::Zero(), Vector3::Zero());
+  positionFilter_.initialize(dt_, Vector3::Zero(), basePositionFK_);
+  qRef_(2, 0) = params.h_ref;
+  qRef_.tail(12) = Vector12(params.q_init.data());
+
+  // Initialize Pinocchio
+  const std::string filename = std::string(EXAMPLE_ROBOT_DATA_MODEL_DIR "/solo_description/robots/solo12.urdf");
+  pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), velocityModel_, false);
+  pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), positionModel_, false);
+  velocityData_ = pinocchio::Data(velocityModel_);
+  positionData_ = pinocchio::Data(positionModel_);
+  pinocchio::computeAllTerms(velocityModel_, velocityData_, qEstimate_, vEstimate_);
+  pinocchio::computeAllTerms(positionModel_, positionData_, qEstimate_, vEstimate_);
+  feetFrames_ << (int)positionModel_.getFrameId("FL_FOOT"), (int)positionModel_.getFrameId("FR_FOOT"),
+      (int)positionModel_.getFrameId("HL_FOOT"), (int)positionModel_.getFrameId("HR_FOOT");
+}
+
+void Estimator::run(MatrixN const& gait, MatrixN const& feetTargets, VectorN const& baseLinearAcceleration,
+                    VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q,
+                    VectorN const& v, VectorN const& perfectPosition, Vector3 const& b_perfectVelocity) {
+  updatFeetStatus(gait, feetTargets);
+  updateIMUData(baseLinearAcceleration, baseAngularVelocity, baseOrientation, perfectPosition);
+  updateJointData(q, v);
+
+  updateForwardKinematics();
+  computeFeetPositionBarycenter();
+
+  estimateVelocity(b_perfectVelocity);
+  estimatePosition(perfectPosition.head(3));
+
+  filterVelocity();
+
+  vSecurity_ = (1 - alphaSecurity_) * vActuators_ + alphaSecurity_ * vSecurity_;
+}
+
+void Estimator::updateReferenceState(VectorN const& vRef) {
+  // Update reference acceleration and velocities
+  Matrix3 Rz = pinocchio::rpy::rpyToMatrix(0., 0., -baseVelRef_[5] * dt_);
+  baseAccRef_.head(3) = (vRef.head(3) - Rz * baseVelRef_.head(3)) / dt_;
+  baseAccRef_.tail(3) = (vRef.tail(3) - Rz * baseVelRef_.tail(3)) / dt_;
+  baseVelRef_ = vRef;
+
+  // Update position and velocity state vectors
+  Rz = pinocchio::rpy::rpyToMatrix(0., 0., qRef_[5]);
+  vRef_.head(2) = Rz.topLeftCorner(2, 2) * baseVelRef_.head(2);
+  vRef_[5] = baseVelRef_[5];
+  vRef_.tail(12) = vActuators_;
+
+  qRef_.head(2) += vRef_.head(2) * dt_;
+  qRef_[2] = qEstimate_[2];
+  qRef_.segment(3, 2) = IMURpy_.head(2);
+  qRef_[5] += baseVelRef_[5] * dt_;
+  qRef_.tail(12) = qActuators_;
+
+  // Transformation matrices
+  hRb_ = pinocchio::rpy::rpyToMatrix(IMURpy_[0], IMURpy_[1], 0.);
+  oRh_ = pinocchio::rpy::rpyToMatrix(0., 0., qRef_[5]);
+  oTh_.head(2) = qRef_.head(2);
+
+  // Express estimated velocity and filtered estimated velocity in horizontal frame
+  h_v_.head(3) = hRb_ * vEstimate_.head(3);
+  h_v_.tail(3) = hRb_ * vEstimate_.segment(3, 3);
+  h_vFiltered_.head(3) = hRb_ * vFiltered_.head(3);
+  h_vFiltered_.tail(3) = hRb_ * vFiltered_.tail(3);
+}
+
+void Estimator::updatFeetStatus(MatrixN const& gait, MatrixN const& feetTargets) {
+  feetStatus_ = gait.row(0);
+  feetTargets_ = feetTargets;
+
+  feetStancePhaseDuration_ += feetStatus_;
+  feetStancePhaseDuration_ = feetStancePhaseDuration_.cwiseProduct(feetStatus_);
+
+  phaseRemainingDuration_ = 1;
+  while (feetStatus_.isApprox((Vector4)gait.row(phaseRemainingDuration_))) {
+    phaseRemainingDuration_++;
+  }
+}
+
+void Estimator::updateIMUData(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity,
+                              Vector3 const& baseOrientation, VectorN const& perfectPosition) {
+  IMULinearAcceleration_ = baseLinearAcceleration;
+  IMUAngularVelocity_ = baseAngularVelocity;
+  IMURpy_ = baseOrientation;
+
+  if (!initialized_) {
+    IMUYawOffset_ = IMURpy_(2);
+    initialized_ = true;
+  }
+  IMURpy_(2) -= IMUYawOffset_;
+
+  if (solo3D_) {
+    IMURpy_.tail(1) = perfectPosition.tail(1);
+  }
+
+  IMUQuat_ = pinocchio::SE3::Quaternion(pinocchio::rpy::rpyToMatrix(IMURpy_(0), IMURpy_(1), IMURpy_(2)));
+}
+
+void Estimator::updateJointData(Vector12 const& q, Vector12 const& v) {
+  qActuators_ = q;
+  vActuators_ = v;
+}
+
+void Estimator::updateForwardKinematics() {
+  q_FK_.tail(12) = qActuators_;
+  v_FK_.tail(12) = vActuators_;
+  q_FK_.segment(3, 4) << 0., 0., 0., 1.;
+  pinocchio::forwardKinematics(velocityModel_, velocityData_, q_FK_, v_FK_);
+
+  q_FK_.segment(3, 4) = IMUQuat_.coeffs();
+  pinocchio::forwardKinematics(positionModel_, positionData_, q_FK_);
+
+  int nContactFeet = 0;
+  Vector3 baseVelocityEstimate = Vector3::Zero();
+  Vector3 basePositionEstimate = Vector3::Zero();
+  for (int foot = 0; foot < 4; foot++) {
+    if (feetStatus_(foot) == 1. && feetStancePhaseDuration_[foot] >= 16) {
+      baseVelocityEstimate += computeBaseVelocityFromFoot(foot);
+      basePositionEstimate += computeBasePositionFromFoot(foot);
+      nContactFeet++;
+    }
+  }
+
+  if (nContactFeet > 0) {
+    baseVelocityFK_ = baseVelocityEstimate / nContactFeet;
+    basePositionFK_ = basePositionEstimate / nContactFeet;
+  }
+}
+
+Vector3 Estimator::computeBaseVelocityFromFoot(int footId) {
+  pinocchio::updateFramePlacement(velocityModel_, velocityData_, feetFrames_[footId]);
+  pinocchio::SE3 contactFrame = velocityData_.oMf[feetFrames_[footId]];
+  Vector3 frameVelocity =
+      pinocchio::getFrameVelocity(velocityModel_, velocityData_, feetFrames_[footId], pinocchio::LOCAL).linear();
+
+  return contactFrame.translation().cross(IMUAngularVelocity_) - contactFrame.rotation() * frameVelocity;
+}
+
+Vector3 Estimator::computeBasePositionFromFoot(int footId) {
+  pinocchio::updateFramePlacement(positionModel_, positionData_, feetFrames_[footId]);
+  Vector3 basePosition = -positionData_.oMf[feetFrames_[footId]].translation();
+  basePosition(0) += footRadius_ * (vActuators_(1 + 3 * footId) + vActuators_(2 + 3 * footId));
+
+  return basePosition;
+}
+
+void Estimator::computeFeetPositionBarycenter() {
+  int nContactFeet = 0;
+  Vector3 feetPositions = Vector3::Zero();
+  for (int j = 0; j < 4; j++) {
+    if (feetStatus_(j) == 1.) {
+      feetPositions += feetTargets_.col(j);
+      nContactFeet++;
+    }
+  }
+  if (nContactFeet > 0) feetPositionBarycenter_ = feetPositions / nContactFeet;
+}
+
+double Estimator::computeAlphaVelocity() {
+  double a = std::ceil(feetStancePhaseDuration_.maxCoeff() * 0.1) - 1;
+  double b = static_cast<double>(phaseRemainingDuration_);
+  double c = ((a + b) - 2) * 0.5;
+  if (a <= 0 || b <= 1)
+    return alphaVelMax_;
+  else
+    return alphaVelMin_ + (alphaVelMax_ - alphaVelMin_) * std::abs(c - (a - 1)) / c;
+}
+
+void Estimator::estimateVelocity(Vector3 const& b_perfectVelocity) {
+  Vector3 alpha = Vector3::Ones() * computeAlphaVelocity();
+  Matrix3 oRb = IMUQuat_.toRotationMatrix();
+  Vector3 bTi = (b_M_IMU_.translation()).cross(IMUAngularVelocity_);
+
+  // At IMU location in world frame
+  Vector3 oi_baseVelocityFK = solo3D_ ? oRb * (b_perfectVelocity + bTi) : oRb * (baseVelocityFK_ + bTi);
+  Vector3 oi_baseVelocity = velocityFilter_.compute(oi_baseVelocityFK, oRb * IMULinearAcceleration_, alpha);
+
+  // At base location in base frame
+  b_baseVelocity_ = oRb.transpose() * oi_baseVelocity - bTi;
+
+  vEstimate_.head(3) = perfectEstimator_ ? b_perfectVelocity : b_baseVelocity_;
+  vEstimate_.segment(3, 3) = IMUAngularVelocity_;
+  vEstimate_.tail(12) = vActuators_;
+}
+
+void Estimator::estimatePosition(Vector3 const& perfectPosition) {
+  Matrix3 oRb = IMUQuat_.toRotationMatrix();
+
+  Vector3 basePosition = solo3D_ ? perfectPosition : (basePositionFK_ + feetPositionBarycenter_);
+  qEstimate_.head(3) = positionFilter_.compute(basePosition, oRb * b_baseVelocity_, alphaPos_);
+
+  if (perfectEstimator_ || solo3D_) qEstimate_(2) = perfectPosition(2);
+  qEstimate_.segment(3, 4) = IMUQuat_.coeffs();
+  qEstimate_.tail(12) = qActuators_;
+}
+
+void Estimator::filterVelocity() {
+  vFiltered_ = vEstimate_.head(6);
+  vx_queue_.pop_back();
+  vy_queue_.pop_back();
+  vz_queue_.pop_back();
+  vx_queue_.push_front(vEstimate_(0));
+  vy_queue_.push_front(vEstimate_(1));
+  vz_queue_.push_front(vEstimate_(2));
+  vFiltered_(0) = std::accumulate(vx_queue_.begin(), vx_queue_.end(), 0.) / windowSize_;
+  vFiltered_(1) = std::accumulate(vy_queue_.begin(), vy_queue_.end(), 0.) / windowSize_;
+  vFiltered_(2) = std::accumulate(vz_queue_.begin(), vz_queue_.end(), 0.) / windowSize_;
+}
diff --git a/src/Filter.cpp b/src/Filter.cpp
new file mode 100644
index 00000000..fac84ef2
--- /dev/null
+++ b/src/Filter.cpp
@@ -0,0 +1,73 @@
+#include "qrw/Filter.hpp"
+
+Filter::Filter()
+    : b_(0.),
+      a_(Vector2::Zero()),
+      x_(Vector6::Zero()),
+      y_(VectorN::Zero(6, 1)),
+      accum_(Vector6::Zero()),
+      init_(false) {
+  // Empty
+}
+
+void Filter::initialize(Params& params) {
+  const double fc = 15.0;
+  b_ = (2 * M_PI * params.dt_wbc * fc) / (2 * M_PI * params.dt_wbc * fc + 1.0);
+
+  a_ << 1.0, -(1.0 - b_);
+
+  x_queue_.resize(1, Vector6::Zero());
+  y_queue_.resize(a_.rows() - 1, Vector6::Zero());
+}
+
+VectorN Filter::filter(Vector6 const& x, bool check_modulo) {
+  // Retrieve measurement
+  x_ = x;
+
+  // Handle modulo for orientation
+  if (check_modulo) {
+    // Handle 2 pi modulo for roll, pitch and yaw
+    // Should happen sometimes for yaw but now for roll and pitch except
+    // if the robot rolls over
+    for (int i = 3; i < 6; i++) {
+      if (std::abs(x_(i, 0) - y_(i, 0)) > 1.5 * M_PI) {
+        handle_modulo(i, x_(i, 0) - y_(i, 0) > 0);
+      }
+    }
+  }
+
+  // Initialisation of the value in the queues to the first measurement
+  if (!init_) {
+    init_ = true;
+    std::fill(x_queue_.begin(), x_queue_.end(), x_.head(6));
+    std::fill(y_queue_.begin(), y_queue_.end(), x_.head(6));
+  }
+
+  // Store measurement in x queue
+  x_queue_.pop_back();
+  x_queue_.push_front(x_.head(6));
+
+  // Compute result (y/x = b/a for the transfert function)
+  accum_ = b_ * x_queue_[0];
+  for (int i = 1; i < a_.rows(); i++) {
+    accum_ -= a_[i] * y_queue_[i - 1];
+  }
+
+  // Store result in y queue for recursion
+  y_queue_.pop_back();
+  y_queue_.push_front(accum_ / a_[0]);
+
+  // Filtered result is stored in y_queue_.front()
+  // Assigned to dynamic-sized vector for binding purpose
+  y_ = y_queue_.front();
+
+  return y_;
+}
+
+void Filter::handle_modulo(int a, bool dir) {
+  // Add or remove 2 PI to all elements in the queues
+  x_queue_[0](a, 0) += dir ? 2.0 * M_PI : -2.0 * M_PI;
+  for (int i = 1; i < a_.rows(); i++) {
+    (y_queue_[i - 1])(a, 0) += dir ? 2.0 * M_PI : -2.0 * M_PI;
+  }
+}
-- 
GitLab