diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3bf95088b41084f327a217704aa2706b00675720..79ed11471e96fe7b843d384a0cb466c248b66392 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,7 @@ set(${PROJECT_NAME}_HEADERS
   include/qrw/Params.hpp
   include/qrw/Estimator.hpp
   include/qrw/Joystick.hpp
+  include/qrw/Filter.hpp
   include/other/st_to_cc.hpp
   )
 
@@ -75,6 +76,7 @@ set(${PROJECT_NAME}_SOURCES
   src/Params.cpp
   src/Estimator.cpp
   src/Joystick.cpp
+  src/Filter.cpp
   )
 
 add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
diff --git a/include/qrw/Filter.hpp b/include/qrw/Filter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b3b34176171ccc49913a1fb4cadcdd7ef048e73
--- /dev/null
+++ b/include/qrw/Filter.hpp
@@ -0,0 +1,75 @@
+#ifndef FILTER_H_INCLUDED
+#define FILTER_H_INCLUDED
+
+#include <iostream>
+#include <fstream>
+#include <cmath>
+#include <deque>
+#include <Eigen/Core>
+#include <Eigen/Dense>
+#include "qrw/Params.hpp"
+#include "pinocchio/math/rpy.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
+    ///
+    /// \param[in] x Quantity to filter
+    ///
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void filter(VectorN const& x);
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    ///
+    /// \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:
+
+    Vector5 b_;  // Denominator coefficients of the filter transfer function
+    Vector5 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/src/Filter.cpp b/src/Filter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..291cfc5e91d92cf83101b2c7a0a47e34f40f2648
--- /dev/null
+++ b/src/Filter.cpp
@@ -0,0 +1,104 @@
+#include "qrw/Filter.hpp"
+
+Filter::Filter()
+  : b_(Vector5::Zero())
+  , a_(Vector5::Zero())
+  , x_(Vector6::Zero())
+  , y_(VectorN::Zero(6, 1))
+  , accum_(Vector6::Zero())
+  , init_(false)
+{
+  // Empty
+}
+
+void Filter::initialize(Params& params)
+{
+  if (params.dt_wbc == 0.001)
+  {
+    b_ << 3.12389769e-5, 1.24955908e-4, 1.87433862e-4, 1.24955908e-4, 3.12389769e-5;
+    a_ << 1., -3.58973389, 4.85127588, -2.92405266, 0.66301048;
+  }
+  else if (params.dt_wbc == 0.002)
+  {
+    b_ << 0.0004166, 0.0016664, 0.0024996, 0.0016664, 0.0004166;
+    a_ << 1., -3.18063855, 3.86119435, -2.11215536, 0.43826514;
+  }
+  else
+  {
+    throw std::runtime_error("No coefficients defined for this time step.");
+  }
+  
+  x_queue_.resize(b_.rows(), Vector6::Zero());
+  y_queue_.resize(a_.rows()-1, Vector6::Zero());
+}
+
+void Filter::filter(VectorN const& x)
+{
+  // If x is position + quaternion then we convert quaternion to RPY
+  if (x.rows() == 7)
+  {
+    x_.head(3) = x.head(3);
+    Eigen::Quaterniond quat(x(6, 0), x(3, 0), x(4, 0), x(5, 0));  // w, x, y, z
+    x_.tail(3) = pinocchio::rpy::matrixToRpy(quat.toRotationMatrix());
+
+    // 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)
+      {
+        std::cout << "Modulo for " << i << std::endl;
+        handle_modulo(i, x_(i, 0) - y_(i, 0) > 0);
+      }
+    }
+  }
+  else  // Otherwise we can directly use x
+  {
+    x_ = x;
+  }
+
+  // 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_ = Vector6::Zero();
+  for (int i = 0; i < b_.rows(); i++) 
+  {
+    accum_ += b_[i] * x_queue_[i];
+  }
+  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();
+}
+
+void Filter::handle_modulo(int a, bool dir)
+{
+  // Add or remove 2 PI to all elements in the queues
+  for (int i = 0; i < b_.rows(); i++) 
+  {
+    (x_queue_[i])(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;
+  }
+}