diff --git a/scripts/Controller.py b/scripts/Controller.py
index ff75422c8772111c875de3f4e400b65d229d7b96..c4029b4970a10dcab4052c8ece956fe49ac75c01 100644
--- a/scripts/Controller.py
+++ b/scripts/Controller.py
@@ -133,6 +133,7 @@ class Controller:
 
         # Wrapper that makes the link with the solver that you want to use for the MPC
         self.mpc_wrapper = MPC_Wrapper.MPC_Wrapper(params, self.q)
+        self.o_targetFootstep = np.zeros((3,4)) # Store result for MPC_planner
 
         # ForceMonitor to display contact forces in PyBullet with red lines
         # import ForceMonitor
@@ -270,8 +271,8 @@ class Controller:
             try:
                 if self.type_MPC == 3 :
                     # Compute the target foostep in local frame, to stop the optimisation around it when t_lock overpass
-                    l_targetFootstep = self.footstepPlanner.getRz().transpose() @ self.footTrajectoryGenerator.getFootPosition() - self.q[0:3,0:1]
-                    self.mpc_wrapper.solve(self.k, xref, fsteps, cgait, l_targetFootstep)
+                    l_targetFootstep = oRh.transpose() @ (self.o_targetFootstep - oTh)
+                    self.mpc_wrapper.solve(self.k, xref, fsteps, cgait, l_targetFootstep, oRh, oTh)
                 else :
                     self.mpc_wrapper.solve(self.k, xref, fsteps, cgait, np.zeros((3,4)))
 
@@ -281,15 +282,19 @@ class Controller:
         # Retrieve reference contact forces in horizontal frame
         self.x_f_mpc = self.mpc_wrapper.get_latest_result()
 
+        # Store o_targetFootstep, used with MPC_planner
+        self.o_targetFootstep = o_targetFootstep.copy()
+
         t_mpc = time.time()
 
         # If the MPC optimizes footsteps positions then we use them
         if self.k > 100 and self.type_MPC == 3 :
-            for foot in range(4):
-                id = 0
-                while cgait[id,foot] == 0 :
-                    id += 1
-                o_targetFootstep[:2,foot] = np.array(self.footstepPlanner.getRz()[:2, :2]) @ self.x_f_mpc[24 +  2*foot:24+2*foot+2, id] + np.array([self.q[0, 0] , self.q[1,0] ])
+            for foot in range(4):                  
+                if cgait[0,foot] == 0 :
+                    id = 0
+                    while cgait[id,foot] == 0 :
+                        id += 1
+                    self.o_targetFootstep[:2,foot] = self.x_f_mpc[24 +  2*foot:24+2*foot+2, id]
 
         # Update pos, vel and acc references for feet
         self.footTrajectoryGenerator.update(self.k, o_targetFootstep)
diff --git a/scripts/MPC_Wrapper.py b/scripts/MPC_Wrapper.py
index beb7db3920a116c4cf697a55c9ada2467d5e6c05..3061c7c79cd91f8961b4351d1a80ce6710a713c7 100644
--- a/scripts/MPC_Wrapper.py
+++ b/scripts/MPC_Wrapper.py
@@ -7,6 +7,14 @@ import crocoddyl_class.MPC_crocoddyl as MPC_crocoddyl
 import crocoddyl_class.MPC_crocoddyl_planner as MPC_crocoddyl_planner
 import pinocchio as pin
 from time import time
+from enum import Enum
+
+class MPC_type(Enum):
+    OSQP = 0
+    CROCODDYL_LINEAR = 1
+    CROCODDYL_NON_LINEAR = 2
+    CROCODDYL_PLANNER = 3
+    CROCODDYL_PLANNER_TIME = 4
 
 class Dummy:
     """Dummy class to store variables"""
@@ -54,13 +62,13 @@ class MPC_Wrapper:
         self.gait_next = np.zeros(4)
         self.mass = params.mass
 
-        self.mpc_type = params.type_MPC
+        self.mpc_type = MPC_type(params.type_MPC)
         self.multiprocessing = params.enable_multiprocessing
         if self.multiprocessing:  # Setup variables in the shared memory
             self.newData = Value('b', False)
             self.newResult = Value('b', False)            
-            if self.mpc_type == 3:  # Need more space to store optimized footsteps and l_fsteps to stop the optimization around it
-                self.dataIn = Array('d', [0.0] * (1 + (np.int(self.n_steps)+1) * 12 + 12*self.N_gait + 12) )
+            if self.mpc_type == MPC_type.CROCODDYL_PLANNER:  # Need more space to store optimized footsteps and l_fsteps to stop the optimization around it (+ oTh and oRh)
+                self.dataIn = Array('d', [0.0] * (1 + (np.int(self.n_steps)+1) * 12 + 12*self.N_gait + 12 + 9 + 3) ) 
                 self.dataOut = Array('d', [0] * 32 * (np.int(self.n_steps)))
             else:
                 self.dataIn = Array('d', [0.0] * (1 + (np.int(self.n_steps)+1) * 12 + 12*self.N_gait))
@@ -69,25 +77,29 @@ class MPC_Wrapper:
             self.running = Value('b', True)
         else:
             # Create the new version of the MPC solver object
-            if self.mpc_type == 0:  # OSQP MPC
+            if self.mpc_type == MPC_type.OSQP:  # OSQP MPC
                 self.mpc = MPC.MPC(params)  # self.dt, self.n_steps, self.T_gait, self.N_gait)
-            elif self.mpc_type == 1:  # Crocoddyl MPC Linear
+            elif self.mpc_type == MPC_type.CROCODDYL_LINEAR:  # Crocoddyl MPC Linear
                 self.mpc = MPC_crocoddyl.MPC_crocoddyl(params, mu=0.9, inner=False, linearModel=True)
-            elif self.mpc_type == 2:  # Crocoddyl MPC Non-Linear
+            elif self.mpc_type == MPC_type.CROCODDYL_NON_LINEAR:  # Crocoddyl MPC Non-Linear
                 self.mpc = MPC_crocoddyl.MPC_crocoddyl(params, mu=0.9, inner=False, linearModel=False)
-            else:  # Crocoddyl MPC Non-Linear with footsteps optimization
+            elif self.mpc_type == MPC_type.CROCODDYL_PLANNER:  # Crocoddyl MPC Non-Linear with footsteps optimization
                 self.mpc = MPC_crocoddyl_planner.MPC_crocoddyl_planner(params, mu=0.9, inner=False)
+            else:
+                print("Unknown MPC type, using crocoddyl linear")
+                self.type = MPC_type.CROCODDYL_LINEAR
+                self.mpc = MPC_crocoddyl.MPC_crocoddyl(params, mu=0.9, inner=False, linearModel=True)
 
         # Setup initial result for the first iteration of the main control loop
         x_init = np.zeros(12)
         x_init[0:6] = q_init[0:6, 0].copy()
-        if self.mpc_type == 3:  # Need more space to store optimized footsteps
+        if self.mpc_type == MPC_type.CROCODDYL_PLANNER:  # Need more space to store optimized footsteps
             self.last_available_result = np.zeros((32, (np.int(self.n_steps))))
         else:
             self.last_available_result = np.zeros((24, (np.int(self.n_steps))))
         self.last_available_result[:24, 0] = np.hstack((x_init, np.array([0.0, 0.0, 8.0] * 4)))
 
-    def solve(self, k, xref, fsteps, gait, l_targetFootstep):
+    def solve(self, k, xref, fsteps, gait, l_targetFootstep, oRh = np.eye(3), oTh = np.zeros((3,1)) ):
         """Call either the asynchronous MPC or the synchronous MPC depending on the value of multiprocessing during
         the creation of the wrapper
 
@@ -102,9 +114,9 @@ class MPC_Wrapper:
         self.t_mpc_solving_start = time()
 
         if self.multiprocessing:  # Run in parallel process
-            self.run_MPC_asynchronous(k, xref, fsteps, l_targetFootstep)
+            self.run_MPC_asynchronous(k, xref, fsteps, l_targetFootstep, oRh, oTh)
         else:  # Run in the same process than main loop
-            self.run_MPC_synchronous(k, xref, fsteps, l_targetFootstep)
+            self.run_MPC_synchronous(k, xref, fsteps, l_targetFootstep, oRh, oTh)
 
         if not np.allclose(gait[0, :], self.gait_past):  # If gait status has changed
             if np.allclose(gait[0, :], self.gait_next):  # If we're still doing what was planned the last time MPC was solved
@@ -145,7 +157,7 @@ class MPC_Wrapper:
             self.not_first_iter = True
             return self.last_available_result
 
-    def run_MPC_synchronous(self, k, xref, fsteps, l_targetFootstep):
+    def run_MPC_synchronous(self, k, xref, fsteps, l_targetFootstep, oRh, oTh):
         """Run the MPC (synchronous version) to get the desired contact forces for the feet currently in stance phase
 
         Args:
@@ -158,10 +170,10 @@ class MPC_Wrapper:
         # Run the MPC to get the reference forces and the next predicted state
         # Result is stored in mpc.f_applied, mpc.q_next, mpc.v_next
 
-        if self.mpc_type == 0:
+        if self.mpc_type == MPC_type.OSQP:
             # OSQP MPC
             self.mpc.run(np.int(k), xref.copy(), fsteps.copy())
-        elif self.mpc_type == 3: # Add goal position to stop the optimisation
+        elif self.mpc_type == MPC_type.CROCODDYL_PLANNER: # Add goal position to stop the optimisation
             # Crocoddyl MPC
             self.mpc.solve(k, xref.copy(), fsteps.copy(), l_targetFootstep)
         else:
@@ -169,9 +181,12 @@ class MPC_Wrapper:
             self.mpc.solve(k, xref.copy(), fsteps.copy())
 
         # Output of the MPC
-        self.f_applied = self.mpc.get_latest_result()
+        if self.mpc_type == MPC_type.CROCODDYL_PLANNER:
+            self.f_applied = self.mpc.get_latest_result(oRh,oTh )
+        else:
+            self.f_applied = self.mpc.get_latest_result()
 
-    def run_MPC_asynchronous(self, k, xref, fsteps, l_targetFootstep):
+    def run_MPC_asynchronous(self, k, xref, fsteps, l_targetFootstep, oRh, oTh):
         """Run the MPC (asynchronous version) to get the desired contact forces for the feet currently in stance phase
 
         Args:
@@ -189,7 +204,7 @@ class MPC_Wrapper:
             p.start()
 
         # Stacking data to send them to the parallel process
-        self.compress_dataIn(k, xref, fsteps, l_targetFootstep)
+        self.compress_dataIn(k, xref, fsteps, l_targetFootstep, oRh, oTh)
         self.newData.value = True
 
         return 0
@@ -215,11 +230,13 @@ class MPC_Wrapper:
                 # print("New data detected")
 
                 # Retrieve data thanks to the decompression function and reshape it
-                if self.mpc_type != 3:
+                if self.mpc_type != MPC_type.CROCODDYL_PLANNER:
                     kf, xref_1dim, fsteps_1dim = self.decompress_dataIn(dataIn)
                 else:
-                    kf, xref_1dim, fsteps_1dim, l_target_1dim = self.decompress_dataIn(dataIn)
+                    kf, xref_1dim, fsteps_1dim, l_target_1dim, oRh, oTh = self.decompress_dataIn(dataIn)
                     l_target = np.reshape(l_target_1dim, (3,4))
+                    oRh = np.reshape(oRh, (3,3))
+                    oTh = np.reshape(oTh, (3,1))
 
                 # Reshaping 1-dimensional data
                 k = int(kf[0])
@@ -229,20 +246,22 @@ class MPC_Wrapper:
                 # Create the MPC object of the parallel process during the first iteration
                 if k == 0:
                     # loop_mpc = MPC.MPC(self.dt, self.n_steps, self.T_gait)
-                    if self.mpc_type == 0:
+                    if self.mpc_type == MPC_type.OSQP:
                         loop_mpc = MPC.MPC(self.params)
-                    elif self.mpc_type == 1:  # Crocoddyl MPC Linear
+                    elif self.mpc_type == MPC_type.CROCODDYL_LINEAR:  # Crocoddyl MPC Linear
                         loop_mpc = MPC_crocoddyl.MPC_crocoddyl(self.params, mu=0.9, inner=False, linearModel=True)
-                    elif self.mpc_type == 2:  # Crocoddyl MPC Non-Linear
+                    elif self.mpc_type == MPC_type.CROCODDYL_NON_LINEAR:  # Crocoddyl MPC Non-Linear
                         loop_mpc = MPC_crocoddyl.MPC_crocoddyl(self.params, mu=0.9, inner=False, linearModel=False)
-                    else:  # Crocoddyl MPC Non-Linear with footsteps optimization
+                    elif self.mpc_type == MPC_type.CROCODDYL_PLANNER:  # Crocoddyl MPC Non-Linear with footsteps optimization
                         loop_mpc = MPC_crocoddyl_planner.MPC_crocoddyl_planner(self.params, mu=0.9, inner=False)
+                    else: # Using linear model
+                        loop_mpc = MPC_crocoddyl.MPC_crocoddyl(self.params, mu=0.9, inner=False, linearModel=True)
 
                 # Run the asynchronous MPC with the data that as been retrieved
-                if self.mpc_type == 0:
+                if self.mpc_type == MPC_type.OSQP:
                     fsteps[np.isnan(fsteps)] = 0.0
                     loop_mpc.run(np.int(k), xref, fsteps)
-                elif self.mpc_type == 3:
+                elif self.mpc_type == MPC_type.CROCODDYL_PLANNER:
                     loop_mpc.solve(k, xref.copy(), fsteps.copy(), l_target.copy())
                 else:
                     loop_mpc.solve(k, xref.copy(), fsteps.copy())
@@ -251,14 +270,17 @@ class MPC_Wrapper:
                 # print(len(self.dataOut))
                 # print((loop_mpc.get_latest_result()).shape)
 
-                self.dataOut[:] = loop_mpc.get_latest_result().ravel(order='F')
+                if self.mpc_type == MPC_type.CROCODDYL_PLANNER:
+                    self.dataOut[:] = loop_mpc.get_latest_result(oRh, oTh).ravel(order='F')
+                else:
+                    self.dataOut[:] = loop_mpc.get_latest_result().ravel(order='F')
 
                 # Set shared variable to true to signal that a new result is available
                 newResult.value = True
 
         return 0
 
-    def compress_dataIn(self, k, xref, fsteps, l_targetFootstep):
+    def compress_dataIn(self, k, xref, fsteps, l_targetFootstep, oRh, oTh):
         """Compress data in a single C-type array that belongs to the shared memory to send data from the main control
         loop to the asynchronous MPC
 
@@ -272,9 +294,9 @@ class MPC_Wrapper:
         fsteps[np.isnan(fsteps)] = 0.0
 
         # Compress data in the shared input array
-        if self.mpc_type == 3:
+        if self.mpc_type == MPC_type.CROCODDYL_PLANNER:
             self.dataIn[:] = np.concatenate([[(k/self.k_mpc)], xref.ravel(),
-                                            fsteps.ravel(), l_targetFootstep.ravel()], axis=0)
+                                            fsteps.ravel(), l_targetFootstep.ravel(), oRh.ravel(), oTh.ravel()], axis=0)
         else:
             self.dataIn[:] = np.concatenate([[(k/self.k_mpc)], xref.ravel(),
                                             fsteps.ravel()], axis=0)
@@ -290,8 +312,8 @@ class MPC_Wrapper:
         """
 
         # Sizes of the different variables that are stored in the C-type array
-        if self.mpc_type == 3:
-            sizes = [0, 1, (np.int(self.n_steps)+1) * 12, 12*self.N_gait, 12]
+        if self.mpc_type == MPC_type.CROCODDYL_PLANNER:
+            sizes = [0, 1, (np.int(self.n_steps)+1) * 12, 12*self.N_gait, 12, 9, 3]
         else:
             sizes = [0, 1, (np.int(self.n_steps)+1) * 12, 12*self.N_gait]
         csizes = np.cumsum(sizes)
@@ -303,7 +325,7 @@ class MPC_Wrapper:
         """Return the result of the asynchronous MPC (desired contact forces) that is stored in the shared memory
         """
 
-        if self.mpc_type == 3:  # Need more space to store optimized footsteps
+        if self.mpc_type == MPC_type.CROCODDYL_PLANNER:  # Need more space to store optimized footsteps
             return np.array(self.dataOut[:]).reshape((32, -1), order='F')
         else:
             return np.array(self.dataOut[:]).reshape((24, -1), order='F')
diff --git a/scripts/crocoddyl_class/MPC_crocoddyl.py b/scripts/crocoddyl_class/MPC_crocoddyl.py
index fc255f44fff4c72c3de3ef569f506cd7e3f5a73f..346a2f49fbd94f8722f0642c215afda485c1d80f 100644
--- a/scripts/crocoddyl_class/MPC_crocoddyl.py
+++ b/scripts/crocoddyl_class/MPC_crocoddyl.py
@@ -10,26 +10,19 @@ class MPC_crocoddyl:
     retrieve the results.
 
     Args:
-        dt (float): time step of the MPC
-        T_mpc (float): Duration of the prediction horizon
+        params (obj): Params object containing the simulation parameters
         mu (float): Friction coefficient
-        inner(bool): Inside or outside approximation of the friction cone
+        inner (float): Inside or outside approximation of the friction cone (mu * 1/sqrt(2))
         linearModel(bool) : Approximation in the cross product by using desired state
     """
 
     def __init__(self, params,  mu=1, inner=True, linearModel=True):
-
-        # Time step of the solver
-        self.dt = params.dt_mpc
-
-        # Period of the MPC
-        self.T_mpc = params.T_mpc
-
-        # Mass of the robot
-        self.mass = params.mass
-
-        # Inertia matrix of the robot in body frame
-        self.gI = np.array(params.I_mat.tolist()).reshape((3, 3))
+        
+        self.dt = params.dt_mpc         # Time step of the solver        
+        self.T_mpc = params.T_mpc       # Period of the MPC    
+        self.n_nodes = int(self.T_mpc/self.dt)    # Number of nodes    
+        self.mass = params.mass         # Mass of the robot
+        self.gI = np.array(params.I_mat.tolist()).reshape((3, 3)) # Inertia matrix in ody frame
 
         # Friction coefficient
         if inner:
@@ -37,25 +30,9 @@ class MPC_crocoddyl:
         else:
             self.mu = mu
 
-        # Gain from OSQP MPC
-
-        # self.w_x = np.sqrt(0.5)
-        # self.w_y = np.sqrt(0.5)
-        # self.w_z = np.sqrt(2.)
-        # self.w_roll = np.sqrt(0.11)
-        # self.w_pitch = np.sqrt(0.11)
-        # self.w_yaw = np.sqrt(0.11)
-        # self.w_vx = np.sqrt(2.*np.sqrt(0.5))
-        # self.w_vy = np.sqrt(2.*np.sqrt(0.5))
-        # self.w_vz = np.sqrt(2.*np.sqrt(2.))
-        # self.w_vroll = np.sqrt(0.05*np.sqrt(0.11))
-        # self.w_vpitch = np.sqrt(0.05*np.sqrt(0.11))
-        # self.w_vyaw = np.sqrt(0.05*np.sqrt(0.11))
-
-        # from osqp, config
-        # self.stateWeight = np.sqrt([2.0, 2.0, 20.0, 0.25, 0.25, 0.25, 0.2, 0.2, 5., 0.0, 0.0, 0.3])
-
-        # Set of gains to get a better behaviour with mpc height used in WBC
+        # self.stateWeight = np.sqrt([2.0, 2.0, 20.0, 0.25, 0.25, 0.25, 0.2, 0.2, 5., 0.0, 0.0, 0.3]) 
+
+        # Weights on the state vector
         self.w_x = 0.3
         self.w_y = 0.3
         self.w_z = 2
@@ -69,93 +46,39 @@ class MPC_crocoddyl:
         self.w_vpitch = 0.07*np.sqrt(self.w_pitch)
         self.w_vyaw = 0.05*np.sqrt(self.w_yaw)
 
-        self.stateWeight = np.array([self.w_x, self.w_y, self.w_z, self.w_roll, self.w_pitch, self.w_yaw,
+        self.stateWeights = np.array([self.w_x, self.w_y, self.w_z, self.w_roll, self.w_pitch, self.w_yaw,
                                      self.w_vx, self.w_vy, self.w_vz, self.w_vroll, self.w_vpitch, self.w_vyaw])
 
-        # Weight Vector : Force Norm
-        self.forceWeights = np.array(4*[0.007, 0.007, 0.007])
-
-        # Weight Vector : Friction cone cost
-        self.frictionWeights = 1.0
-
-        # Max iteration ddp solver
-        self.max_iteration = 10
-
-        # Warm Start for the solver
-        self.warm_start = True
-
-        # Minimum normal force (N)
-        self.min_fz = 0.2
-        self.max_fz = 25    
+        
+        self.forceWeights = np.array(4*[0.007, 0.007, 0.007]) # Weight Vector : Force Norm
+        self.frictionWeights = 1.0                            # Weight Vector : Friction cone cost
 
-        # Gait matrix
-        self.gait = np.zeros((params.N_gait, 4))
-        self.index = 0
+        self.min_fz = 0.2       # Minimum normal force (N)
+        self.max_fz = 25        # Maximum normal force (N)   
 
-        # Weight on the shoulder term :
-        self.shoulderWeights = 5.
-        self.shoulder_hlim = 0.23
+        self.shoulderWeights = 5.       # Weight on the shoulder term :
+        self.shoulder_hlim = 0.23       # shoulder maximum height
 
         # Integration scheme
         self.implicit_integration = True
 
+        self.max_iteration = 10     # Max iteration ddp solver        
+        self.warm_start = True      # Warm Start for the solver
+
         # Position of the feet
         self.fsteps = np.full((params.N_gait, 12), np.nan)
+        self.gait = np.zeros((params.N_gait, 4))
+        self.index = 0
 
-        # List of the actionModel
+        # Action models
         self.ListAction = []
-
-        # Initialisation of the List model using ActionQuadrupedModel()
-        # The same model cannot be used [model]*(T_mpc/dt) because the dynamic
-        # model changes for each nodes.
-        for i in range(int(self.T_mpc/self.dt)):
-            if linearModel:
-                model = quadruped_walkgen.ActionModelQuadruped()
-            else:
-                model = quadruped_walkgen.ActionModelQuadrupedNonLinear()
-
-            # Model parameters
-            model.dt = self.dt
-            model.mass = self.mass
-            model.gI = self.gI
-            model.mu = self.mu
-            model.min_fz = self.min_fz
-            model.max_fz = self.max_fz
-
-            # Weights vectors
-            model.stateWeights = self.stateWeight
-            model.forceWeights = self.forceWeights
-            model.frictionWeights = self.frictionWeights
-            # shoulder term :
-            model.shoulderWeights = self.shoulderWeights
-            model.shoulder_hlim = self.shoulder_hlim
-
-            # integration scheme
-            model.implicit_integration = self.implicit_integration
-
-            # Add model to the list of model
-            self.ListAction.append(model)
-
-        # Terminal Node
         if linearModel:
+            self.ListAction = [quadruped_walkgen.ActionModelQuadruped() for _ in range(self.n_nodes)]
             self.terminalModel = quadruped_walkgen.ActionModelQuadruped()
         else:
+            self.ListAction = [quadruped_walkgen.ActionModelQuadrupedNonLinear() for _ in range(self.n_nodes)]
             self.terminalModel = quadruped_walkgen.ActionModelQuadrupedNonLinear()
-
-        # Model parameters of terminal node
-        self.terminalModel.dt = self.dt
-        self.terminalModel.mass = self.mass
-        self.terminalModel.gI = self.gI
-        self.terminalModel.mu = self.mu
-        self.terminalModel.min_fz = self.min_fz
-        self.terminalModel.shoulderWeights = self.shoulderWeights
-        self.terminalModel.shoulder_hlim = self.shoulder_hlim
-        self.terminalModel.implicit_integration = self.implicit_integration
-
-        # Weights vectors of terminal node
-        self.terminalModel.stateWeights = 10*self.stateWeight
-        self.terminalModel.forceWeights = np.zeros(12)
-        self.terminalModel.frictionWeights = 0.
+        self.updateActionModels()
 
         # Shooting problem
         self.problem = crocoddyl.ShootingProblem(np.zeros(12),  self.ListAction, self.terminalModel)
@@ -226,27 +149,9 @@ class MPC_crocoddyl:
             self.x_init = self.ddp.xs[2:]
             self.x_init.insert(0, xref[:, 0])
             self.x_init.append(self.ddp.xs[-1])
-        
-        else :
-
-            self.x_init.append(xref[:, 0] )
-
-            for i in range(len(self.ListAction)) :
-                self.x_init.append(np.zeros(12) )
-                self.u_init.append(np.zeros(12) )
-            
-
        
-
-        """print("1")
-        from IPython import embed
-        embed()"""
         self.ddp.solve(self.x_init,  self.u_init, self.max_iteration)
 
-        """print("3")
-        from IPython import embed
-        embed()"""
-
         return 0
 
     def get_latest_result(self):
@@ -275,42 +180,38 @@ class MPC_crocoddyl:
 
         return np.array(self.ddp.us)[:, :].transpose()[:, :]
 
-    def updateActionModel(self):
+    def initializeActionModel(self, model, terminal=False):
+        """ Initialize an action model with the parameters"""
+        # Model parameters
+        model.dt = self.dt
+        model.mass = self.mass
+        model.gI = self.gI
+        model.mu = self.mu
+        model.min_fz = self.min_fz
+        model.max_fz = self.max_fz
+
+        # Weights vectors
+        model.stateWeights = self.stateWeights
+        if terminal:
+            model.forceWeights = np.zeros(12)
+            model.frictionWeights = 0.
+        else:
+            model.max_fz = self.max_fz
+            model.forceWeights = self.forceWeights
+            model.frictionWeights = self.frictionWeights
+
+        # shoulder term :
+        model.shoulderWeights = self.shoulderWeights
+        model.shoulder_hlim = self.shoulder_hlim
+
+        # integration scheme
+        model.implicit_integration = self.implicit_integration
+
+    def updateActionModels(self):
         """Update the quadruped model with the new weights or model parameters.
         Useful to try new weights without modify this class
         """
+        for model in self.ListAction:
+            self.initializeActionModel(model)
 
-        for elt in self.ListAction:
-            elt.dt = self.dt
-            elt.mass = self.mass
-            elt.gI = self.gI
-            elt.mu = self.mu
-            elt.min_fz = self.min_fz
-            elt.max_fz = self.max_fz
-
-            # Weights vectors
-            elt.stateWeights = self.stateWeight
-            elt.forceWeights = self.forceWeights
-            elt.frictionWeights = self.frictionWeights
-
-            # shoulder term :
-            elt.shoulderWeights = self.shoulderWeights
-            elt.shoulder_hlim = self.shoulder_hlim
-
-        # Model parameters of terminal node
-        self.terminalModel.dt = self.dt
-        self.terminalModel.mass = self.mass
-        self.terminalModel.gI = self.gI
-        self.terminalModel.mu = self.mu
-        self.terminalModel.min_fz = self.min_fz
-
-        # Weights vectors of terminal node
-        self.terminalModel.stateWeights = self.stateWeight
-        self.terminalModel.forceWeights = np.zeros(12)
-        self.terminalModel.frictionWeights = 0.
-
-        # shoulder term :
-        self.terminalModel.shoulderWeights = self.shoulderWeights
-        self.terminalModel.shoulder_hlim = self.shoulder_hlim
-
-        return 0
+        self.initializeActionModel(self.terminalModel, terminal=True)
diff --git a/scripts/crocoddyl_class/MPC_crocoddyl_planner.py b/scripts/crocoddyl_class/MPC_crocoddyl_planner.py
index b3f46193c0bcfb64fad42c2bfbf632fc642a5c5a..e870b50e1a4765ef752e952e6f548267daae9e3c 100644
--- a/scripts/crocoddyl_class/MPC_crocoddyl_planner.py
+++ b/scripts/crocoddyl_class/MPC_crocoddyl_planner.py
@@ -10,27 +10,20 @@ class MPC_crocoddyl_planner():
     retrieve the results.
 
     Args:
-        dt (float): time step of the MPC
-        T_mpc (float): Duration of the prediction horizon
+        params (obj): Params object containing the simulation parameters
         mu (float): Friction coefficient
-        inner(bool): Inside or outside approximation of the friction cone
+        inner (float): Inside or outside approximation of the friction cone (mu * 1/sqrt(2))
+        linearModel(bool) : Approximation in the cross product by using desired state
     """
 
     def __init__(self, params,  mu=1, inner=True, warm_start=True, min_fz=0.0):
 
-        # Time step of the solver
-        self.dt = params.dt_mpc
-
-        # Period of the MPC
-        self.T_mpc = params.T_mpc
-
-        # Mass of the robot
-        self.mass = 2.50000279
-
-        # Inertia matrix of the robot in body frame
-        self.gI = np.array([[3.09249e-2, -8.00101e-7, 1.865287e-5],
-                            [-8.00101e-7, 5.106100e-2, 1.245813e-4],
-                            [1.865287e-5, 1.245813e-4, 6.939757e-2]])
+        self.dt = params.dt_mpc                   # Time step of the solver        
+        self.T_mpc = params.T_mpc                 # Period of the MPC    
+        self.n_nodes = int(self.T_mpc/self.dt)    # Number of nodes    
+        self.mass = params.mass                   # Mass of the robot
+        self.max_iteration = 20                   # Max iteration ddp solver
+        self.gI = np.array(params.I_mat.tolist()).reshape((3, 3)) # Inertia matrix in ody frame
 
         # Friction coefficient
         if inner:
@@ -38,6 +31,8 @@ class MPC_crocoddyl_planner():
         else:
             self.mu = mu
 
+        # self.stateWeight = np.sqrt([2.0, 2.0, 20.0, 0.25, 0.25, 0.25, 0.2, 0.2, 5., 0.0, 0.0, 0.3]) 
+
         # Weights Vector : States
         self.w_x = 0.3
         self.w_y = 0.3
@@ -54,72 +49,53 @@ class MPC_crocoddyl_planner():
         self.stateWeights = np.array([self.w_x, self.w_y, self.w_z, self.w_roll, self.w_pitch, self.w_yaw,
                                       self.w_vx, self.w_vy, self.w_vz, self.w_vroll, self.w_vpitch, self.w_vyaw])
 
-        # Weight Vector : Force Norm
-        self.forceWeights = np.array(4*[0.01, 0.01, 0.01])
-
-        # Weight Vector : Friction cone cost
-        self.frictionWeights = 0.5
-
-        # Weights on the heuristic term 
-        self.heuristicWeights = np.array(4*[0.3, 0.4])
+        self.forceWeights = 2*np.array(4*[0.01, 0.01, 0.01])  # Weight Vector : Force Norm
+        self.frictionWeights = 1.                          # Weight Vector : Friction cone cost
+        self.heuristicWeights = np.array(4*[0.3, 0.4])      # Weights on the heuristic term
+        self.stepWeights = np.full(8, 0.005)                 # Weight on the step command (distance between steps)
+        self.stopWeights = 2.5*np.ones(8)                       # Weights to stop the optimisation at the end of the flying phase
+        self.shoulderContactWeight = 5.                      # Weight for shoulder-to-contact penalty
+        self.shoulder_hlim = 0.235
 
-        # Weight on the step command (distance between steps)
-        self.stepWeights = np.full(8, 0.05)
-
-        # Weights to stop the optimisation at the end of the flying phase
-        self.stopWeights = np.full(8, 1.)
-
-        # Weight for shoulder-to-contact penalty
-        self.shoulderContactWeight = 5
-        self.shoulder_hlim = 0.225
-
-        # Max iteration ddp solver
-        self.max_iteration = 10
-
-        # Warm-start for the solver
-        # Always on, just an approximation, not the previous result
         # TODO : create a proper warm-start with the previous optimisation
-        self.warm_start = warm_start
+        self.warm_start = True
 
         # Minimum normal force(N) and reference force vector bool
-        self.min_fz = min_fz
+        self.min_fz = 1.
         self.relative_forces = True 
 
         # Gait matrix
         self.gait = np.zeros((params.N_gait, 4))
-        self.gait_old = np.zeros((1, 4))
+        self.gait_old = np.zeros(4)
 
         # Position of the feet in local frame
-        self.fsteps = np.full((params.N_gait, 12), 0.0)
+        self.fsteps = np.zeros((params.N_gait, 12))
 
         # List to generate the problem
-        self.ListAction = []
+        self.action_models = []
         self.x_init = []
-        self.u_init = []       
-
-        self.l_fsteps = np.zeros((3, 4))
-        self.o_fsteps = np.zeros((3, 4))
+        self.u_init = []
 
-        # Shooting problem
-        self.problem = None
-
-        # ddp solver
-        self.ddp = None
-
-        # Xs results without the actionStepModel
-        self.Xs = np.zeros((20, int(self.T_mpc/self.dt)))
-        # self.Us = np.zeros((12,int(T_mpc/dt)))
+        self.problem = None   # Shooting problem
+        self.ddp = None  # ddp solver
+        self.Xs = np.zeros((20, int(self.T_mpc/self.dt)))  # Xs results without the actionStepModel
 
         # Initial foot location (local frame, X,Y plan)
-        self.p0 = [0.1946, 0.14695, 0.1946, -0.14695, -0.1946,   0.14695, -0.1946,  -0.14695]
+        self.shoulders = [0.1946, 0.14695, 0.1946, -0.14695, -0.1946,   0.14695, -0.1946,  -0.14695]
+
+        # Index to stop the feet optimisation
+        self.index_lock_time = int(params.lock_time / params.dt_mpc)  # Row index in the gait matrix when the optimisation of the feet should be stopped
+        self.index_stop_optimisation = []  # List of index to reset the stopWeights to 0 after optimisation
 
-        # Index to stop the feet optimisation 
-        self.index_lock_time = int(params.lock_time / params.dt_mpc ) # Row index in the gait matrix when the optimisation of the feet should be stopped
-        self.index_stop_optimisation = [] # List of index to reset the stopWeights to 0 after optimisation
+        # USefull to optimise around the previous optimisation
+        self.flying_foot = 4*[False] # Bool corresponding to the current flying foot (gait[0,foot_id] == 0)
+        self.flying_foot_nodes = np.zeros(4) # The number of nodes in the next phase of flight
+        self.flying_max_nodes = int(params.T_mpc / (2 * params.dt_mpc)) # TODO : get the maximum number of nodes from the gait_planner
 
-        self.initializeModels(params)
+        # Initialize the lists of models
+        self.initialize_models(params)
 
-    def initializeModels(self, params):
+    def initialize_models(self, params):
         ''' Reset the two lists of augmented and step-by-step models, to avoid recreating them at each loop.
         Not all models here will necessarily be used.  
 
@@ -129,226 +105,228 @@ class MPC_crocoddyl_planner():
         self.models_augmented = []
         self.models_step = []
 
-        for j in range(params.N_gait) :
-            model = quadruped_walkgen.ActionModelQuadrupedAugmented()            
-            
+        for _ in range(params.N_gait):
+            model = quadruped_walkgen.ActionModelQuadrupedAugmented()
             self.update_model_augmented(model)
             self.models_augmented.append(model)
 
-        for j in range(4 * int(params.T_gait / params.T_mpc) ) :
-            model = quadruped_walkgen.ActionModelQuadrupedStep()   
-            
+        for _ in range(4 * int(params.T_gait / params.T_mpc)):
+            model = quadruped_walkgen.ActionModelQuadrupedStep()
             self.update_model_step(model)
             self.models_step.append(model)
 
         # Terminal node
-        self.terminalModel = quadruped_walkgen.ActionModelQuadrupedAugmented() 
-        self.update_model_augmented(self.terminalModel)
-        # Weights vectors of terminal node (no command cost)
-        self.terminalModel.forceWeights = np.zeros(12)
-        self.terminalModel.frictionWeights = 0.
-        self.terminalModel.heuristicWeights = np.full(8, 0.0)
-        self.terminalModel.stopWeights = np.full(8, 0.0)
+        self.terminal_model = quadruped_walkgen.ActionModelQuadrupedAugmented()
+        self.update_model_augmented(self.terminal_model, terminal=True)
 
-        return 0
-
-    def solve(self, k, xref, l_feet, l_stop):
+    def solve(self, k, xref, footsteps, l_stop):
         """ Solve the MPC problem
 
         Args:
             k : Iteration
             xref : the desired state vector
-            l_feet : current position of the feet (given by planner)
+            footsteps : current position of the feet (given by planner)
             l_stop : current and target position of the feet (given by footstepTragectory generator)
         """
-
-        # Update the dynamic depending on the predicted feet position
-        self.updateProblem(k, xref, l_feet, l_stop)
-
-        # Solve problem
+        self.updateProblem(k, xref, footsteps, l_stop)
         self.ddp.solve(self.x_init, self.u_init, self.max_iteration)
 
         # Reset to 0 the stopWeights for next optimisation
-        for index_stopped in self.index_stop_optimisation :
+        for index_stopped in self.index_stop_optimisation:
             self.models_augmented[index_stopped].stopWeights = np.zeros(8)
 
-        return 0
-
-    def updateProblem(self, k, xref, l_feet, l_stop):
-        """Update the dynamic of the model list according to the predicted position of the feet,
+    def updateProblem(self, k, xref, footsteps, l_stop):
+        """
+        Update the dynamic of the model list according to the predicted position of the feet,
         and the desired state.
 
         Args:
         """
-        # Save previous gait state before updating the gait
-        self.gait_old[0, :] = self.gait[0, :].copy()        
-
-        # Recontruct the gait based on the computed footsteps
-        a = 0
-        while np.any(l_feet[a, :]):
-            self.gait[a, :] = (l_feet[a, ::3] != 0.0).astype(int)
-            a += 1
-        self.gait[a:, :] = 0.0
-
-        # On swing phase before --> initialised below shoulder
-        p0 = (1.0 - np.repeat(self.gait[0, :], 2)) * self.p0
-        # On the ground before -->  initialised with the current feet position
-        p0 += np.repeat(self.gait[0, :], 2) * l_feet[0, [0, 1, 3, 4, 6, 7, 9, 10]]  # (x, y) of each foot       
-        
+        self.compute_gait_matrix(footsteps)
+
+        p0 = (1.0 - np.repeat(self.gait[0, :], 2)) * self.shoulders \
+            + np.repeat(self.gait[0, :], 2) * footsteps[0, [0, 1, 3, 4, 6, 7, 9, 10]]
+
         self.x_init.clear()
         self.u_init.clear()
-        self.ListAction.clear()
+        self.action_models.clear()
         self.index_stop_optimisation.clear()
 
         index_step = 0
-        index_augmented = 0   
-        j = 0   
-
-        # Iterate over all phases of the gait
-        while np.any(self.gait[j, :]):
-            if j == 0 : # First row, need to take into account previous gait
-                if np.any(self.gait[0,:] - self.gait_old[0,:]) :
-                    # Step model
-                    self.models_step[index_step].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                       xref[:, j+1], self.gait[0, :] - self.gait_old[0, :])
-                    self.ListAction.append(self.models_step[index_step])
-
-                    # Augmented model
-                    self.models_augmented[index_augmented].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                    l_stop, xref[:, j+1], self.gait[j, :])                  
-
-                    # Activation of the cost to stop the optimisation around l_stop (position locked by the footstepGenerator)
-                    if j < self.index_lock_time :
-                        self.models_augmented[index_augmented].stopWeights = self.stopWeights
-                        self.index_stop_optimisation.append(index_augmented)
-                    
-                    self.ListAction.append(self.models_augmented[index_augmented])
-
-
-                    index_step += 1
-                    index_augmented += 1
-                    # Warm-start
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))
-                    self.u_init.append(np.zeros(8))
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))   
-                    self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])] ))
+        index_augmented = 0
+        j = 1
+        
+        stopping_needed = 4*[False]
+        if k > 110 :
+            for index_foot, is_flying in enumerate(self.flying_foot):
+                if is_flying:
+                    stopping_needed[index_foot] = self.flying_foot_nodes[index_foot] != self.flying_max_nodes # Position optimized at the previous control cycle
+
+        # Augmented model, first node, j = 0
+        self.models_augmented[index_augmented].updateModel(np.reshape(footsteps[0, :], (3, 4), order='F'),
+                                                            l_stop, xref[:, 1], self.gait[0, :])
+        self.action_models.append(self.models_augmented[index_augmented])
+
+        index_augmented += 1
+        # Warm-start
+        self.x_init.append(np.concatenate([xref[:, 1], p0]))
+        self.u_init.append(np.repeat(self.gait[0, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[0, :])]))
+
+        while np.any(self.gait[j, :]):            
+            if np.any(self.gait[j, :] - self.gait[j-1, :]):
+                # Step model
+                self.models_step[index_step].updateModel(np.reshape(footsteps[j, :], (3, 4), order='F'),
+                                                            xref[:, j+1], self.gait[j, :] - self.gait[j-1, :])
+                self.action_models.append(self.models_step[index_step])
+
+                # Augmented model
+                self.models_augmented[index_augmented].updateModel(np.reshape(footsteps[j, :], (3, 4), order='F'),
+                                                                    l_stop, xref[:, j+1], self.gait[j, :])
+
+                # Activation of the cost to stop the optimisation around l_stop (position locked by the footstepGenerator)
+                # if j < self.index_lock_time:
+                #     self.models_augmented[index_augmented].stopWeights = self.stopWeights
+                #     self.index_stop_optimisation.append(index_augmented)
+
+                feet_ground = np.where(self.gait[j,:] == 1)[0]
+                activation_cost = False
+                for foot in range(4): 
+                    if (stopping_needed[foot]) and (self.flying_foot[foot]) and (foot in feet_ground) and (j < int(self.flying_foot_nodes[foot] + self.flying_max_nodes) ) :
+                        coeff_activated = np.zeros(8)
+                        coeff_activated[2*foot: 2*foot + 2] = np.array([1,1])
+                        self.models_augmented[index_augmented].stopWeights = self.models_augmented[index_augmented].stopWeights + coeff_activated*self.stopWeights
+                        activation_cost = True
                 
-                else : 
-                    # Augmented model
-                    self.models_augmented[index_augmented].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                     l_stop, xref[:, j+1], self.gait[j, :])
-                    self.ListAction.append(self.models_augmented[index_augmented])
-                    
-                    index_augmented += 1
-                    # Warm-start
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))   
-                    self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])] ))
-            
-            else : 
-                if np.any(self.gait[j,:] - self.gait[j-1,:]) :
-                    # Step model
-                    self.models_step[index_step].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                       xref[:, j+1], self.gait[j, :] - self.gait[j-1, :])
-                    self.ListAction.append(self.models_step[index_step])
-
-                    # Augmented model
-                    self.models_augmented[index_augmented].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                     l_stop, xref[:, j+1], self.gait[j, :])
-
-                    # Activation of the cost to stop the optimisation around l_stop (position locked by the footstepGenerator)
-                    if j < self.index_lock_time :
-                        self.models_augmented[index_augmented].stopWeights = self.stopWeights
-                        self.index_stop_optimisation.append(index_augmented)
-                    
-                    self.ListAction.append(self.models_augmented[index_augmented])
-
-                    index_step += 1
-                    index_augmented += 1
-                    # Warm-start
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))
-                    self.u_init.append(np.zeros(8))
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))   
-                    self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])] ))
-                    
+                if activation_cost :
+                    self.index_stop_optimisation.append(index_augmented)
+
+
+                self.action_models.append(self.models_augmented[index_augmented])
+
+                index_step += 1
+                index_augmented += 1
+                # Warm-start
+                self.x_init.append(np.concatenate([xref[:, j+1], p0]))
+                self.u_init.append(np.zeros(8))
+                self.x_init.append(np.concatenate([xref[:, j+1], p0]))
+                self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])]))
+
+            else:
+                self.models_augmented[index_augmented].updateModel(np.reshape(footsteps[j, :], (3, 4), order='F'),
+                                                                    l_stop, xref[:, j+1], self.gait[j, :])
+                self.action_models.append(self.models_augmented[index_augmented])
+
+                feet_ground = np.where(self.gait[j,:] == 1)[0]
+                activation_cost = False
+                for foot in range(4): 
+                    if (stopping_needed[foot]) and (self.flying_foot[foot]) and (foot in feet_ground) and (j < int(self.flying_foot_nodes[foot] + self.flying_max_nodes) ) :
+                        coeff_activated = np.zeros(8)
+                        coeff_activated[2*foot: 2*foot + 2] = np.array([1,1])
+                        self.models_augmented[index_augmented].stopWeights = self.models_augmented[index_augmented].stopWeights + coeff_activated*self.stopWeights
+                        activation_cost = True
                 
-                else :
-                    self.models_augmented[index_augmented].updateModel(np.reshape(l_feet[j, :], (3, 4), order='F'),
-                                                     l_stop, xref[:, j+1], self.gait[j, :])
-                    self.ListAction.append(self.models_augmented[index_augmented])
-                    
-                    index_augmented += 1
-                    # Warm-start
-                    self.x_init.append(np.concatenate([xref[:, j+1], p0]))   
-                    self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])] ))
+                if activation_cost :
+                    self.index_stop_optimisation.append(index_augmented)
+
+                index_augmented += 1
+                # Warm-start
+                self.x_init.append(np.concatenate([xref[:, j+1], p0]))
+                self.u_init.append(np.repeat(self.gait[j, :], 3) * np.array(4*[0., 0., 2.5*9.81/np.sum(self.gait[j, :])]))
 
             # Update row matrix
             j += 1
 
-        # Update terminal model 
-        self.terminalModel.updateModel(np.reshape(l_feet[j-1, :], (3, 4), order='F'), l_stop, xref[:, -1], self.gait[j-1, :])
+        # Update terminal model
+        self.terminal_model.updateModel(np.reshape(footsteps[j-1, :], (3, 4), order='F'), l_stop, xref[:, -1], self.gait[j-1, :])
         # Warm-start
-        self.x_init.append(np.concatenate([xref[:, j-1], p0])) 
-
-        # Shooting problem
-        self.problem = crocoddyl.ShootingProblem(np.zeros(20),  self.ListAction, self.terminalModel)
+        self.x_init.append(np.concatenate([xref[:, j-1], p0]))
 
+        self.problem = crocoddyl.ShootingProblem(np.zeros(20),  self.action_models, self.terminal_model)
         self.problem.x0 = np.concatenate([xref[:, 0], p0])
 
-        # DDP Solver
         self.ddp = crocoddyl.SolverDDP(self.problem)
 
-        return 0
-
-    def get_latest_result(self):
-        """Return the desired contact forces that have been computed by the last iteration of the MPC
-        Args:
+    def get_latest_result(self, oRh, oTh):
+        """ 
+        Return the desired contact forces that have been computed by the last iteration of the MPC
+        Args : 
+         - q ( Array 7x1 ) : pos, quaternion orientation
         """
-
-        cpt = 0
+        index = 0
         N = int(self.T_mpc / self.dt)
         result = np.zeros((32, N))
-        for i in range(len(self.ListAction)):
-            if self.ListAction[i].__class__.__name__ != "ActionModelQuadrupedStep":
-                if cpt >= N:
+        for i in range(len(self.action_models)):
+            if self.action_models[i].__class__.__name__ != "ActionModelQuadrupedStep":
+                if index >= N:
                     raise ValueError("Too many action model considering the current MPC prediction horizon")
-                result[:12, cpt] = self.ddp.xs[i][:12]
-                result[12:24, cpt] = self.ddp.us[i]  # * np.repeat(self.gait[cpt, :] , 3)
-                result[24:, cpt] = self.ddp.xs[i][12:]
-                cpt += 1
-                if i > 0 and self.ListAction[i-1].__class__.__name__ == "ActionModelQuadrupedStep":
-                    # print(self.ddp.xs[i][12:])
+                result[:12, index] = self.ddp.xs[i+1][:12] # First node correspond to current state
+                result[12:24, index] = self.ddp.us[i]
+                result[24:, index] = ( oRh[:2,:2] @ (self.ddp.xs[i+1][12:].reshape((2,4) , order = "F")  ) + oTh[:2]).reshape((8), order = "F")
+                if i > 0 and self.action_models[i-1].__class__.__name__ == "ActionModelQuadrupedStep":
                     pass
+                index += 1
 
         return result
 
-    def update_model_augmented(self, model):
-        '''Set intern parameters for augmented model type
-        '''
+    def update_model_augmented(self, model, terminal=False):
+        """ 
+        Set intern parameters for augmented model type
+        """
         # Model parameters
         model.dt = self.dt
         model.mass = self.mass
         model.gI = self.gI
         model.mu = self.mu
         model.min_fz = self.min_fz
-        model.relative_forces = self.relative_forces
+        model.relative_forces = True
         model.shoulderContactWeight = self.shoulderContactWeight
+        model.shoulder_hlim = self.shoulder_hlim 
 
         # Weights vectors
         model.stateWeights = self.stateWeights
-        model.forceWeights = self.forceWeights
-        model.frictionWeights = self.frictionWeights
+        model.stopWeights = np.zeros(8)
 
-        # Weight on feet position
-        model.stopWeights = np.full(8, 0.0) # Will be updated when necessary
-        model.heuristicWeights = self.heuristicWeights
-
-        return 0
+        if terminal:
+            self.terminal_model.forceWeights = np.zeros(12)
+            self.terminal_model.frictionWeights = 0.
+            self.terminal_model.heuristicWeights = np.zeros(8)
+        else:
+            model.frictionWeights = self.frictionWeights
+            model.forceWeights = self.forceWeights
+            model.heuristicWeights = self.heuristicWeights
 
     def update_model_step(self, model):
-        """Set intern parameters for step model type
+        """
+        Set intern parameters for step model type
         """
         model.heuristicWeights = np.zeros(8)
         model.stateWeights = self.stateWeights
         model.stepWeights = self.stepWeights
 
-        return 0
\ No newline at end of file
+    def compute_gait_matrix(self, footsteps):
+        """ 
+        Recontruct the gait based on the computed footstepsC
+        Args:
+            footsteps : current and predicted position of the feet
+        """
+
+        self.gait_old = self.gait[0, :].copy()
+
+        j = 0
+        self.gait = np.zeros(np.shape(self.gait))
+        while np.any(footsteps[j, :]):
+            self.gait[j, :] = (footsteps[j, ::3] != 0.0).astype(int)
+            j += 1
+
+        # Get the current flying feet and the number of nodes
+        for foot in range(4):
+            row = 0
+            if self.gait[0, foot] == 0:
+                self.flying_foot[foot] = True 
+                while self.gait[row, foot] == 0:
+                    row += 1
+                self.flying_foot_nodes[foot] = int(row)
+            else:
+                self.flying_foot[foot] = False
+    
+
diff --git a/scripts/solo3D/tools/vizu_help.py b/scripts/solo3D/tools/vizu_help.py
new file mode 100644
index 0000000000000000000000000000000000000000..70da222f983acc6e7d5a6e53bdc9032246616241
--- /dev/null
+++ b/scripts/solo3D/tools/vizu_help.py
@@ -0,0 +1,508 @@
+import numpy as np
+import pybullet as pyb
+import pybullet_data
+from time import perf_counter as clock
+import pinocchio as pin
+# from solo3D.tools.geometry import EulerToQuaternion
+import os 
+
+class PybVisualizationTraj():
+    ''' Class used to vizualise the feet trajectory on the pybullet simulation 
+    '''
+
+    def __init__(self, footTrajectoryGenerator, enable_pyb_GUI):
+
+        # Pybullet enabled
+        self.enable_pyb_GUI = enable_pyb_GUI
+
+        self.footTrajectoryGenerator = footTrajectoryGenerator
+
+
+        # self.ftps_Ids_target = [0] * 4
+        # self.n_points = 15
+
+        # n_points for the traj , 4 feet , 3 target max in futur
+        # self.trajectory_Ids = np.zeros((3, 4, self.n_points))
+        self.ftps_Ids_target = np.zeros((3, 4))
+
+
+
+    def update(self, k , device , o_targetFootstep_mpc, o_targetFootstep_planner) :
+        ''' Update position of the objects in pybullet environment.
+        Args :
+        - k (int) : step of the simulation
+        - device (object) : Class used to set up the pybullet env
+    '''
+
+        if k == 1:  # k ==0, device is still a dummy object, pyb env did not started
+            self.initializeEnv()
+
+
+        if self.enable_pyb_GUI and k > 100:
+
+            self.updateTarget(o_targetFootstep_mpc, o_targetFootstep_planner)
+
+        #     # Update position of PyBullet camera
+        #     # self.updateCamera(k , device)
+
+        #     if k % self.refresh == 0:
+
+        #         t_init = clock()
+
+        #         # # Update target trajectory, current and next phase
+        #         self.updateTargetTrajectory()
+
+        #         # # Update refrence trajectory
+        #         # self.updateRefTrajectory()
+
+        #         # update constraints
+        #         # self.updateConstraints()
+
+        #         #update Sl1M :
+        #         # self.updateSl1M_target(all_feet_pos)
+
+        #         t_end = clock() - t_init
+                # print("Time for update pybullet = ",  t_end)
+
+        return 0
+
+    def updateTarget(self, o_targetFootstep_mpc, o_targetFootstep_planner ):
+        
+        for foot in range(4) :
+            pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[0, foot]),
+                                                            posObj=o_targetFootstep_mpc[:, foot],
+                                                            ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+        target_footstep_curve = self.footTrajectoryGenerator.getFootPosition()
+        for foot in range(4) :
+            pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[1, foot]),
+                                                            posObj=target_footstep_curve[:, foot],
+                                                            ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+        # for foot in range(4) :
+        #     pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[2, foot]),
+        #                                                         posObj=o_targetFootstep_planner[:, foot],
+        #                                                         ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+
+        return 0
+    
+    def initializeEnv(self):
+        ''' Load object in pybullet environment.
+        '''
+
+        print("Loading pybullet object ...")
+
+        pyb.setAdditionalSearchPath(pybullet_data.getDataPath())   
+        for i in range(self.ftps_Ids_target.shape[0]):  # nb of feet target in futur
+
+            # rgbaColor : [R , B , G , alpha opacity]
+            if i == 0:
+                rgba = [0., 1., 0., 1.]
+
+            elif i == 1 :
+                rgba = [1., 0., 0., 1.]
+            else :
+                rgba = [0., 0., 1., 1.]
+
+
+            mesh_scale = [0.008, 0.008, 0.008]
+            visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+                                                  fileName="sphere_smooth.obj",
+                                                  halfExtents=[0.5, 0.5, 0.1],
+                                                  rgbaColor=rgba,
+                                                  specularColor=[0.4, .4, 0],
+                                                  visualFramePosition=[0.0, 0.0, 0.0],
+                                                  meshScale=mesh_scale)
+            for j in range(4):
+                self.ftps_Ids_target[i, j] = pyb.createMultiBody(baseMass=0.0,
+                                                                 baseInertialFramePosition=[0, 0, 0],
+                                                                 baseVisualShapeIndex=visualShapeId,
+                                                                 basePosition=[0.0, 0.0, -0.1],
+                                                                 useMaximalCoordinates=True)
+        return 0 
+
+
+
+    # def updateConstraints(self):
+
+    #     gait = self.gait.getCurrentGait() 
+    #     fsteps =  self.footStepPlannerQP.getFootsteps()
+    #     footsteps = fsteps[0].reshape((3,4) , order ="F")
+        
+    #     feet = np.where(gait[0,:] == 1)[0]
+        
+    #     for i in range(4):
+    #         if i in feet :
+    #             pyb.resetBasePositionAndOrientation(int( self.constraints_objects[i]),
+    #                                             posObj=footsteps[:,i],
+    #                                             ornObj=np.array([0.0, 0.0, 0.0, 1.0]))           
+
+    #         else :
+    #             pyb.resetBasePositionAndOrientation(int( self.constraints_objects[i]),
+    #                                             posObj=np.array([0.,0.,-1]),
+    #                                             ornObj=np.array([0.0, 0.0, 0.0, 1.0]))     
+
+    #     return 0
+
+    # def updateCamera(self, k, device):
+    #     # Update position of PyBullet camera on the robot position to do as if it was attached to the robot
+    #     if k > 10 and self.enable_pyb_GUI:
+    #         pyb.resetDebugVisualizerCamera(cameraDistance=0.95, cameraYaw=357, cameraPitch=-29,
+    #                                        cameraTargetPosition=[0.6, 0.14, -0.22])
+    #         # pyb.resetDebugVisualizerCamera(cameraDistance=1., cameraYaw=357, cameraPitch=-28,
+    #         #                                cameraTargetPosition=[device.dummyHeight[0], device.dummyHeight[1], 0.0])
+
+    #     return 0
+
+    # def updateSl1M_target(self,all_feet_pos) :
+    #     ''' Update position of the SL1M target
+    #     Args : 
+    #         -  all_feet_pos : list of optimized position such as : [[Foot 1 next_pos, None , Foot1 next_pos] , [Foot 2 next_pos, None , Foot2 next_pos] ]
+    #     '''
+
+    #     for i in range(len(all_feet_pos[0])) :
+    #         for j in range(len(all_feet_pos)) :
+    #             if all_feet_pos[j][i] is None : 
+    #                 pyb.resetBasePositionAndOrientation(int(self.sl1m_Ids_target[i, j]),
+    #                                                                 posObj=np.array([0., 0., -0.5]),
+    #                                                                 ornObj=np.array([0.0, 0.0, 0.0, 1.0]))                   
+                
+    #             else :
+    #                 pyb.resetBasePositionAndOrientation(int(self.sl1m_Ids_target[i, j]),
+    #                                                                 posObj=all_feet_pos[j][i],
+    #                                                                 ornObj=np.array([0.0, 0.0, 0.0, 1.0]))     
+                    
+                
+
+
+    #     return 0 
+
+    # def updateRefTrajectory(self):
+    #     ''' Update the reference state trajectory given by StatePlanner Class.
+    #     '''
+
+    #     referenceState = self.statePlanner.getReferenceStates()
+
+    #     for k in range(self.state_Ids.shape[0]):
+
+    #         quat = EulerToQuaternion(referenceState[3:6, k])
+    #         pyb.resetBasePositionAndOrientation(int(self.state_Ids[k]),
+    #                                             posObj=referenceState[:3, k] + np.array([0., 0., 0.04]),
+    #                                             ornObj=quat)
+
+    #     # Update surface :
+
+    #     a, b, c = self.statePlanner.surface_equation
+    #     pos = self.statePlanner.surface_point
+
+    #     RPY = np.array([-np.arctan2(b, 1.), -np.arctan2(a, 1.), 0.])
+    #     quat = EulerToQuaternion(RPY)
+    #     pyb.resetBasePositionAndOrientation(int(self.surface_Id),
+    #                                         posObj=pos,
+    #                                         ornObj=quat)
+
+    #     return 0
+
+    # def updateTargetTrajectory(self):
+    #     ''' Update the target trajectory for current and next phases. Hide the unnecessary spheres.
+    #     '''
+
+    #     gait = self.gait.getCurrentGait()
+    #     fsteps =  self.footStepPlannerQP.getFootsteps()
+
+    #     for j in range(4):
+
+    #         # Count the position of the plotted trajectory in the temporal horizon
+    #         # c = 0 --> Current trajectory/foot pos
+    #         # c = 1 --> Next trajectory/foot pos
+    #         c = 0
+    #         i = 0
+
+    #         init_pos = np.zeros(3)
+
+    #         while gait[i, :].any():
+    #             footsteps = fsteps[i].reshape((3,4) , order ="F")
+    #             if i > 0:
+    #                 if (1 - gait[i-1, j]) * gait[i, j] > 0:  # from flying phase to stance
+
+
+                        
+
+    #                     # In any case plot target
+    #                     pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[c, j]),
+    #                                                         posObj=footsteps[:, j],
+    #                                                         ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+    #                     if c == 0:
+    #                         # Current flying phase, using coeff store in Bezier curve class
+    #                         t0 = self.footTrajectoryGenerator.t0s[j]
+    #                         t1 = self.footTrajectoryGenerator.t_swing[j]
+    #                         t_vector = np.linspace(t0, t1, self.n_points)
+
+    #                         for id_t, t in enumerate(t_vector):
+    #                             # Bezier trajectory
+    #                             pos = self.footTrajectoryGenerator.evaluateBezier(j, 0, t)
+    #                             # Polynomial Curve 5th order
+    #                             # pos = self.footTrajectoryGenerator.evaluatePoly(j, 0, t)
+    #                             pyb.resetBasePositionAndOrientation(int(self.trajectory_Ids[c, j, id_t]),
+    #                                                                 posObj=pos,
+    #                                                                 ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #                     else:
+    #                         # Next phase, using a simple polynomial curve to approximate the next trajectory
+    #                         t0 = 0.
+    #                         t1 = self.gait.getPhaseDuration(i, j, 1.0)
+    #                         t_vector = np.linspace(t0, t1, self.n_points)
+
+    #                         self.footTrajectoryGenerator.updatePolyCoeff_simple(j, init_pos, footsteps[:, j], t1)
+    #                         for id_t, t in enumerate(t_vector):
+    #                             pos = self.footTrajectoryGenerator.evaluatePoly_simple(j, 0, t)
+    #                             pyb.resetBasePositionAndOrientation(int(self.trajectory_Ids[c, j, id_t]),
+    #                                                                 posObj=pos,
+    #                                                                 ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #                     c += 1
+
+    #                 if gait[i-1, j] * (1 - gait[i, j]) > 0:
+    #                     # Starting a flying phase
+    #                     footsteps_previous = fsteps[i-1].reshape((3,4) , order ="F")
+    #                     init_pos[:] = footsteps_previous[:, j]
+
+    #             else:
+    #                 if gait[i, j] == 1:
+    #                     # foot already on the ground
+    #                     pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[c, j]),
+    #                                                         posObj=footsteps[:, j],
+    #                                                         ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #                     # not hidden in the floor, traj
+    #                     if not pyb.getBasePositionAndOrientation(int(self.trajectory_Ids[0, j, 0]))[0][2] == -0.1:
+    #                         for t in range(self.n_points):
+    #                             pyb.resetBasePositionAndOrientation(int(self.trajectory_Ids[0, j, t]),
+    #                                                                 posObj=np.array([0., 0., -0.1]),
+    #                                                                 ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #                     c += 1
+
+    #             i += 1
+
+    #         # Hide the sphere objects not used
+    #         while c < self.ftps_Ids_target.shape[0]:
+
+    #             # not hidden in the floor, target
+    #             if not pyb.getBasePositionAndOrientation(int(self.ftps_Ids_target[c, j]))[0][2] == -0.1:
+    #                 pyb.resetBasePositionAndOrientation(int(self.ftps_Ids_target[c, j]),
+    #                                                     posObj=np.array([0., 0., -0.1]),
+    #                                                     ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #             # not hidden in the floor, traj
+    #             if not pyb.getBasePositionAndOrientation(int(self.trajectory_Ids[c, j, 0]))[0][2] == -0.1:
+    #                 for t in range(self.n_points):
+    #                     pyb.resetBasePositionAndOrientation(int(self.trajectory_Ids[c, j, t]),
+    #                                                         posObj=np.array([0., 0., -0.1]),
+    #                                                         ornObj=np.array([0.0, 0.0, 0.0, 1.0]))
+
+    #             c += 1
+
+    #     return 0
+
+    # def initializeEnv(self):
+    #     ''' Load object in pybullet environment.
+    #     '''
+
+    #     print("Loading pybullet object ...")
+
+    #     pyb.setAdditionalSearchPath(pybullet_data.getDataPath())    
+
+        # Stairs object  
+        # Add stairs with platform and bridge
+        # self.stairsId = pyb.loadURDF(self.ENV_URDF)  
+        # pyb.changeDynamics(self.stairsId, -1, lateralFriction=1.0)
+        #pyb.setAdditionalSearchPath("/home/corberes/Bureau/edin/my_quad_reactive/quadruped-reactive-walking/scripts/solo3D/objects/")
+        # path_mesh = "solo3D/objects/object_" + str(self.object_stair) + "/meshes/"
+        # for elt in os.listdir(path_mesh) :
+        #     name_object = path_mesh + elt
+        
+        #     mesh_scale = [1.0, 1., 1.]
+        #     # Add stairs with platform and bridge
+        #     visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                         fileName=name_object,
+        #                                         rgbaColor=[.3, 0.3, 0.3, 1.0],
+        #                                         specularColor=[0.4, .4, 0],
+        #                                         visualFramePosition=[0.0, 0.0, 0.0],
+        #                                         meshScale=mesh_scale)
+
+        #     collisionShapeId = pyb.createCollisionShape(shapeType=pyb.GEOM_MESH,
+        #                                                             fileName=name_object,
+        #                                                             collisionFramePosition=[0.0, 0.0, 0.0],
+        #                                                             meshScale=mesh_scale)
+
+        #     tmpId = pyb.createMultiBody(baseMass=0.0,
+        #                                                 baseInertialFramePosition=[0, 0, 0],
+        #                                                 baseCollisionShapeIndex=collisionShapeId,
+        #                                                 baseVisualShapeIndex=visualShapeId,
+        #                                                 basePosition=[0.0, 0., 0.0],
+        #                                                 useMaximalCoordinates=True)
+        #     pyb.changeDynamics(tmpId, -1, lateralFriction=1.0)   
+        
+        # Sphere Object for target footsteps :
+        # for i in range(self.ftps_Ids_target.shape[0]):  # nb of feet target in futur
+
+        #     # rgbaColor : [R , B , G , alpha opacity]
+        #     if i == 0:
+        #         rgba = [0.41, 1., 0., 1.]
+
+        #     else:
+        #         rgba = [0.41, 1., 0., 0.5]
+
+        #     mesh_scale = [0.008, 0.008, 0.008]
+        #     visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                           fileName="sphere_smooth.obj",
+        #                                           halfExtents=[0.5, 0.5, 0.1],
+        #                                           rgbaColor=rgba,
+        #                                           specularColor=[0.4, .4, 0],
+        #                                           visualFramePosition=[0.0, 0.0, 0.0],
+        #                                           meshScale=mesh_scale)
+        #     for j in range(4):
+        #         self.ftps_Ids_target[i, j] = pyb.createMultiBody(baseMass=0.0,
+        #                                                          baseInertialFramePosition=[0, 0, 0],
+        #                                                          baseVisualShapeIndex=visualShapeId,
+        #                                                          basePosition=[0.0, 0.0, -0.1],
+        #                                                          useMaximalCoordinates=True)
+
+        # Sphere Object for trajcetories :
+        # for i in range(self.trajectory_Ids.shape[0]):
+
+        #     # rgbaColor : [R , B , G , alpha opacity]
+        #     if i == 0:
+        #         rgba = [0.41, 1., 0., 1.]
+        #     else:
+        #         rgba = [0.41, 1., 0., 0.5]
+
+        #     mesh_scale = [0.0035, 0.0035, 0.0035]
+        #     visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                           fileName="sphere_smooth.obj",
+        #                                           halfExtents=[0.5, 0.5, 0.1],
+        #                                           rgbaColor=rgba,
+        #                                           specularColor=[0.4, .4, 0],
+        #                                           visualFramePosition=[0.0, 0.0, 0.0],
+        #                                           meshScale=mesh_scale)
+        #     for j in range(4):
+        #         for id_t in range(self.n_points):
+        #             self.trajectory_Ids[i, j, id_t] = pyb.createMultiBody(baseMass=0.0,
+        #                                                                   baseInertialFramePosition=[0, 0, 0],
+        #                                                                   baseVisualShapeIndex=visualShapeId,
+        #                                                                   basePosition=[0.0, 0.0, -0.1],
+        #                                                                   useMaximalCoordinates=True)
+
+        # # Cube for planner trajectory
+        # for i in range(self.state_Ids.shape[0]):
+        #     # rgbaColor : [R , B , G , alpha opacity]
+        #     if i == 0:
+        #         rgba = [0., 0., 1., 1.]
+        #     else:
+        #         rgba = [0., 0., 1., 1.]
+
+        #     mesh_scale = [0.005, 0.005, 0.005]
+        #     visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                           fileName="sphere_smooth.obj",
+        #                                           halfExtents=[0.5, 0.5, 0.1],
+        #                                           rgbaColor=rgba,
+        #                                           specularColor=[0.4, .4, 0],
+        #                                           visualFramePosition=[0.0, 0.0, 0.0],
+        #                                           meshScale=mesh_scale)
+
+        #     self.state_Ids[i] = pyb.createMultiBody(baseMass=0.0,
+        #                                             baseInertialFramePosition=[0, 0, 0],
+        #                                             baseVisualShapeIndex=visualShapeId,
+        #                                             basePosition=[0.0, 0.0, -0.1],
+        #                                             useMaximalCoordinates=True)
+
+        # # Cube for surface Planner
+
+        # rgba = [0., 0., 1., 0.2]
+
+        # mesh_scale = [2*self.statePlanner.FIT_SIZE_X, 2*self.statePlanner.FIT_SIZE_Y, 0.001]
+        # visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                       fileName="cube.obj",
+        #                                       halfExtents=[0.5, 0.5, 0.1],
+        #                                       rgbaColor=rgba,
+        #                                       specularColor=[0.4, .4, 0],
+        #                                       visualFramePosition=[0.0, 0.0, 0.0],
+        #                                       meshScale=mesh_scale)
+
+        # self.surface_Id = pyb.createMultiBody(baseMass=0.0,
+        #                                                 baseInertialFramePosition=[0, 0, 0],
+        #                                                 baseVisualShapeIndex=visualShapeId,
+        #                                                 basePosition=[0.0, 0.0, -0.1],
+        #                                                 useMaximalCoordinates=True)
+
+        # Object for constraints : 
+        # n_effectors = 4 
+        # # kinematic_constraints_path = "/home/thomas_cbrs/Library/solo-rbprm/data/com_inequalities/feet_quasi_flat/"
+        # # limbs_names = ["FLleg" , "FRleg" , "HLleg" , "HRleg"]
+        # kinematic_constraints_path = os.getcwd() + "/solo3D/objects/constraints_sphere/"
+        # limbs_names = ["FL" , "FR" , "HL" , "HR"]
+        # for foot in range(n_effectors):
+        #     foot_name = limbs_names[foot]
+        #     # filekin = kinematic_constraints_path + "COM_constraints_in_" + \
+        #     #     foot_name + "_effector_frame_quasi_static_reduced.obj"
+        #     filekin = kinematic_constraints_path + "COM_constraints_" + \
+        #         foot_name + "3.obj"
+
+        #     mesh_scale = [1.,1.,1.] 
+        #     rgba = [0.,0.,1.,0.2]
+        #     visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                             fileName=filekin,
+        #                                             rgbaColor=rgba,   
+        #                                             specularColor=[0.4, .4, 0],
+        #                                             visualFramePosition=[0.0, 0.0, 0.0],
+        #                                             meshScale=mesh_scale)
+
+        #     self.constraints_objects[foot]  = pyb.createMultiBody(baseMass=0.0,
+        #                                                 baseInertialFramePosition=[0, 0, 0],
+        #                                                 baseVisualShapeIndex=visualShapeId,
+        #                                                 basePosition=[0.0, 0.0, -0.1],
+        #                                                 useMaximalCoordinates=True)
+
+        # Sphere Object for trajcetories :
+        # 5 phases + init pos
+        # for i in range(6):
+
+        #     # rgbaColor : [R , B , G , alpha opacity]
+        #     # if i == 0 :
+        #     #     rgba = [1., 0., 1., 1.]
+        #     # else :
+        #     #     rgba = [1., (1/7)*i, 0., 1.]
+
+        #     rgba_list = [[1.,0.,0.,1.],
+        #             [1.,0.,1.,1.],
+        #             [1.,1.,0.,1.],
+        #             [0.,0.,1.,1.]]
+            
+
+        #     mesh_scale = [0.01, 0.01, 0.01]
+            
+        #     for j in range(4):
+        #         rgba = rgba_list[j]
+        #         rgba[-1] = 1 - (1/9)*i
+        #         visualShapeId = pyb.createVisualShape(shapeType=pyb.GEOM_MESH,
+        #                                           fileName="sphere_smooth.obj",
+        #                                           halfExtents=[0.5, 0.5, 0.1],
+        #                                           rgbaColor=rgba,
+        #                                           specularColor=[0.4, .4, 0],
+        #                                           visualFramePosition=[0.0, 0.0, 0.0],
+        #                                           meshScale=mesh_scale)
+                
+                
+        #         self.sl1m_Ids_target[i, j] = pyb.createMultiBody(baseMass=0.0,
+        #                                                                 baseInertialFramePosition=[0, 0, 0],
+        #                                                                 baseVisualShapeIndex=visualShapeId,
+        #                                                                 basePosition=[0.0, 0.0, -0.1],
+        #                                                                 useMaximalCoordinates=True)
+
+        
+
+        print("pybullet object loaded")
+
+        return 0
\ No newline at end of file
diff --git a/src/config_solo12.yaml b/src/config_solo12.yaml
index ca0cef1a7fde5144a13b9420897d3a9839a63f35..01e8c291d8fd442e090e772d63ebb8dd2c023c47 100644
--- a/src/config_solo12.yaml
+++ b/src/config_solo12.yaml
@@ -11,7 +11,7 @@ robot:
     N_SIMULATION: 30000  # Number of simulated wbc time steps
     enable_pyb_GUI: true  # Enable/disable PyBullet GUI
     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
+    enable_multiprocessing: true  # Enable/disable running the MPC in another process in parallel of the main loop
     perfect_estimator: false  # Enable/disable perfect estimator by using data directly from PyBullet
     
     # General control parameters
@@ -22,7 +22,7 @@ robot:
     dt_mpc: 0.02  # Time step of the model predictive control
     T_gait: 0.48  # Duration of one gait period
     T_mpc: 0.48  # Duration of the prediction horizon
-    type_MPC: 1  # Which MPC solver you want to use: 0 for OSQP MPC, 1, 2, 3 for Crocoddyl MPCs
+    type_MPC: 3  # Which MPC solver you want to use: 0 for OSQP MPC, 1, 2, 3 for Crocoddyl MPCs
     kf_enabled: false  # Use complementary filter (False) or kalman filter (True) for the estimator 
     Kp_main: 6.0  # Proportional gains for the PD+
     Kd_main: 0.3  # Derivative gains for the PD+