From f688142a9128e42a8577730a249095ae7e0688ae Mon Sep 17 00:00:00 2001
From: odri <odri@furano.laas.fr>
Date: Thu, 19 Aug 2021 14:43:50 +0200
Subject: [PATCH] Working on Logging (adding, removing, cleaning)

---
 include/qrw/Estimator.hpp      |  7 ++--
 include/qrw/QPWBC.hpp          |  2 ++
 python/gepadd.cpp              |  3 ++
 scripts/Controller.py          | 63 +++-----------------------------
 scripts/LoggerControl.py       | 65 ++++++++++++++++------------------
 scripts/main_solo12_control.py |  2 +-
 src/Estimator.cpp              | 35 ++++++++----------
 7 files changed, 60 insertions(+), 117 deletions(-)

diff --git a/include/qrw/Estimator.hpp b/include/qrw/Estimator.hpp
index 4ea154c0..bd7daa67 100644
--- a/include/qrw/Estimator.hpp
+++ b/include/qrw/Estimator.hpp
@@ -164,7 +164,7 @@ class Estimator {
   ////////////////////////////////////////////////////////////////////////////////////////////////
   void run_filter(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& dummyPos, VectorN const& b_baseVel);
+                  VectorN const& v_mes, VectorN const& dummyPos, Vector3 const& b_baseVel);
 
   ////////////////////////////////////////////////////////////////////////////////////////////////
   ///
@@ -210,6 +210,7 @@ class Estimator {
   Vector3 getFilterPosFiltX() { return filter_xyz_pos_.getFiltX(); }
 
   VectorN getQUpdated() { return q_up_; }
+  VectorN getVUpdated() { return v_up_; }
   VectorN getVRef() { return v_ref_; }
   VectorN getHV() { return h_v_; }
   VectorN getHVWindowed() { return h_v_windowed_; }
@@ -223,7 +224,6 @@ class Estimator {
   ComplementaryFilter filter_xyz_vel_;  // Complementary filter for base velocity
 
   double dt_wbc;           // Time step of the estimator
-  double alpha_v_;         // Low pass coefficient for the outputted filtered velocity
   double alpha_secu_;      // Low pass coefficient for the outputted filtered velocity for security check
   double offset_yaw_IMU_;  // Yaw orientation of the IMU at startup
   bool perfect_estimator;  // Enable perfect estimator (directly from the PyBullet simulation)
@@ -267,7 +267,8 @@ class Estimator {
   Vector12 q_security_;  // Position limits for the actuators above which safety controller is triggered
 
   // For updateState function
-  VectorN q_up_;      // Configuration vector
+  VectorN q_up_;      // Configuration vector in ideal world frame
+  VectorN v_up_;      // Velocity vector in ideal world frame
   VectorN v_ref_;     // Reference velocity vector
   VectorN h_v_;       // Velocity vector in horizontal frame
   Matrix3 oRh_;       // Rotation between horizontal and world frame
diff --git a/include/qrw/QPWBC.hpp b/include/qrw/QPWBC.hpp
index b3296c36..e1189929 100644
--- a/include/qrw/QPWBC.hpp
+++ b/include/qrw/QPWBC.hpp
@@ -292,7 +292,9 @@ class WbcWrapper {
   VectorN get_qdes() { return qdes_; }
   VectorN get_vdes() { return vdes_; }
   VectorN get_tau_ff() { return tau_ff_; }
+  VectorN get_ddq_cmd() { return ddq_cmd_; }
   VectorN get_f_with_delta() { return f_with_delta_; }
+  VectorN get_ddq_with_delta() { return ddq_with_delta_; }
   MatrixN get_feet_pos() { return invkin_->get_posf().transpose(); }
   MatrixN get_feet_err() { return log_feet_pos_target - invkin_->get_posf().transpose(); }
   MatrixN get_feet_vel() { return invkin_->get_vf().transpose(); }
diff --git a/python/gepadd.cpp b/python/gepadd.cpp
index 9c5f633b..85f81109 100644
--- a/python/gepadd.cpp
+++ b/python/gepadd.cpp
@@ -297,7 +297,9 @@ struct WbcWrapperPythonVisitor : public bp::def_visitor<WbcWrapperPythonVisitor<
             .def_readonly("qdes", &WbcWrapper::get_qdes)
             .def_readonly("vdes", &WbcWrapper::get_vdes)
             .def_readonly("tau_ff", &WbcWrapper::get_tau_ff)
+            .def_readonly("ddq_cmd", &WbcWrapper::get_ddq_cmd)
             .def_readonly("f_with_delta", &WbcWrapper::get_f_with_delta)
+            .def_readonly("ddq_with_delta", &WbcWrapper::get_ddq_with_delta)
             .def_readonly("feet_pos", &WbcWrapper::get_feet_pos)
             .def_readonly("feet_err", &WbcWrapper::get_feet_err)
             .def_readonly("feet_vel", &WbcWrapper::get_feet_vel)
@@ -353,6 +355,7 @@ struct EstimatorPythonVisitor : public bp::def_visitor<EstimatorPythonVisitor<Es
             .def("getFilterPosAlpha", &Estimator::getFilterPosAlpha, "")
             .def("getFilterPosFiltX", &Estimator::getFilterPosFiltX, "")
             .def("getQUpdated", &Estimator::getQUpdated, "")
+            .def("getVUpdated", &Estimator::getVUpdated, "")
             .def("getVRef", &Estimator::getVRef, "")
             .def("getHV", &Estimator::getHV, "")
             .def("getHVWindowed", &Estimator::getHVWindowed, "")
diff --git a/scripts/Controller.py b/scripts/Controller.py
index d8044358..5b62c584 100644
--- a/scripts/Controller.py
+++ b/scripts/Controller.py
@@ -189,8 +189,6 @@ class Controller:
         # Interface with the PD+ on the control board
         self.result = Result()
 
-        test_footTrajectoryGenerator(params)
-
         # Run the control loop once with a dummy device for initialization
         dDevice = dummyDevice()
         dDevice.joints.positions = q_init
@@ -228,6 +226,7 @@ class Controller:
         self.h_v[0:6, 0] = self.estimator.getHV()
         self.h_v_windowed[0:6, 0] = self.estimator.getHVWindowed()
         self.q[:, 0] = self.estimator.getQUpdated()
+        self.v[:, 0] = self.estimator.getVUpdated()
         self.yaw_estim = self.estimator.getYawEstim()
         # TODO: Understand why using Python or C++ h_v leads to a slightly different result since the 
         # difference between them at each time step is 1e-16 at max (butterfly effect?)
@@ -330,7 +329,8 @@ class Controller:
             self.result.D = self.Kd_main * np.ones(12)
             self.result.q_des[:] = self.wbcWrapper.qdes[:]
             self.result.v_des[:] = self.wbcWrapper.vdes[:]
-            self.result.tau_ff[:] = self.Kff_main * self.wbcWrapper.tau_ff
+            self.result.FF = self.Kff_main * np.ones(12)
+            self.result.tau_ff[:] = self.wbcWrapper.tau_ff
 
             # Display robot in Gepetto corba viewer
             if self.enable_corba_viewer and (self.k % 5 == 0):
@@ -385,6 +385,7 @@ class Controller:
             self.result.D = 0.1 * np.ones(12)
             self.result.q_des[:] = np.zeros(12)
             self.result.v_des[:] = np.zeros(12)
+            self.result.FF = np.zeros(12)
             self.result.tau_ff[:] = np.zeros(12)
 
     def log_misc(self, tic, t_filter, t_planner, t_mpc, t_wbc):
@@ -394,59 +395,3 @@ class Controller:
         self.t_mpc = t_mpc - t_planner
         self.t_wbc = t_wbc - t_mpc
         self.t_loop = time.time() - tic
-
-def test_footTrajectoryGenerator(params):
-
-    gait = lqrw.Gait()
-    gait.initialize(params)
-
-    ftg = lqrw.FootTrajectoryGenerator()
-    ftg.initialize(params, gait)
-
-    o_targetFootstep = np.zeros((3, 4))
-    o_targetFootstep = 2.0 * ftg.getFootPosition()
-    k = 0
-    N = 1000
-    log_p_ref = np.zeros((N, 3, 4))
-    log_p_target = np.zeros((N, 3, 4))
-    log_p = np.zeros((N, 3, 4))
-    log_v = np.zeros((N, 3, 4))
-    log_a = np.zeros((N, 3, 4))
-    while k < N:
-
-        # Update reference
-        o_targetFootstep[0, :] -= 0.0001 
-
-        # Update gait
-        gait.updateGait(k, 20, 0)
-
-        # Update foot trajectory generator
-        ftg.update(k, o_targetFootstep)
-        log_p_ref[k] = o_targetFootstep
-        log_p_target[k] = ftg.getTargetPosition()
-        log_p[k] = ftg.getFootPosition()
-        log_v[k] = ftg.getFootVelocity()
-        log_a[k] = ftg.getFootAcceleration()
-
-        k += 1
-
-    from matplotlib import pyplot as plt
-
-    index12 = [1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12]
-    lgd_X = ["FL", "FR", "HL", "HR"]
-    lgd_Y = ["Pos X", "Pos Y", "Pos Z"]
-    plt.figure()
-    for i in range(12):
-        if i == 0:
-            ax0 = plt.subplot(3, 4, index12[i])
-        else:
-            plt.subplot(3, 4, index12[i], sharex=ax0)
-        plt.plot(log_p_ref[:, i % 3, np.int(i/3)], color='r', linewidth=3, marker='')
-        plt.plot(log_p_target[:, i % 3, np.int(i/3)], color='forestgreen', linewidth=3, marker='')
-        plt.plot(log_p[:, i % 3, np.int(i/3)], color='b', linewidth=3, marker='')
-        plt.legend([lgd_Y[i % 3] + " " + lgd_X[np.int(i/3)]+" Ref"], prop={'size': 8})
-    plt.suptitle("")
-
-    plt.show(block=True)
-
-    print("END TEST")
diff --git a/scripts/LoggerControl.py b/scripts/LoggerControl.py
index ff467175..59bdc9b6 100644
--- a/scripts/LoggerControl.py
+++ b/scripts/LoggerControl.py
@@ -41,11 +41,10 @@ class LoggerControl():
         self.esti_LP_filt_x = np.zeros([logSize, 3])  # filtered output of the position complementary filter
 
         # Loop
-        self.loop_o_q_int = np.zeros([logSize, 18])  # position in world frame (esti_q_filt + dt * loop_o_v)
+        self.loop_o_q = np.zeros([logSize, 18])  # position in ideal world frame (esti_q_filt + dt * loop_o_v)
         self.loop_o_v = np.zeros([logSize, 18])  # estimated velocity in world frame
         self.loop_h_v = np.zeros([logSize, 18])  # estimated velocity in horizontal frame
         self.loop_h_v_windowed = np.zeros([logSize, 6])  # estimated velocity in horizontal frame
-        self.loop_pos_virtual_world = np.zeros([logSize, 3])  # x, y, yaw perfect position in world
         self.loop_t_filter = np.zeros([logSize])  # time taken by the estimator
         self.loop_t_planner = np.zeros([logSize])  # time taken by the planning
         self.loop_t_mpc = np.zeros([logSize])  # time taken by the mcp
@@ -59,8 +58,6 @@ class LoggerControl():
         # Gait
         self.planner_gait = np.zeros([logSize, N0_gait, 4])  # Gait sequence
         self.planner_is_static = np.zeros([logSize])  # if the planner is in static mode or not
-        self.planner_q_static = np.zeros([logSize, 19])  # position in static mode (4 stance phase)
-        self.planner_RPY_static = np.zeros([logSize, 3])  # RPY orientation in static mode (4 stance phase)
 
         # State planner
         if statePlanner is not None:
@@ -90,8 +87,11 @@ class LoggerControl():
         self.wbc_D = np.zeros([logSize, 12])  # derivative gains of the PD+
         self.wbc_q_des = np.zeros([logSize, 12])  # desired position of actuators
         self.wbc_v_des = np.zeros([logSize, 12])  # desired velocity of actuators
+        self.wbc_FF = np.zeros([logSize, 12])  # gains for the feedforward torques
         self.wbc_tau_ff = np.zeros([logSize, 12])  # feedforward torques computed by the WBC
+        self.wbc_ddq_IK = np.zeros([logSize, 18])  # joint accelerations computed by the IK
         self.wbc_f_ctc = np.zeros([logSize, 12])  # contact forces computed by the WBC
+        self.wbc_ddq_QP = np.zeros([logSize, 18])  # joint accelerations computed by the QP
         self.wbc_feet_pos = np.zeros([logSize, 3, 4])  # current feet positions according to WBC
         self.wbc_feet_pos_target = np.zeros([logSize, 3, 4])  # current feet positions targets for WBC
         self.wbc_feet_err = np.zeros([logSize, 3, 4])  # error between feet positions and their reference
@@ -135,11 +135,10 @@ class LoggerControl():
         self.esti_LP_filt_x[self.i] = estimator.getFilterPosFiltX()
 
         # Logging from the main loop
-        self.loop_o_q_int[self.i] = loop.q[:, 0]
+        self.loop_o_q[self.i] = loop.q[:, 0]
         self.loop_o_v[self.i] = loop.v[:, 0]
         self.loop_h_v[self.i] = loop.h_v[:, 0]
         self.loop_h_v_windowed[self.i] = loop.h_v_windowed[:, 0]
-        self.loop_pos_virtual_world[self.i] = np.array([loop.q[0, 0], loop.q[1, 0], loop.yaw_estim])
         self.loop_t_filter[self.i] = loop.t_filter
         self.loop_t_planner[self.i] = loop.t_planner
         self.loop_t_mpc[self.i] = loop.t_mpc
@@ -151,8 +150,6 @@ class LoggerControl():
         self.loop_vref_filt_mpc[self.i] = loop.vref_filt_mpc[:, 0]
 
         # Logging from the planner
-        # self.planner_q_static[self.i] = planner.q_static[:]
-        # self.planner_RPY_static[self.i] = planner.RPY_static[:, 0]
         self.planner_xref[self.i] = statePlanner.getReferenceStates()
         self.planner_fsteps[self.i] = footstepPlanner.getFootsteps()
         self.planner_gait[self.i] = gait.getCurrentGait()
@@ -171,8 +168,11 @@ class LoggerControl():
         self.wbc_D[self.i] = loop.result.D
         self.wbc_q_des[self.i] = loop.result.q_des
         self.wbc_v_des[self.i] = loop.result.v_des
+        self.wbc_FF[self.i] = loop.result.FF
         self.wbc_tau_ff[self.i] = loop.result.tau_ff
+        self.wbc_ddq_IK[self.i] = wbc.ddq_cmd
         self.wbc_f_ctc[self.i] = wbc.f_with_delta
+        self.wbc_ddq_QP[self.i] = wbc.ddq_with_delta
         self.wbc_feet_pos[self.i] = wbc.feet_pos
         self.wbc_feet_pos_target[self.i] = wbc.feet_pos_target
         self.wbc_feet_err[self.i] = wbc.feet_err
@@ -245,7 +245,7 @@ class LoggerControl():
         pin.computeAllTerms(solo12.model, solo12.data, q, np.zeros((12, 1)))
         feet_pos = np.zeros([self.esti_q_filt.shape[0], 3, 4])
         for i in range(self.esti_q_filt.shape[0]):
-            q[:, 0] = self.loop_o_q_int[i, 6:]
+            q[:, 0] = self.loop_o_q[i, 6:]
             pin.forwardKinematics(solo12.model, solo12.data, q)
             pin.updateFramePlacements(solo12.model, solo12.data)
             for j, idx in enumerate(foot_ids):
@@ -308,12 +308,9 @@ class LoggerControl():
                 ax0 = plt.subplot(3, 2, index6[i])
             else:
                 plt.subplot(3, 2, index6[i], sharex=ax0)
-            if i in [0, 1]:
-                plt.plot(t_range, self.loop_pos_virtual_world[:, i], "b", linewidth=3)
-                plt.plot(t_range, self.loop_pos_virtual_world[:, i], "r", linewidth=3)
-            elif i == 5:
-                plt.plot(t_range, self.loop_pos_virtual_world[:, 2], "b", linewidth=3)
-                plt.plot(t_range, self.loop_pos_virtual_world[:, 2], "r", linewidth=3)
+            if i in [0, 1, 5]:
+                plt.plot(t_range, self.loop_o_q[:, i], "b", linewidth=3)
+                plt.plot(t_range, self.loop_o_q[:, i], "r", linewidth=3)
             else:
                 plt.plot(t_range, self.planner_xref[:, i, 0], "b", linewidth=2)
                 plt.plot(t_range, self.planner_xref[:, i, 1], "r", linewidth=3)
@@ -377,18 +374,18 @@ class LoggerControl():
         steps = np.zeros((12, 1))
         o_step = np.zeros((3, 1))
         plt.figure()
-        plt.plot(self.loop_o_q_int[:, 0], self.loop_o_q_int[:, 1], linewidth=2, color="k")
+        plt.plot(self.loop_o_q[:, 0], self.loop_o_q[:, 1], linewidth=2, color="k")
         for i in range(self.planner_fsteps.shape[0]):
             fsteps = self.planner_fsteps[i]
-            RPY = utils_mpc.quaternionToRPY(self.loop_o_q_int[i, 3:7])
+            RPY = utils_mpc.quaternionToRPY(self.loop_o_q[i, 3:7])
             quat[:, 0] = utils_mpc.EulerToQuaternion([0.0, 0.0, RPY[2]])
             oRh = pin.Quaternion(quat).toRotationMatrix()
             for j in range(4):
                 #if np.any(fsteps[k, (j*3):((j+1)*3)]) and not np.array_equal(steps[(j*3):((j+1)*3), 0],
                 #                                                                fsteps[k, (j*3):((j+1)*3)]):
                 # steps[(j*3):((j+1)*3), 0] = fsteps[k, (j*3):((j+1)*3)]
-                # o_step[:, 0:1] = oRh @ steps[(j*3):((j+1)*3), 0:1] + self.loop_o_q_int[i:(i+1), 0:3].transpose()
-                o_step[:, 0:1] = oRh @ fsteps[0:1, (j*3):((j+1)*3)].transpose() + self.loop_o_q_int[i:(i+1), 0:3].transpose()
+                # o_step[:, 0:1] = oRh @ steps[(j*3):((j+1)*3), 0:1] + self.loop_o_q[i:(i+1), 0:3].transpose()
+                o_step[:, 0:1] = oRh @ fsteps[0:1, (j*3):((j+1)*3)].transpose() + self.loop_o_q[i:(i+1), 0:3].transpose()
                 plt.plot(o_step[0, 0], o_step[1, 0], linestyle=None, linewidth=1, marker="o", color=f_c[j])
         """
 
@@ -402,9 +399,9 @@ class LoggerControl():
                 plt.subplot(3, 4, index12[i], sharex=ax0)
             tau_fb = self.wbc_P[:, i] * (self.wbc_q_des[:, i] - self.esti_q_filt[:, 7+i]) + \
                 self.wbc_D[:, i] * (self.wbc_v_des[:, i] - self.esti_v_filt[:, 6+i])
-            h1, = plt.plot(t_range, self.wbc_tau_ff[:, i], "r", linewidth=3)
+            h1, = plt.plot(t_range, self.wbc_FF[:, i] * self.wbc_tau_ff[:, i], "r", linewidth=3)
             h2, = plt.plot(t_range, tau_fb, "b", linewidth=3)
-            h3, = plt.plot(t_range, self.wbc_tau_ff[:, i] + tau_fb, "g", linewidth=3)
+            h3, = plt.plot(t_range, self.wbc_FF[:, i] * self.wbc_tau_ff[:, i] + tau_fb, "g", linewidth=3)
             h4, = plt.plot(t_range[:-1], loggerSensors.torquesFromCurrentMeasurment[1:, i],
                            "violet", linewidth=3, linestyle="--")
             plt.xlabel("Time [s]")
@@ -626,7 +623,7 @@ class LoggerControl():
             else:
                 plt.subplot(3, 2, index6[i], sharex=ax0)
 
-            plt.plot(t_range, self.loop_o_q_int[:, i], linewidth=3)
+            plt.plot(t_range, self.loop_o_q[:, i], linewidth=3)
             plt.plot(t_range, self.loop_q_filt_mpc[:, i], linewidth=3)
 
             plt.legend(["Estimated", "Estimated 15Hz filt"], prop={'size': 8})
@@ -687,10 +684,9 @@ class LoggerControl():
                  esti_LP_alpha=self.esti_LP_alpha,
                  esti_LP_filt_x=self.esti_LP_filt_x,
 
-                 loop_o_q_int=self.loop_o_q_int,
+                 loop_o_q=self.loop_o_q,
                  loop_o_v=self.loop_o_v,
                  loop_h_v=self.loop_h_v,
-                 loop_pos_virtual_world=self.loop_pos_virtual_world,
                  loop_t_filter=self.loop_t_filter,
                  loop_t_planner=self.loop_t_planner,
                  loop_t_mpc=self.loop_t_mpc,
@@ -701,8 +697,6 @@ class LoggerControl():
                  loop_h_v_filt_mpc=self.loop_h_v_filt_mpc,
                  loop_vref_filt_mpc=self.loop_vref_filt_mpc,
 
-                 planner_q_static=self.planner_q_static,
-                 planner_RPY_static=self.planner_RPY_static,
                  planner_xref=self.planner_xref,
                  planner_fsteps=self.planner_fsteps,
                  planner_gait=self.planner_gait,
@@ -719,8 +713,11 @@ class LoggerControl():
                  wbc_D=self.wbc_D,
                  wbc_q_des=self.wbc_q_des,
                  wbc_v_des=self.wbc_v_des,
+                 wbc_FF=self.wbc_FF,
                  wbc_tau_ff=self.wbc_tau_ff,
+                 wbc_ddq_IK=self.wbc_ddq_IK,
                  wbc_f_ctc=self.wbc_f_ctc,
+                 wbc_ddq_QP=self.wbc_ddq_QP,
                  wbc_feet_pos=self.wbc_feet_pos,
                  wbc_feet_pos_target=self.wbc_feet_pos_target,
                  wbc_feet_err=self.wbc_feet_err,
@@ -778,10 +775,9 @@ class LoggerControl():
         self.esti_LP_alpha = data["esti_LP_alpha"]
         self.esti_LP_filt_x = data["esti_LP_filt_x"]
 
-        self.loop_o_q_int = data["loop_o_q_int"]
+        self.loop_o_q = data["loop_o_q"]
         self.loop_o_v = data["loop_o_v"]
         self.loop_h_v = data["loop_h_v"]
-        self.loop_pos_virtual_world = data["loop_pos_virtual_world"]
         self.loop_t_filter = data["loop_t_filter"]
         self.loop_t_planner = data["loop_t_planner"]
         self.loop_t_mpc = data["loop_t_mpc"]
@@ -792,8 +788,6 @@ class LoggerControl():
         self.loop_h_v_filt_mpc = data["loop_h_v_filt_mpc"]
         self.loop_vref_filt_mpc = data["loop_vref_filt_mpc"]
 
-        self.planner_q_static = data["planner_q_static"]
-        self.planner_RPY_static = data["planner_RPY_static"]
         self.planner_xref = data["planner_xref"]
         self.planner_fsteps = data["planner_fsteps"]
         self.planner_gait = data["planner_gait"]
@@ -810,8 +804,11 @@ class LoggerControl():
         self.wbc_D = data["wbc_D"]
         self.wbc_q_des = data["wbc_q_des"]
         self.wbc_v_des = data["wbc_v_des"]
+        self.wbc_FF = data["wbc_FF"]
         self.wbc_tau_ff = data["wbc_tau_ff"]
+        self.wbc_ddq_IK = data["wbc_ddq_IK"]
         self.wbc_f_ctc = data["wbc_f_ctc"]
+        self.wbc_ddq_QP = data["wbc_ddq_QP"]
         self.wbc_feet_pos = data["wbc_feet_pos"]
         self.wbc_feet_pos_target = data["wbc_feet_pos_target"]
         self.wbc_feet_err = data["wbc_feet_err"]
@@ -977,11 +974,11 @@ class LoggerControl():
 
         fsteps = self.planner_fsteps[0]
         o_step = np.zeros((3*int(fsteps.shape[0]), 1))
-        RPY = pin.rpy.matrixToRpy(pin.Quaternion(self.loop_o_q_int[0, 3:7]).toRotationMatrix())
+        RPY = pin.rpy.matrixToRpy(pin.Quaternion(self.loop_o_q[0, 3:7]).toRotationMatrix())
         quat[:, 0] = utils_mpc.EulerToQuaternion([0.0, 0.0, RPY[2]])
         oRh = pin.Quaternion(quat).toRotationMatrix()
         for j in range(4):
-            o_step[0:3, 0:1] = oRh @ fsteps[0:1, (j*3):((j+1)*3)].transpose() + self.loop_o_q_int[0:1, 0:3].transpose()
+            o_step[0:3, 0:1] = oRh @ fsteps[0:1, (j*3):((j+1)*3)].transpose() + self.loop_o_q[0:1, 0:3].transpose()
             h1, = plt.plot(o_step[0::3, 0], o_step[1::3, 0], linestyle=None, linewidth=0, marker="o", color=f_c[j])
             h1s.append(h1)
 
@@ -1006,12 +1003,12 @@ class LoggerControl():
             rounded = int(np.round(time_slider.val / self.dt, decimals=0))
             fsteps = self.planner_fsteps[rounded]
             o_step = np.zeros((3*int(fsteps.shape[0]), 1))
-            RPY = pin.rpy.matrixToRpy(pin.Quaternion(self.loop_o_q_int[rounded, 3:7]).toRotationMatrix())
+            RPY = pin.rpy.matrixToRpy(pin.Quaternion(self.loop_o_q[rounded, 3:7]).toRotationMatrix())
             quat[:, 0] = utils_mpc.EulerToQuaternion([0.0, 0.0, RPY[2]])
             oRh = pin.Quaternion(quat).toRotationMatrix()
             for j in range(4):
                 for k in range(int(fsteps.shape[0])):
-                    o_step[(3*k):(3*(k+1)), 0:1] = oRh @ fsteps[(k):(k+1), (j*3):((j+1)*3)].transpose() + self.loop_o_q_int[rounded:(rounded+1), 0:3].transpose()
+                    o_step[(3*k):(3*(k+1)), 0:1] = oRh @ fsteps[(k):(k+1), (j*3):((j+1)*3)].transpose() + self.loop_o_q[rounded:(rounded+1), 0:3].transpose()
                 h1s[j].set_xdata(o_step[0::3, 0].copy())
                 h1s[j].set_ydata(o_step[1::3, 0].copy())
             fig.canvas.draw_idle()
diff --git a/scripts/main_solo12_control.py b/scripts/main_solo12_control.py
index 5b402ad2..b8403418 100644
--- a/scripts/main_solo12_control.py
+++ b/scripts/main_solo12_control.py
@@ -197,7 +197,7 @@ def control_loop(name_interface_clone=None, des_vel_analysis=None):
         device.joints.set_velocity_gains(controller.result.D)
         device.joints.set_desired_positions(controller.result.q_des)
         device.joints.set_desired_velocities(controller.result.v_des)
-        device.joints.set_torques(controller.result.tau_ff.ravel())
+        device.joints.set_torques(controller.result.FF * controller.result.tau_ff.ravel())
 
         # Call logger
         if params.LOGGING or params.PLOTTING:
diff --git a/src/Estimator.cpp b/src/Estimator.cpp
index 1a2623b5..fe943803 100644
--- a/src/Estimator.cpp
+++ b/src/Estimator.cpp
@@ -42,7 +42,6 @@ Vector3 ComplementaryFilter::compute(Vector3 const& x, Vector3 const& dx, Vector
 
 Estimator::Estimator()
     : dt_wbc(0.0),
-      alpha_v_(0.0),
       alpha_secu_(0.0),
       offset_yaw_IMU_(0.0),
       perfect_estimator(false),
@@ -71,6 +70,7 @@ Estimator::Estimator()
       v_filt_dyn_(VectorN::Zero(18, 1)),
       v_secu_dyn_(VectorN::Zero(12, 1)),
       q_up_(VectorN::Zero(18)),
+      v_up_(VectorN::Zero(18)),
       v_ref_(VectorN::Zero(6)),
       h_v_(VectorN::Zero(6)),
       oRh_(Matrix3::Identity()),
@@ -86,11 +86,6 @@ void Estimator::initialize(Params& params) {
   perfect_estimator = params.perfect_estimator;
 
   // Filtering estimated linear velocity
-  double fc = params.fc_v_esti;  // Cut frequency
-  double y = 1 - std::cos(2 * M_PI * fc * dt_wbc);
-  alpha_v_ = -y + std::sqrt(y * y + 2 * y);
-  alpha_v_ = 1.0;
-
   N_queue_ = static_cast<int>(std::round(params.T_gait / dt_wbc));
   vx_queue_.resize(N_queue_, 0.0);  // List full of 0.0
   vy_queue_.resize(N_queue_, 0.0);  // List full of 0.0
@@ -100,8 +95,8 @@ void Estimator::initialize(Params& params) {
   wY_queue_.resize(N_queue_, 0.0);  // List full of 0.0
 
   // Filtering velocities used for security checks
-  fc = 6.0;  // Cut frequency
-  y = 1 - std::cos(2 * M_PI * fc * dt_wbc);
+  double fc = 6.0;  // Cut frequency
+  double y = 1 - std::cos(2 * M_PI * fc * dt_wbc);
   alpha_secu_ = -y + std::sqrt(y * y + 2 * y);
 
   FK_xyz_(2, 0) = params.h_ref;
@@ -260,7 +255,7 @@ Vector3 Estimator::BaseVelocityFromKinAndIMU(int contactFrameId) {
 
 void Estimator::run_filter(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& dummyPos, VectorN const& b_baseVel) {
+                           VectorN const& v_mes, VectorN const& dummyPos, Vector3 const& b_baseVel) {
   feet_status_ = gait.block(0, 0, 1, 4);
   feet_goals_ = goals;
 
@@ -344,20 +339,17 @@ void Estimator::run_filter(MatrixN const& gait, MatrixN const& goals, VectorN co
   q_filt_.tail(12) = actuators_pos_;  // Actuators pos are already directly from PyBullet
 
   // Output filtered velocity vector (18 x 1)
-  if (perfect_estimator) {  // Linear velocities directly from PyBullet
-    v_filt_.head(3) = (1 - alpha_v_) * v_filt_.head(3) + alpha_v_ * b_baseVel;
-  } else {
-    v_filt_.head(3) = (1 - alpha_v_) * v_filt_.head(3) + alpha_v_ * b_filt_lin_vel_;
-  }
+  // Linear velocities directly from PyBullet if perfect estimator
+  v_filt_.head(3) = perfect_estimator ? b_baseVel : b_filt_lin_vel_;
   v_filt_.block(3, 0, 3, 1) = filt_ang_vel;  // Angular velocities are already directly from PyBullet
   v_filt_.tail(12) = actuators_vel_;         // Actuators velocities are already directly from PyBullet
 
   vx_queue_.pop_back();
   vy_queue_.pop_back();
   vz_queue_.pop_back();
-  vx_queue_.push_front(b_filt_lin_vel_(0));
-  vy_queue_.push_front(b_filt_lin_vel_(1));
-  vz_queue_.push_front(b_filt_lin_vel_(2));
+  vx_queue_.push_front(perfect_estimator ? b_baseVel(0) : b_filt_lin_vel_(0));
+  vy_queue_.push_front(perfect_estimator ? b_baseVel(1) : b_filt_lin_vel_(1));
+  vz_queue_.push_front(perfect_estimator ? b_baseVel(2) : b_filt_lin_vel_(2));
   v_filt_bis_(0) = std::accumulate(vx_queue_.begin(), vx_queue_.end(), 0.0) / N_queue_;
   v_filt_bis_(1) = std::accumulate(vy_queue_.begin(), vy_queue_.end(), 0.0) / N_queue_;
   v_filt_bis_(2) = std::accumulate(vz_queue_.begin(), vz_queue_.end(), 0.0) / N_queue_;
@@ -424,12 +416,14 @@ void Estimator::updateState(VectorN const& joystick_v_ref, Gait& gait) {
     Matrix2 Ryaw;
     Ryaw << cos(yaw_estim_), -sin(yaw_estim_), sin(yaw_estim_), cos(yaw_estim_);
 
-    q_up_.head(2) = q_up_.head(2) + Ryaw * v_ref_.head(2) * dt_wbc;
+    v_up_.head(2) = Ryaw * v_ref_.head(2);
+    q_up_.head(2) = q_up_.head(2) + v_up_.head(2) * dt_wbc;
 
     // Mix perfect x and y with height measurement
     q_up_[2] = q_filt_dyn_[2];
 
     // Mix perfect yaw with pitch and roll measurements
+    v_up_[5] = v_ref_[5];
     yaw_estim_ += v_ref_[5] * dt_wbc;
     q_up_.block(3, 0, 3, 1) << IMU_RPY_[0], IMU_RPY_[1], yaw_estim_;
 
@@ -438,12 +432,13 @@ void Estimator::updateState(VectorN const& joystick_v_ref, Gait& gait) {
 
     // Actuators measurements
     q_up_.tail(12) = q_filt_dyn_.tail(12);
+    v_up_.tail(12) = v_filt_dyn_.tail(12);
 
     // Velocities are the one estimated by the estimator
     Matrix3 hRb = pinocchio::rpy::rpyToMatrix(IMU_RPY_[0], IMU_RPY_[1], 0.0);
 
-    h_v_.head(3) = hRb * v_filt_dyn_.block(0, 0, 3, 1);
-    h_v_.tail(3) = hRb * v_filt_dyn_.block(3, 0, 3, 1);
+    h_v_.head(3) = hRb * v_filt_.block(0, 0, 3, 1);
+    h_v_.tail(3) = hRb * v_filt_.block(3, 0, 3, 1);
     h_v_windowed_.head(3) = hRb * v_filt_bis_.block(0, 0, 3, 1);
     h_v_windowed_.tail(3) = hRb * v_filt_bis_.block(3, 0, 3, 1);
   } else {
-- 
GitLab