From e2b0ba3e26fdc65df669c9fb63fbdafcedcfdca8 Mon Sep 17 00:00:00 2001
From: paleziart <paleziart@laas.fr>
Date: Thu, 25 Aug 2022 16:58:03 +0200
Subject: [PATCH] Bindings for C++ MpcWrapper + Use this wrapper when using
 OSQP MPC

---
 include/qrw/MpcWrapper.hpp                    | 18 ++++++++++---
 python/CMakeLists.txt                         |  1 +
 python/MpcWrapper.cpp                         | 13 +++++----
 .../quadruped_reactive_walking/Controller.py  | 13 +++------
 src/MpcWrapper.cpp                            | 27 +++++++++++++------
 5 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/include/qrw/MpcWrapper.hpp b/include/qrw/MpcWrapper.hpp
index bdf630e2..aa97e770 100644
--- a/include/qrw/MpcWrapper.hpp
+++ b/include/qrw/MpcWrapper.hpp
@@ -131,15 +131,25 @@ class MpcWrapper {
   /// \brief Return the latest available result of the MPC
   ///
   ////////////////////////////////////////////////////////////////////////////////////////////////
-  Eigen::Matrix<double, 24, 2> get_latest_result();
+  MatrixN get_latest_result();
+
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  ///
+  /// \brief Return the solving duration of the latest MPC iteration
+  ///
+  ////////////////////////////////////////////////////////////////////////////////////////////////
+  double get_t_mpc_solving_duration() { return t_mpc_solving_duration_; }
 
  private:
   Params* params_;  // Object that stores parameters
   MPC mpc_;         // MPC object used for synchronous solving (not in parallel loop)
 
-  Eigen::Matrix<double, 24, 2> last_available_result;  // Latest available result of the MPC
-  RowVector4 gait_past;                                  // Gait status of the previous MPC time step
-  RowVector4 gait_next;                                  // Gait status of the next MPC time step
+  MatrixN last_available_result;  // Latest available result of the MPC
+  RowVector4 gait_past;           // Gait status of the previous MPC time step
+  RowVector4 gait_next;           // Gait status of the next MPC time step
+
+  std::chrono::time_point<std::chrono::system_clock> t_mpc_solving_start_;
+  double t_mpc_solving_duration_;
 };
 
 #endif  // MPCWRAPPER_H_INCLUDED
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 35bb50e9..ee9fb478 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -3,6 +3,7 @@ set(${PY_NAME}_SOURCES
   Estimator.cpp
   Filter.cpp
   MPC.cpp
+  MpcWrapper.cpp
   FootstepPlanner.cpp
   FootTrajectoryGenerator.cpp
   Gait.cpp
diff --git a/python/MpcWrapper.cpp b/python/MpcWrapper.cpp
index 0a567ae9..8bb6a45f 100644
--- a/python/MpcWrapper.cpp
+++ b/python/MpcWrapper.cpp
@@ -5,12 +5,15 @@
 template <typename MpcWrapper>
 struct MpcWrapperVisitor : public bp::def_visitor<MpcWrapperVisitor<MpcWrapper>> {
   template <class PyClassMpcWrapper>
-  void visit(PyClassMpcWrapper& mpc) const {
-    mpc.def(bp::init<>(bp::arg(""), "Default constructor."))
-        .def(bp::init<Params&>(bp::args("params"), "Constructor with parameters."))
-        .def("solve", &MpcWrapper::solve, bp::args("k", "xref_in", "fsteps_in", "gait_in"), "Run MpcWrapper from Python.\n")
+  void visit(PyClassMpcWrapper& cl) const {
+    cl.def(bp::init<>(bp::arg(""), "Default constructor."))
+        .def("initialize", &MpcWrapper::initialize, bp::args("params"), "Initialize Gait from Python.\n")
+        .def("solve", &MpcWrapper::solve, bp::args("k", "xref_in", "fsteps_in", "gait_in"),
+             "Run MpcWrapper from Python.\n")
         .def("get_latest_result", &MpcWrapper::get_latest_result,
-             "Get latest result (predicted trajectory  forces to apply).\n");
+             "Get latest result (predicted trajectory  forces to apply).\n")
+        .def_readonly("t_mpc_solving_duration", &MpcWrapper::get_t_mpc_solving_duration,
+             "Solving time of latest MPC iteration.\n");
   }
 
   static void expose() { bp::class_<MpcWrapper>("MpcWrapper", bp::no_init).def(MpcWrapperVisitor<MpcWrapper>()); }
diff --git a/python/quadruped_reactive_walking/Controller.py b/python/quadruped_reactive_walking/Controller.py
index bdcbfcdd..6bcb8044 100644
--- a/python/quadruped_reactive_walking/Controller.py
+++ b/python/quadruped_reactive_walking/Controller.py
@@ -86,8 +86,7 @@ class Controller:
         self.q_display = np.zeros(19)
 
         if self.type_MPC == 0:
-            print("Create wrapper")
-            self.mpc_wrapper = qrw.mpcWrapper()
+            self.mpc_wrapper = qrw.MpcWrapper()
             self.mpc_wrapper.initialize(params)
         else:
             self.mpc_wrapper = MPC_Wrapper.MPC_Wrapper(params, self.q_init)
@@ -560,7 +559,6 @@ class Controller:
                         - self.footTrajectoryGenerator.get_elapsed_durations(),
                     )
                 elif self.type_MPC == 0:
-                    print("Call Wrapper")
                     self.mpc_wrapper.solve(
                         self.k,
                         reference_state,
@@ -577,15 +575,12 @@ class Controller:
             except ValueError:
                 print("MPC Problem")
 
-        # Use a temporary result if contact status changes between two calls of the MPC
-        self.mpc_wrapper.get_temporary_result(self.gait.matrix[0, :])
-
         if self.type_MPC == 0:
-            print("get latest result")
             self.mpc_result = self.mpc_wrapper.get_latest_result()
-            from IPython import embed
-            embed()
+            self.mpc_cost = 0.0
         else:
+            # Use a temporary result if contact status changes between two calls of the MPC
+            self.mpc_wrapper.get_temporary_result(self.gait.matrix[0, :])
             self.mpc_result, self.mpc_cost = self.mpc_wrapper.get_latest_result()
 
         if self.k > 100 and self.type_MPC == 3:
diff --git a/src/MpcWrapper.cpp b/src/MpcWrapper.cpp
index 1aa24e55..26d5256d 100644
--- a/src/MpcWrapper.cpp
+++ b/src/MpcWrapper.cpp
@@ -18,6 +18,8 @@ std::mutex mutexStop;  // To check if the thread should still run
 std::mutex mutexIn;    // From main loop to MPC
 std::mutex mutexOut;   // From MPC to main loop
 
+std::thread parallel_thread(parallel_loop);  // spawn new thread that runs MPC in parallel
+
 void stop_thread() {
   const std::lock_guard<std::mutex> lockStop(mutexStop);
   shared_running = false;
@@ -69,6 +71,9 @@ MatrixN read_out() {
 }
 
 void parallel_loop() {
+  while (shared_params == nullptr) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(1));  // Wait a bit
+  }
   int k;
   MatrixN xref;
   MatrixN fsteps;
@@ -87,8 +92,7 @@ void parallel_loop() {
       loop_mpc.run(k, xref, fsteps);
 
       // Store the result (predicted state + desired forces) in the shared memory
-      // MPC::get_latest_result() returns a matrix of size 24 x N and we want to
-      // retrieve only the 2 first columns i.e. dataOut.block(0, 0, 24, 2)
+      // MPC::get_latest_result() returns a matrix of size 24 x N
       // std::cout << "NEW RESULT AVAILABLE, WRITING OUT" << std::endl;
       result = loop_mpc.get_latest_result();
       write_out(result);
@@ -99,26 +103,32 @@ void parallel_loop() {
 }
 
 MpcWrapper::MpcWrapper()
-    : last_available_result(Eigen::Matrix<double, 24, 2>::Zero()),
-      gait_past(RowVector4::Zero()),
-      gait_next(RowVector4::Zero()) {}
+    : gait_past(RowVector4::Zero()),
+      gait_next(RowVector4::Zero()),
+      t_mpc_solving_duration_(0.0) {}
 
 void MpcWrapper::initialize(Params& params) {
   params_ = &params;
   mpc_ = MPC(params);
 
+  t_mpc_solving_start_ = std::chrono::system_clock::now();
+
   // Default result for first step
+  last_available_result = MatrixN::Zero(24, params.gait.rows());
   last_available_result(2, 0) = params.h_ref;
   last_available_result.col(0).tail(12) = (Vector3(0.0, 0.0, 8.0)).replicate<4, 1>();
 
   // Initialize the shared memory
-  shared_params = &params;
   shared_k = 42;
   shared_xref = MatrixN::Zero(12, params.gait.rows() + 1);
   shared_fsteps = MatrixN::Zero(params.gait.rows(), 12);
+  shared_params = &params;
 }
 
 void MpcWrapper::solve(int k, MatrixN xref, MatrixN fsteps, MatrixN gait) {
+
+  t_mpc_solving_start_ = std::chrono::system_clock::now();
+
   // std::cout << "NEW DATA AVAILABLE, WRITING IN" << std::endl;
   write_in(k, xref, fsteps);
 
@@ -142,10 +152,11 @@ void MpcWrapper::solve(int k, MatrixN xref, MatrixN fsteps, MatrixN gait) {
   gait_next = gait.row(1);
 }
 
-Eigen::Matrix<double, 24, 2> MpcWrapper::get_latest_result() {
+MatrixN MpcWrapper::get_latest_result() {
   // Retrieve data from parallel process if a new result is available
   if (check_new_result()) {
-    last_available_result = read_out().block(0, 0, 24, 2);
+    t_mpc_solving_duration_ = ((std::chrono::duration<double>)(std::chrono::system_clock::now() - t_mpc_solving_start_)).count();
+    last_available_result = read_out();
   }
   // std::cout << "get_latest_result: " << std::endl << last_available_result.transpose() << std::endl;
   return last_available_result;
-- 
GitLab