From 593c56dc8f45fc9501091fe945e35b5992a1d5b9 Mon Sep 17 00:00:00 2001 From: odri <odri@furano.laas.fr> Date: Fri, 2 Sep 2022 17:16:08 +0200 Subject: [PATCH] [EXPE] com circle --- config/walk_parameters.yaml | 16 ++-- .../quadruped_reactive_walking/Controller.py | 50 +++++++++++-- .../WB_MPC/CrocoddylOCP.py | 33 ++++++--- .../WB_MPC/ProblemData.py | 20 ++--- .../WB_MPC/Target.py | 49 ++++++++++--- .../WB_MPC_Wrapper.py | 43 ++++++----- .../tools/LoggerControl.py | 73 +++++++++++++++---- 7 files changed, 202 insertions(+), 82 deletions(-) diff --git a/config/walk_parameters.yaml b/config/walk_parameters.yaml index c4e6cf0b..06e62a66 100644 --- a/config/walk_parameters.yaml +++ b/config/walk_parameters.yaml @@ -6,14 +6,14 @@ robot: LOGGING: true # Enable/disable logging during the experiment PLOTTING: true # Enable/disable automatic plotting at the end of the experiment DEMONSTRATION: false # Enable/disable demonstration functionalities - SIMULATION: true # Enable/disable PyBullet simulation or running on real robot - enable_pyb_GUI: true # Enable/disable PyBullet GUI + SIMULATION: false # Enable/disable PyBullet simulation or running on real robot + enable_pyb_GUI: false # Enable/disable PyBullet GUI 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: 5000 # Number of simulated wbc time steps + N_SIMULATION: 10000 # Number of simulated wbc time steps enable_corba_viewer: false # Enable/disable Corba Viewer - enable_multiprocessing: true # 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 @@ -25,18 +25,18 @@ robot: dt_mpc: 0.01 # 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 save_guess: false # true to interpolate the impedance quantities between nodes of the MPC - movement: "" # name of the movement to perform + movement: "base_circle" # name of the movement to perform interpolate_mpc: true # true to interpolate the impedance quantities between nodes of the MPC interpolation_type: 3 # 0,1,2,3 decide which kind of interpolation is used - closed_loop: false # true to close the loop on the MPC - Kp_main: [3, 3, 3] # Proportional gains for the PD+ + closed_loop: true # true to close the loop on the MPC + Kp_main: [2, 2, 2] # Proportional gains for the PD+ Kd_main: [0.3, 0.3, 0.3] # Derivative gains for the PD+ Kff_main: 1.0 # Feedforward torques multiplier for the PD+ # Parameters of Gait N_periods: 1 gait: [16, 1, 1, 1, 1, - 16, 1, 0, 1, 1] + 16, 1, 1, 1, 1] # Parameters of Joystick gp_alpha_vel: 0.003 #Â Coefficient of the low pass filter applied to gamepad velocity diff --git a/python/quadruped_reactive_walking/Controller.py b/python/quadruped_reactive_walking/Controller.py index 17a11a4b..e8a32a82 100644 --- a/python/quadruped_reactive_walking/Controller.py +++ b/python/quadruped_reactive_walking/Controller.py @@ -28,6 +28,7 @@ class Result: self.v_des = np.zeros(12) self.tau_ff = np.zeros(12) + class DummyDevice: def __init__(self, h): self.imu = self.IMU() @@ -86,13 +87,21 @@ class Controller: self.target = Target(params, foot_pose) self.footsteps = [] + self.base_refs = [] for k in range(self.params.T * self.params.mpc_wbc_ratio): - self.target_footstep = self.target.compute(k).copy() + if params.movement == "base_circle": + self.target_base = self.target.compute(k).copy() + self.target_footstep = np.zeros((3, 4)) + else: + self.target_footstep = self.target.compute(k).copy() + self.target_base = np.zeros(3) + if k % self.params.mpc_wbc_ratio == 0: + self.base_refs.append(self.target_base.copy()) self.footsteps.append(self.target_footstep.copy()) self.mpc = WB_MPC_Wrapper.MPC_Wrapper( - self.pd, params, self.footsteps, self.gait + self.pd, params, self.footsteps, self.base_refs, self.gait ) self.mpc_solved = False self.k_result = 0 @@ -109,6 +118,11 @@ class Controller: self.us_init = None print("No initial guess\n") + self.filter_q = qrw.Filter() + self.filter_q.initialize(params) + self.filter_v = qrw.Filter() + self.filter_v.initialize(params) + device = DummyDevice(params.h_ref) device.joints.positions = q_init self.compute(device) @@ -127,9 +141,16 @@ class Controller: t_measures = time.time() self.t_measures = t_measures - t_start - self.target_footstep = self.target.compute( - self.k + self.params.T * self.params.mpc_wbc_ratio - ) + if self.params.movement == "base_circle": + self.target_base = self.target.compute( + self.k + self.params.T * self.params.mpc_wbc_ratio + ) + self.target_footstep = np.zeros((3, 4)) + else: + self.target_base = np.zeros(3) + self.target_footstep = self.target.compute( + self.k + self.params.T * self.params.mpc_wbc_ratio + ) if self.k % self.params.mpc_wbc_ratio == 0: if self.mpc_solved: @@ -142,6 +163,7 @@ class Controller: self.k, self.x, self.target_footstep.copy(), + self.target_base.copy(), self.gait, self.xs_init, self.us_init, @@ -155,6 +177,7 @@ class Controller: self.k, self.mpc_result.xs[1], self.target_footstep.copy(), + self.target_base.copy(), self.gait, self.xs_init, self.us_init, @@ -341,7 +364,7 @@ class Controller: self.estimator.run( self.gait, - footstep.reshape((3, 4), order='F'), + footstep.reshape((3, 4), order="F"), device.imu.linear_acceleration, device.imu.gyroscope, device.imu.attitude_euler, @@ -361,6 +384,7 @@ class Controller: self.v_ref = self.estimator.get_base_vel_ref() self.h_v = self.estimator.get_h_v() self.h_v_windowed = self.estimator.get_h_v_filtered() + self.v_windowed = self.estimator.get_v_filtered() self.q_estimate = self.estimator.get_q_estimate() self.v_estimate = self.estimator.get_v_estimate() @@ -373,7 +397,19 @@ class Controller: self.v = self.estimator.get_v_reference() - self.x = np.concatenate([self.q_estimate, self.v_estimate]) + self.base_position_filtered = self.filter_q.filter(self.q[:6], True) + + self.q_filtered = self.q_estimate.copy() + self.q_filtered[:3] = self.base_position_filtered[:3] + self.q_filtered[3:7] = pin.Quaternion( + pin.rpy.rpyToMatrix(self.base_position_filtered[3:]) + ).coeffs() + self.v_filtered = self.v_estimate.copy() + # self.v_filtered[:6] = np.zeros(6) + # self.v_filtered[:6] = self.filter_v.filter(self.v_windowed, False) + self.v_filtered[:6] = self.filter_v.filter(self.v_estimate[:6], False) + + self.x = np.concatenate([self.q_filtered, self.v_filtered]) return oRh, hRb, oTh def compute_torque(self): diff --git a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py index 9543ec26..0a41fdc7 100644 --- a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py +++ b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py @@ -10,7 +10,7 @@ from time import time class OCP: - def __init__(self, pd: ProblemData, params, footsteps, gait): + def __init__(self, pd: ProblemData, params, footsteps, base_refs, gait): self.pd = pd self.params = params self.max_iter = 1000 if params.save_guess else 1 @@ -21,17 +21,17 @@ class OCP: self.t_update_last_model = 0.0 self.t_shift = 0.0 - rm, tm = self.initialize_models(gait, footsteps) + rm, tm = self.initialize_models(gait, footsteps, base_refs) self.x0 = self.pd.x0 self.problem = crocoddyl.ShootingProblem(self.x0, rm, tm) self.ddp = crocoddyl.SolverFDDP(self.problem) - def initialize_models(self, gait, footsteps): + def initialize_models(self, gait, footsteps, base_refs): models = [] for t, step in enumerate(gait[:-1]): - tasks = self.make_task(footsteps[t]) + tasks = self.make_task(footsteps[t], base_refs[t]) support_feet = [self.pd.feet_ids[i] for i in np.nonzero(step == 1)[0]] models.append(self.create_model(support_feet, tasks)) @@ -40,10 +40,10 @@ class OCP: return models, terminal_model - def solve(self, x0, footstep, gait, xs_init=None, us_init=None): + def solve(self, x0, footstep, base_ref, gait, xs_init=None, us_init=None): t_start = time() self.x0 = x0 - self.make_ocp(footstep, gait) + self.make_ocp(footstep, base_ref, gait) t_update = time() self.t_update = t_update - t_start @@ -66,7 +66,7 @@ class OCP: self.t_solve = time() - t_start - def make_ocp(self, footstep, gait): + def make_ocp(self, footstep, base_ref, gait): """ Create a shooting problem for a simple walking gait. @@ -76,7 +76,7 @@ class OCP: pin.updateFramePlacements(self.pd.model, self.pd.rdata) if self.initialized: - tasks = self.make_task(footstep) + tasks = self.make_task(footstep, base_ref) support_feet = [self.pd.feet_ids[i] for i in np.nonzero(gait[-1] == 1)[0]] self.update_model(self.problem.runningModels[0], tasks, support_feet) @@ -89,8 +89,11 @@ class OCP: self.initialized = True - def make_task(self, footstep): + def make_task(self, footstep, base_ref): task = [[], []] + if base_ref is not None: + task[0].append(self.pd.base_id) + task[1].append(base_ref) for foot in range(4): if footstep[:, foot].any(): task[0].append(self.pd.feet_ids[foot]) @@ -219,7 +222,7 @@ class OCP: nu = model.differential.actuation.nu costs = model.differential.costs for i in self.pd.feet_ids: - cone = crocoddyl.FrictionCone(self.pd.Rsurf, self.pd.mu, 4, False, 3) + cone = crocoddyl.FrictionCone(self.pd.Rsurf, self.pd.mu, 4, False, min_nforce=3.) residual = crocoddyl.ResidualModelContactFrictionCone( self.state, i, cone, nu ) @@ -241,6 +244,10 @@ class OCP: costs.addCost(name, foot_tracking, self.pd.foot_tracking_w) costs.changeCostStatus(name, False) + + residual = crocoddyl.ResidualModelFrameTranslation(self.state, self.pd.base_id, np.zeros(3), nu) + base_tracking = crocoddyl.CostModelResidual(self.state, residual) + costs.addCost("base_tracking", base_tracking, self.pd.base_tracking_w) control_residual = crocoddyl.ResidualModelControl(self.state, self.pd.uref) control_reg = crocoddyl.CostModelResidual(self.state, control_residual) @@ -259,11 +266,13 @@ class OCP: def update_tracking_costs(self, costs, tasks, support_feet): index = 0 + if tasks[0][0] == self.pd.base_id: + costs.costs["base_tracking"].cost.residual.reference = tasks[1][index] + index += 1 + for i in self.pd.feet_ids: name = self.pd.model.frames[i].name + "_foot_tracking" if i in tasks[0]: costs.costs[name].cost.residual.reference = tasks[1][index] index += 1 costs.changeCostStatus(name, i not in support_feet) - - # print(f"{name} reference: {costs.costs[name].cost.residual.reference} status:{i in tasks[0]}") diff --git a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py index 48e91ad3..b5048ac7 100644 --- a/python/quadruped_reactive_walking/WB_MPC/ProblemData.py +++ b/python/quadruped_reactive_walking/WB_MPC/ProblemData.py @@ -32,7 +32,8 @@ class problemDataAbstract: self.v0 = np.zeros(18) self.x0 = np.concatenate([self.q0, self.v0]) - self.u0 = np.zeros(self.nu) + + self.u0 = np.array([0.0, 0.0, 9.81 * params.mass / 4.0] * 4) self.baumgarte_gains = np.array([0, 100]) @@ -59,6 +60,7 @@ class ProblemData(problemDataAbstract): super().__init__(params) self.useFixedBase = 0 + self.base_id = self.model.getFrameId("base_link") # Cost function weights self.mu = 0.7 @@ -66,23 +68,23 @@ class ProblemData(problemDataAbstract): if params.movement == "step": self.foot_tracking_w = 2.0 * 1e3 else: - self.foot_tracking_w = 1.5 * 1e1 - self.friction_cone_w = 1e4 - self.control_bound_w = 1e3 + self.foot_tracking_w = 0. + self.base_tracking_w = 1e5 + self.friction_cone_w = 0.0 # 1e4 + self.control_bound_w = 0. self.control_reg_w = 1e0 self.state_reg_w = np.array( [0] * 3 + [1e1] * 3 + [1e1] * 3 - + [1e-3] * 3 + + [1e1] * 3 + [1e1] * 6 + [0] * 6 - + [1e1] * 3 + [1e-1] * 3 - + [1e1] * 6 + + [1e-1] * 3 + + [1e-1] * 6 ) - self.terminal_velocity_w = np.array([0] * self.nv + [1e4] * self.nv) - self.force_reg_w = 1e0 + self.terminal_velocity_w = np.array([0] * self.nv + [0.] * self.nv) self.xref = self.x0 self.uref = self.u0 diff --git a/python/quadruped_reactive_walking/WB_MPC/Target.py b/python/quadruped_reactive_walking/WB_MPC/Target.py index e28de2df..10d383b3 100644 --- a/python/quadruped_reactive_walking/WB_MPC/Target.py +++ b/python/quadruped_reactive_walking/WB_MPC/Target.py @@ -11,7 +11,16 @@ class Target: self.dt_wbc = params.dt_wbc self.k_per_step = 160 - if params.movement == "circle": + if params.movement == "base_circle": + self.initial_base = np.array([0.0, 0.0, params.h_ref]) + self.A = np.array([0.02, 0.0, 0.0]) + self.offset = np.array([0.0, 0.0, 0.0]) + self.freq = np.array([0.5, 0.5, 0.0]) + self.phase = np.array([0.0, 0.0, 0.0]) + elif params.movement == "circle": + self.position = np.array(params.footsteps_init.tolist()).reshape( + (3, 4), order="F" + ) self.A = np.array([0.05, 0.0, 0.04]) self.offset = np.array([0.05, 0, 0.05]) self.freq = np.array([0.5, 0.0, 0.5]) @@ -31,26 +40,42 @@ class Target: self.params.footsteps_init.tolist() ).reshape((3, 4), order="F") self.ramp_length = 100 - self.target_ramp = np.linspace(0.0, 0.1, self.ramp_length) + self.target_ramp_x = np.linspace(0.0, -0.0, self.ramp_length) + self.target_ramp_y = np.linspace(0.0, 0.0, self.ramp_length) + self.target_ramp_z = np.linspace(0.0, 0.05, self.ramp_length) def compute(self, k): footstep = np.zeros((3, 4)) - if self.params.movement == "circle": - footstep[:, 1] = self.evaluate_circle(k) + if self.params.movement == "base_circle": + target = self.evaluate_circle(k, self.initial_base) + elif self.params.movement == "circle": + target[:, 1] = self.evaluate_circle(k, self.position[:, 1]) elif self.params.movement == "step": - footstep[:, 1] = self.evaluate_step(1, k) - footstep[2, 1] += 0.015 + target[:, 1] = self.evaluate_step(1, k) + target[2, 1] += 0.015 else: - footstep = self.target_footstep.copy() - footstep[2, 1] = ( - self.target_ramp[k] if k < self.ramp_length else self.target_ramp[-1] + target = self.target_footstep.copy() + target[0, 1] = ( + self.target_ramp_x[k] + if k < self.ramp_length + else self.target_ramp_x[-1] + ) + target[1, 1] = ( + self.target_ramp_y[k] + if k < self.ramp_length + else self.target_ramp_y[-1] + ) + target[2, 1] = ( + self.target_ramp_z[k] + if k < self.ramp_length + else self.target_ramp_z[-1] ) - return footstep + return target - def evaluate_circle(self, k): + def evaluate_circle(self, k, initial_position): return ( - self.position[:, 1] + initial_position + self.offset + self.A * np.sin(2 * np.pi * self.freq * k * self.dt_wbc + self.phase) ) diff --git a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py index 2c7e092e..5fd83da5 100644 --- a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py +++ b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py @@ -26,7 +26,7 @@ class MPC_Wrapper: a parallel process """ - def __init__(self, pd, params, footsteps, gait): + def __init__(self, pd, params, footsteps, base_refs, gait): self.params = params self.pd = pd self.T = params.T @@ -34,12 +34,14 @@ class MPC_Wrapper: self.nx = pd.nx self.ndx = pd.ndx - self.footsteps_plan = footsteps self.initial_gait = gait self.multiprocessing = params.enable_multiprocessing if self.multiprocessing: + self.footsteps_plan = footsteps + self.base_refs = base_refs + self.new_data = Value("b", False) self.running = Value("b", True) self.in_k = Value("i", 0) @@ -48,18 +50,19 @@ class MPC_Wrapper: self.in_xs = Array("d", [0] * ((self.T + 1) * self.nx)) self.in_us = Array("d", [0] * (self.T * self.nu)) self.in_footstep = Array("d", [0] * 12) + self.in_base_ref = Array("d", [0] * 3) self.in_gait = Array("d", [0] * ((self.T + 1) * 4)) self.out_xs = Array("d", [0] * ((self.T + 1) * self.nx)) self.out_us = Array("d", [0] * (self.T * self.nu)) self.out_k = Array("d", [0] * (self.T * self.nu * self.ndx)) self.out_solving_time = Value("d", 0.0) else: - self.ocp = OCP(pd, params, footsteps, gait) + self.ocp = OCP(pd, params, footsteps, base_refs, gait) self.last_available_result = Result(pd, params) self.new_result = Value("b", False) - def solve(self, k, x0, footstep, gait, xs=None, us=None): + def solve(self, k, x0, footstep, base_ref, gait, xs=None, us=None): """ Call either the asynchronous MPC or the synchronous MPC depending on the value of multiprocessing during the creation of the wrapper @@ -68,9 +71,9 @@ class MPC_Wrapper: k (int): Number of inv dynamics iterations since the start of the simulation """ if self.multiprocessing: - self.run_MPC_asynchronous(k, x0, footstep, gait, xs, us) + self.run_MPC_asynchronous(k, x0, footstep, base_ref, gait, xs, us) else: - self.run_MPC_synchronous(x0, footstep, gait, xs, us) + self.run_MPC_synchronous(x0, footstep, base_ref, gait, xs, us) def get_latest_result(self): """ @@ -95,11 +98,11 @@ class MPC_Wrapper: return self.last_available_result - def run_MPC_synchronous(self, x0, footstep, gait, xs, us): + def run_MPC_synchronous(self, x0, footstep, base_ref, gait, xs, us): """ Run the MPC (synchronous version) """ - self.ocp.solve(x0, footstep, gait, xs, us) + self.ocp.solve(x0, footstep, base_ref, gait, xs, us) ( self.last_available_result.xs, self.last_available_result.us, @@ -108,7 +111,7 @@ class MPC_Wrapper: ) = self.ocp.get_results() self.new_result.value = True - def run_MPC_asynchronous(self, k, x0, footstep, gait, xs, us): + def run_MPC_asynchronous(self, k, x0, footstep, base_ref, gait, xs, us): """ Run the MPC (asynchronous version) """ @@ -117,7 +120,7 @@ class MPC_Wrapper: p = Process(target=self.MPC_asynchronous) p.start() - self.add_new_data(k, x0, footstep, gait, xs, us) + self.add_new_data(k, x0, footstep, base_ref, gait, xs, us) def MPC_asynchronous(self): """ @@ -129,29 +132,29 @@ class MPC_Wrapper: self.new_data.value = False - k, x0, footstep, gait, xs, us = self.decompress_dataIn() + k, x0, footstep, base_ref, gait, xs, us = self.decompress_dataIn() if k == 0: loop_ocp = OCP( - self.pd, self.params, self.footsteps_plan, self.initial_gait + self.pd, self.params, self.footsteps_plan, self.base_refs, self.initial_gait ) - loop_ocp.solve(x0, footstep, gait, xs, us) + loop_ocp.solve(x0, footstep, base_ref, gait, xs, us) xs, us, K, solving_time = loop_ocp.get_results() self.compress_dataOut(xs, us, K, solving_time) self.new_result.value = True - def add_new_data(self, k, x0, footstep, gait, xs, us): + def add_new_data(self, k, x0, footstep, base_ref, gait, xs, us): """ 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 that there is a new data """ - self.compress_dataIn(k, x0, footstep, gait, xs, us) + self.compress_dataIn(k, x0, footstep, base_ref, gait, xs, us) self.new_data.value = True - def compress_dataIn(self, k, x0, footstep, gait, xs, us): + def compress_dataIn(self, k, x0, footstep, base_ref, gait, xs, us): """ Decompress data from a C-type structure that belongs to the shared memory to retrieve data from the main control loop in the asynchronous MPC @@ -163,6 +166,8 @@ class MPC_Wrapper: np.frombuffer(self.in_x0.get_obj()).reshape(self.nx)[:] = x0 with self.in_footstep.get_lock(): np.frombuffer(self.in_footstep.get_obj()).reshape((3, 4))[:, :] = footstep + with self.in_base_ref.get_lock(): + np.frombuffer(self.in_base_ref.get_obj()).reshape(3)[:] = base_ref with self.in_gait.get_lock(): np.frombuffer(self.in_gait.get_obj()).reshape((self.T + 1, 4))[:, :] = gait @@ -191,11 +196,13 @@ class MPC_Wrapper: x0 = np.frombuffer(self.in_x0.get_obj()).reshape(self.nx) with self.in_footstep.get_lock(): footstep = np.frombuffer(self.in_footstep.get_obj()).reshape((3, 4)) + with self.in_base_ref.get_lock(): + base_ref = np.frombuffer(self.in_base_ref.get_obj()).reshape(3) with self.in_gait.get_lock(): gait = np.frombuffer(self.in_gait.get_obj()).reshape((self.T + 1, 4)) if not self.in_warm_start.value: - return k, x0, footstep, gait, None, None + return k, x0, footstep, base_ref, gait, None, None with self.in_xs.get_lock(): xs = list( @@ -204,7 +211,7 @@ class MPC_Wrapper: with self.in_us.get_lock(): us = list(np.frombuffer(self.in_us.get_obj()).reshape((self.T, self.nu))) - return k, x0, footstep, gait, xs, us + return k, x0, footstep, base_ref, gait, xs, us def compress_dataOut(self, xs, us, K, solving_time): """ diff --git a/python/quadruped_reactive_walking/tools/LoggerControl.py b/python/quadruped_reactive_walking/tools/LoggerControl.py index 1f3e02e1..4d0d92ad 100644 --- a/python/quadruped_reactive_walking/tools/LoggerControl.py +++ b/python/quadruped_reactive_walking/tools/LoggerControl.py @@ -55,8 +55,11 @@ class LoggerControl: self.t_ocp_solve = np.zeros(size) # MPC - self.q = np.zeros([size, pd.nq]) - self.v = np.zeros([size, pd.nv]) + self.q_estimate_rpy = np.zeros([size, pd.nq - 1]) + self.q_estimate = np.zeros([size, pd.nq]) + self.v_estimate = np.zeros([size, pd.nv]) + self.q_filtered = np.zeros([size, pd.nq]) + self.v_filtered = np.zeros([size, pd.nv]) self.ocp_xs = np.zeros([size, params.T + 1, pd.nx]) self.ocp_us = np.zeros([size, params.T, pd.nu]) self.ocp_K = np.zeros([size, self.pd.nu, self.pd.ndx]) @@ -64,6 +67,7 @@ class LoggerControl: self.MPC_equivalent_Kd = np.zeros([size, self.pd.nu]) self.target = np.zeros([size, 3]) + self.target_base = np.zeros([size, 3]) # Whole body control self.wbc_P = np.zeros([size, 12]) # proportionnal gains of the PD+ @@ -108,8 +112,11 @@ class LoggerControl: self.t_measures[self.i] = controller.t_measures # Logging from model predictive control - self.q[self.i] = np.array(controller.q_estimate) - self.v[self.i] = np.array(controller.v_estimate) + self.q_estimate_rpy[self.i] = np.array(controller.q) + self.q_estimate[self.i] = np.array(controller.q_estimate) + self.v_estimate[self.i] = np.array(controller.v_estimate) + self.q_filtered[self.i] = np.array(controller.q_filtered) + self.v_filtered[self.i] = np.array(controller.v_filtered) self.ocp_xs[self.i] = np.array(controller.mpc_result.xs) self.ocp_us[self.i] = np.array(controller.mpc_result.us) self.ocp_K[self.i] = controller.mpc_result.K[0] @@ -128,10 +135,16 @@ class LoggerControl: self.target[i] = controller.footsteps[i // self.params.mpc_wbc_ratio][ :, 1 ] + self.target_base[i] = controller.base_refs[ + i // self.params.mpc_wbc_ratio + ] if self.i + self.params.T * self.params.mpc_wbc_ratio < self.log_size: self.target[ self.i + self.params.T * self.params.mpc_wbc_ratio ] = controller.target_footstep[:, 1] + self.target_base[ + self.i + self.params.T * self.params.mpc_wbc_ratio + ] = controller.target_base[:] if not self.params.enable_multiprocessing: self.t_ocp_update[self.i] = controller.mpc.ocp.t_update @@ -223,20 +236,38 @@ class LoggerControl: def plot_target(self, save=False, fileName="/tmp"): import matplotlib.pyplot as plt - x_mes = np.concatenate([self.q, self.v], axis=1) + x = np.concatenate([self.q_filtered, self.v_filtered], axis=1) m_feet_p_log = { - idx: get_translation_array(self.pd, x_mes, idx)[0] - for idx in self.pd.feet_ids + idx: get_translation_array(self.pd, x, idx)[0] for idx in self.pd.feet_ids } # Target plot - _, axs = plt.subplots(3, sharex=True) + _, axs = plt.subplots(3, 2, sharex=True) legend = ["x", "y", "z"] for p in range(3): - axs[p].set_title("Base position on " + legend[p]) - axs[p].plot([self.pd.xref[p]] * self.log_size) - axs[p].plot(self.q[:, p]) - axs[p].legend(["Target", "Measured"]) + axs[p, 0].set_title("Base position on " + legend[p]) + axs[p, 0].plot(self.target_base[:, p]) + axs[p, 0].plot(self.q_estimate[:, p]) + axs[p, 0].plot(self.q_filtered[:, p]) + axs[p, 0].legend(["Target", "Estimated", "Filtered"]) + + axs[p, 1].set_title("Base rotation on " + legend[p]) + axs[p, 1].plot(self.q_estimate_rpy[:, 3 + p]) + axs[p, 1].legend(["Estimated"]) + + _, axs = plt.subplots(3, 2, sharex=True) + legend = ["x", "y", "z"] + for p in range(3): + axs[p, 0].set_title("Base velocity on " + legend[p]) + axs[p, 0].plot([self.pd.xref[19 + p]] * self.log_size) + axs[p, 0].plot(self.v_estimate[:, p]) + axs[p, 0].plot(self.v_filtered[:, p]) + axs[p, 0].legend(["Target", "Estimated", "Filtered"]) + + axs[p, 1].set_title("Base angular velocity on " + legend[p]) + axs[p, 1].plot(self.v_estimate[:, 3 + p]) + axs[p, 1].plot(self.v_filtered[:, 3 + p]) + axs[p, 1].legend(["Estimated", "Filtered"]) _, axs = plt.subplots(3, sharex=True) legend = ["x", "y", "z"] @@ -317,8 +348,13 @@ class LoggerControl: np.savez_compressed( name, - q=self.q, - v=self.v, + target=self.target, + target_base=self.target_base, + q_estimate_rpy=self.q_estimate_rpy, + q_estimate=self.q_estimate, + v_estimate=self.v_estimate, + q_filtered=self.q_filtered, + v_filtered=self.v_filtered, ocp_xs=self.ocp_xs, ocp_us=self.ocp_us, ocp_K=self.ocp_K, @@ -364,6 +400,8 @@ class LoggerControl: return # Load sensors arrays + self.target = self.data["target"] + self.target_base = self.data["target_base"] self.q_mes = self.data["q_mes"] self.v_mes = self.data["v_mes"] self.baseOrientation = self.data["baseOrientation"] @@ -389,8 +427,11 @@ class LoggerControl: self.t_loop = self.data["t_loop"] self.t_measures = self.data["t_meausres"] - self.q = self.data["q"] - self.v = self.data["v"] + self.q_estimate_rpy = self.data["q_estimate_rpy"] + self.q_estimate = self.data["q_estimate"] + self.v_estimate = self.data["v_estimate"] + self.q_filtered = self.data["q_filtered"] + self.v_filtered = self.data["v_filtered"] self.ocp_xs = self.data["ocp_xs"] self.ocp_us = self.data["ocp_us"] self.ocp_K = self.data["ocp_K"] -- GitLab