From e9a1c011dbc5d310b8617335c67c3653a2925dfa Mon Sep 17 00:00:00 2001
From: Ale <alessandroassirell98@gmail.com>
Date: Thu, 7 Jul 2022 15:08:55 +0200
Subject: [PATCH] addeded files to repo

---
 config/walk_parameters.yaml                   |  4 +-
 python/CMakeLists.txt                         |  1 +
 .../quadruped_reactive_walking/Controller.py  | 44 +++++++++++++++----
 .../WB_MPC/CrocoddylOCP.py                    | 34 +++++++++-----
 .../WB_MPC/OcpResult.py                       | 10 +++++
 .../WB_MPC/ProblemData.py                     |  2 +-
 .../WB_MPC_Wrapper.py                         | 32 ++++++++------
 .../main_solo12_control.py                    |  8 +++-
 8 files changed, 96 insertions(+), 39 deletions(-)
 create mode 100644 python/quadruped_reactive_walking/WB_MPC/OcpResult.py

diff --git a/config/walk_parameters.yaml b/config/walk_parameters.yaml
index 1aba9ddb..df3b5ffb 100644
--- a/config/walk_parameters.yaml
+++ b/config/walk_parameters.yaml
@@ -11,7 +11,7 @@ robot:
     envID: 0  # Identifier of the environment to choose in which one the simulation will happen
     use_flat_plane: true  # If True the ground is flat, otherwise it has bumps
     predefined_vel: true  # If we are using a predefined reference velocity (True) or a joystick (False)
-    N_SIMULATION: 180000  # Number of simulated wbc time steps
+    N_SIMULATION: 2000  # Number of simulated wbc time steps
     enable_corba_viewer: false  # Enable/disable Corba Viewer
     enable_multiprocessing: false  # Enable/disable running the MPC in another process in parallel of the main loop
     perfect_estimator: true  # Enable/disable perfect estimator by using data directly from PyBullet
@@ -25,7 +25,7 @@ robot:
     dt_mpc: 0.02  # Time step of the model predictive control
     type_MPC: 3  # Which MPC solver you want to use: 0 for OSQP MPC, 1, 2, 3 for Crocoddyl MPCs
 #     Kp_main: [0.0, 0.0, 0.0]  # Proportional gains for the PD+
-    Kp_main: [3.0, 3.0, 3.0]  # Proportional gains for the PD+
+    Kp_main: [100.0, 100.0, 100.0]  # Proportional gains for the PD+
 #     Kd_main: [0., 0., 0.]  # Derivative gains for the PD+
     Kd_main: [0.3, 0.3, 0.3]  # Derivative gains for the PD+
 #     Kff_main: 0.0  # Feedforward torques multiplier for the PD+
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 2674ab28..092f1d00 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -31,6 +31,7 @@ set(${PY_NAME}_WB_MPC_PYTHON
   ShootingNode.py
   ProblemData.py
   Target.py
+  OcpResult.py
   )
 
 set(${PY_NAME}_TOOLS_PYTHON
diff --git a/python/quadruped_reactive_walking/Controller.py b/python/quadruped_reactive_walking/Controller.py
index 820d5970..b9966a78 100644
--- a/python/quadruped_reactive_walking/Controller.py
+++ b/python/quadruped_reactive_walking/Controller.py
@@ -30,6 +30,8 @@ class DummyDevice:
         self.base_position = np.zeros(3)
         self.base_position[2] = 0.1944
         self.b_base_velocity = np.zeros(3)
+        self.baseState = tuple()
+        self.baseVel = tuple()
 
     class IMU:
         def __init__(self):
@@ -57,6 +59,8 @@ class Controller:
         self.q_security = np.array([1.2, 2.1, 3.14] * 4)
 
         self.mpc = WB_MPC_Wrapper.MPC_Wrapper(pd, target, params)
+        self.pd = pd
+        self.target = target
 
         self.k = 0
         self.error = False
@@ -66,8 +70,8 @@ class Controller:
         
         device = DummyDevice()
         device.joints.positions = q_init
-        self.compute(device)
-
+        self.guess = {}
+        #self.compute(device)
 
 
     def compute(self, device, qc=None):
@@ -78,8 +82,10 @@ class Controller:
         """
         t_start = time.time()
 
+        m = self.read_state(device)
+
         try:
-            self.mpc.solve(self.k, None)
+            self.mpc.solve(self.k, m['x_m'], self.guess)
         except ValueError:
             self.error = True
             print("MPC Problem")
@@ -89,11 +95,15 @@ class Controller:
 
             self.result.P = np.array(self.params.Kp_main.tolist() * 4)
             self.result.D = np.array(self.params.Kd_main.tolist() * 4)
-            self.result.FF = self.params.Kff_main * 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.FF = self.params.Kff_main * np.ones(12)
+            self.result.q_des = self.mpc_result.q[1]
+            self.result.v_des = self.mpc_result.v[1]
             self.result.tau_ff = np.zeros(12)
 
+            self.guess["xs"] = self.mpc_result.x
+            self.guess["us"] = self.mpc_result.u
+
         self.t_wbc = time.time() - t_start
 
         self.clamp_result(device)
@@ -114,7 +124,7 @@ class Controller:
         Update position of PyBullet camera on the robot position to do as if it was
         attached to the robot
         """
-        if self.k > 10 and self.enable_pyb_GUI:
+        if self.k > 10 and self.params.enable_pyb_GUI:
             pyb.resetDebugVisualizerCamera(
                 cameraDistance=0.6,
                 cameraYaw=45,
@@ -148,10 +158,10 @@ class Controller:
 
     def clamp(self, num, min_value=None, max_value=None):
         clamped = False
-        if min_value is not None and num <= min_value:
+        if min_value is not None and num.any() <= min_value:
             num = min_value
             clamped = True
-        if max_value is not None and num >= max_value:
+        if max_value is not None and num.any() >= max_value:
             num = max_value
             clamped = True
         return clamped
@@ -208,3 +218,19 @@ class Controller:
         self.result.q_des[:] = np.zeros(12)
         self.result.v_des[:] = np.zeros(12)
         self.result.tau_ff[:] = np.zeros(12)
+
+    def read_state(self, device):
+        qj_m = device.joints.positions
+        vj_m = device.joints.velocities
+        bp_m = self.tuple_to_array(device.baseState)
+        bv_m = self.tuple_to_array(device.baseVel)
+        if self.pd.useFixedBase == 0:
+            x_m = np.concatenate([bp_m, qj_m, bv_m, vj_m])
+        else:
+            x_m = np.concatenate([qj_m[3:6], vj_m[3:6]])
+
+        return {'qj_m': qj_m, 'vj_m': vj_m, 'x_m': x_m}
+
+    def tuple_to_array(self, tup):
+        a = np.array([element for tupl in tup for element in tupl])
+        return a
diff --git a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py
index b6c2af65..b4d98589 100644
--- a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py
+++ b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py
@@ -1,6 +1,7 @@
 from tracemalloc import start
 from .ProblemData import ProblemData
 from .Target import Target
+from .OcpResult import OcpResult
 import crocoddyl
 import pinocchio as pin
 import numpy as np
@@ -11,6 +12,7 @@ class OCP:
         self.pd = pd
         self.target = target
         self.state = crocoddyl.StateMultibody(self.pd.model)
+        self.results = OcpResult()
         if pd.useFixedBase == 0:
             self.actuation = crocoddyl.ActuationModelFloatingBase(self.state)
         else:
@@ -143,9 +145,28 @@ class OCP:
             us = guess['us']
             print("Using warmstart")
         start_time = time()
-        self.ddp.solve(xs, us, 1, False)
+        self.ddp.solve(xs, us, 30, False)
         print("Solver time: ", time()- start_time, "\n")
 
+
+    def get_results(self):
+        self.results.x = self.ddp.xs.tolist()
+        self.results.a = self.get_croco_acc()
+        self.results.u = self.ddp.us.tolist()
+        self.results.K = self.ddp.K
+
+        if self.pd.useFixedBase == 0:
+            self.results.q = np.array(self.results.x)[:, 7: self.pd.nq]
+            self.results.v = np.array(self.results.x)[:, self.pd.nq + 6: ]
+        else:
+            self.results.q = self.pd.q0
+            self.results.v = self.pd.q0
+            self.results.q[3:6] = np.array(self.results.x)[:, : self.pd.nq]
+            self.results.v[3:6] = np.array(self.results.x)[:, self.pd.nq :]
+
+
+        return self.results
+
     def get_croco_forces(self):
         d = self.ddp.problem.runningDatas[0]
         cnames = d.differential.multibody.contacts.contacts.todict().keys()
@@ -179,13 +200,4 @@ class OCP:
          for m in self.ddp.problem.runningDatas]
         return acc
 
-    def get_results(self):
-        x = self.ddp.xs.tolist()
-        a = self.get_croco_acc()
-        u = self.ddp.us.tolist()
-        if self.pd.useFixedBase == 0:
-            f_ws = self.get_croco_forces_ws()
-        else:
-            f_ws = []
-
-        return None, x, a, u, f_ws, None
+    
\ No newline at end of file
diff --git a/python/quadruped_reactive_walking/WB_MPC/OcpResult.py b/python/quadruped_reactive_walking/WB_MPC/OcpResult.py
new file mode 100644
index 00000000..e5bb3895
--- /dev/null
+++ b/python/quadruped_reactive_walking/WB_MPC/OcpResult.py
@@ -0,0 +1,10 @@
+class OcpResult:
+    def __init__(self):
+        self.x = None
+        self.a = None
+        self.u = None
+        self.fs = None
+        self.K = None
+        self.f_world = None
+        self.q = None
+        self.v = None
\ No newline at end of file
diff --git a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
index 56c59442..1e47aab4 100644
--- a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
+++ b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py
@@ -87,7 +87,7 @@ class ProblemData(problemDataAbstract):
         self.terminal_velocity_w = np.array([0] * 18 + [1e3] * 18 )
         self.control_bound_w = 1e3
 
-        self.x0 = np.array([ 0, 0, 0.23289725, 0, 0, 0, 1, 0.1, 0.8, -1.6,
+        self.x0 = np.array([ 0.0, 0.0, 0.2607495, 0, 0, 0, 1, 0.1, 0.8, -1.6,
                             -0.1,  0.8, -1.6,  0.1, -0.8, 1.6, -0.1, -0.8, 1.6,
                             0, 0, 0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) # x0 got from PyBullet
                             
diff --git a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py
index c0cb92b9..fc2c766d 100644
--- a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py
+++ b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py
@@ -20,7 +20,8 @@ class DataInCtype(Structure):
     # TODO add the data exchanged with the OCP
     _fields_ = [
         ("k", ctypes.c_int64),
-        ("blop", ctypes.c_double * 12)
+        ("x0", ctypes.c_double * 6),
+        ("guess", ctypes.c_double * 12),
     ]
 
 
@@ -33,6 +34,8 @@ class MPC_Wrapper:
     def __init__(self, pd, target, params):
         self.initialized = False
         self.params = params
+        self.pd = pd
+        self.target = target
 
         n_nodes = 0
 
@@ -53,7 +56,7 @@ class MPC_Wrapper:
         self.last_available_result = np.zeros((24, n_nodes))
         self.last_cost = 0.0
 
-    def solve(self, k, inputs):
+    def solve(self, k, x0, guess=None):
         """
         Call either the asynchronous MPC or the synchronous MPC depending on the value
         of multiprocessing during the creation of the wrapper
@@ -61,10 +64,11 @@ class MPC_Wrapper:
         Args:
             k (int): Number of inv dynamics iterations since the start of the simulation
         """
+
         if self.multiprocessing:
-            self.run_MPC_asynchronous(k, inputs)
+            self.run_MPC_asynchronous(k, x0, guess)
         else:
-            self.run_MPC_synchronous(inputs)
+            self.run_MPC_synchronous(x0, guess)
 
     def get_latest_result(self):
         """
@@ -82,15 +86,15 @@ class MPC_Wrapper:
             self.initialized = True
         return self.last_available_result, self.last_cost
 
-    def run_MPC_synchronous(self, inputs):
+    def run_MPC_synchronous(self, x0, guess):
         """
         Run the MPC (synchronous version)
         """
-        self.ocp.solve(inputs)
-        self.last_available_result = self.ocp.get_latest_result()
-        self.last_cost = self.ocp.retrieve_cost()
+        self.ocp.solve(x0, guess)
+        self.last_available_result = self.ocp.get_results()
+        #self.last_cost = self.ocp.retrieve_cost()
 
-    def run_MPC_asynchronous(self, k, inputs):
+    def run_MPC_asynchronous(self, k, x0, guess):
         """
         Run the MPC (asynchronous version)
         """
@@ -98,7 +102,7 @@ class MPC_Wrapper:
             p = Process(target=self.MPC_asynchronous)
             p.start()
 
-        self.add_new_data(k, inputs)
+        self.add_new_data(k, x0, guess)
 
     def MPC_asynchronous(self):
         """
@@ -108,17 +112,17 @@ class MPC_Wrapper:
             if self.newData.value:
                 self.newData.value = False
 
-                k, inputs = self.decompress_dataIn(self.dataIn)
+                k, x0, guess = self.decompress_dataIn(self.dataIn)
 
                 if k == 0:
-                    loop_ocp = OCP(self.params)
+                    loop_ocp = OCP(self.pd, self.target)
 
-                loop_ocp.solve(inputs)
+                loop_ocp.solve(x0, guess)
                 self.dataOut[:] = loop_ocp.get_latest_result().ravel(order="F")
                 self.cost.value = loop_ocp.retrieve_cost()
                 self.newResult.value = True
 
-    def add_new_data(self, k, inputs):
+    def add_new_data(self, k, x0):
         """
         Compress data in a C-type structure that belongs to the shared memory to send 
         data from the main control loop to the asynchronous MPC and notify the process
diff --git a/python/quadruped_reactive_walking/main_solo12_control.py b/python/quadruped_reactive_walking/main_solo12_control.py
index ec137201..23b6accb 100644
--- a/python/quadruped_reactive_walking/main_solo12_control.py
+++ b/python/quadruped_reactive_walking/main_solo12_control.py
@@ -6,12 +6,13 @@ import numpy as np
 import quadruped_reactive_walking as qrw
 from .Controller import Controller
 from .tools.LoggerControl import LoggerControl
-from .WB_MPC.ProblemData import ProblemData
+from .WB_MPC.ProblemData import ProblemData, ProblemDataFull
 from .WB_MPC.Target import Target
 
 params = qrw.Params()  # Object that holds all controller parameters
-pd = ProblemData(params)
+pd = ProblemDataFull(params)
 target = Target(pd)
+target.update(0)
 
 if params.SIMULATION:
     from .tools.PyBulletSimulator import PyBulletSimulator
@@ -165,10 +166,12 @@ def control_loop():
     k_log_whole = 0
     T_whole = time.time()
     dT_whole = 0.0
+    cnt = 0
     while (not device.is_timeout) and (t < t_max) and (not controller.error):
         t_start_whole = time.time()
 
         device.parse_sensor_data()
+        target.update(cnt)
         if controller.compute(device, qc):
             break
 
@@ -198,6 +201,7 @@ def control_loop():
 
         t_log_whole[k_log_whole] = t_end_whole - t_start_whole
         k_log_whole += 1
+        cnt += 1
 
     # ****************************************************************
     finished = t >= t_max
-- 
GitLab