diff --git a/python/quadruped_reactive_walking/Controller.py b/python/quadruped_reactive_walking/Controller.py index e07c02c02d3ba4ba9ae6a7618c9139e84a4e2c43..47c0614edf1b1e90f76dc7d0e03e84f414fe5e4e 100644 --- a/python/quadruped_reactive_walking/Controller.py +++ b/python/quadruped_reactive_walking/Controller.py @@ -73,16 +73,20 @@ class Controller: device = DummyDevice() device.joints.positions = q_init try: - file = np.load('/tmp/init_guess.npy', allow_pickle=True).item() - self.guess = {"xs": list(file["xs"]), "us": list(file["us"])} - print("\nInitial guess loaded\n") + file = np.load("/tmp/init_guess.npy", allow_pickle=True).item() + self.xs_init = list(file["xs"]) + self.us_init = list(file["us"]) + print("Initial guess loaded \n") except: - self.guess = {} - print("\nNo initial guess\n") - # self.compute(device) + self.xs_init = None + self.us_init = None + print("No initial guess\n") + + self.compute(device) def compute(self, device, qc=None): - """Run one iteration of the main control loop + """ + Run one iteration of the main control loop Args: device (object): Interface with the masterboard or the simulation @@ -96,19 +100,14 @@ class Controller: self.point_target = self.target.evaluate_in_t(1)[self.pd.rfFootId] try: - #self.mpc.solve(self.k, m["x_m"], self.guess) # Closed loop mpc + # Closed-loop + # self.mpc.solve(self.k, m["x_m"], self.xs_init, self.us_init) - ## Trajectory tracking + # Trajectory tracking if self.initialized: - self.mpc.solve(self.k, self.mpc_result.x[1], self.guess) + self.mpc.solve(self.k, self.mpc_result.xs[1], self.xs_init, self.us_init) else: - self.mpc.solve(self.k, m["x_m"], self.guess) - - ### ONLY IF YOU WANT TO STORE THE FIRST SOLUTION TO WARMSTART THE INITIAL Problem ### - #if not self.initialized: - # np.save(open('/tmp/init_guess.npy', "wb"), {"xs": self.mpc.ocp.get_results().x, "us": self.mpc.ocp.get_results().u} ) - # print("Initial guess saved") - + self.mpc.solve(self.k, m["x_m"], self.xs_init, self.us_init) except ValueError: self.error = True print("MPC Problem") @@ -117,7 +116,12 @@ class Controller: self.t_mpc = t_mpc - t_measures if not self.error: - self.mpc_result, self.mpc_cost = self.mpc.get_latest_result() + self.mpc_result = self.mpc.get_latest_result() + + ### ONLY IF YOU WANT TO STORE THE FIRST SOLUTION TO WARM-START THE INITIAL Problem ### + # if not self.initialized: + # np.save(open('/tmp/init_guess.npy', "wb"), {"xs": self.mpc_result.xs, "us": self.mpc_result.us} ) + # print("Initial guess saved") # self.result.P = np.array(self.params.Kp_main.tolist() * 4) self.result.P = np.array([5] * 3 + [1] * 3 + [5] * 6) @@ -138,8 +142,8 @@ class Controller: self.result.v_des = self.mpc_result.v[1] self.result.tau_ff = np.zeros(12) - self.guess["xs"] = self.mpc_result.x[1:] + [self.mpc_result.x[-1]] - self.guess["us"] = self.mpc_result.u[1:] + [self.mpc_result.u[-1]] + self.xs_init = self.mpc_result.x[1:] + [self.mpc_result.x[-1]] + self.us_init = self.mpc_result.u[1:] + [self.mpc_result.u[-1]] t_send = time.time() self.t_send = t_send - t_mpc diff --git a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py index 431912fc4266daa792563bf1bd02c9a8e1e57c2e..97409d1aa8b2e7649c6ff90e71c32935f3f2bec1 100644 --- a/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py +++ b/python/quadruped_reactive_walking/WB_MPC/CrocoddylOCP.py @@ -2,7 +2,6 @@ from tracemalloc import start from .ProblemData import ProblemData from .Target import Target -from .OcpResult import OcpResult import crocoddyl import pinocchio as pin import numpy as np @@ -14,7 +13,6 @@ class OCP: self.pd = pd self.target = target - self.results = OcpResult() self.state = crocoddyl.StateMultibody(self.pd.model) self.initialized = False self.t_problem_update = 0 @@ -27,16 +25,18 @@ class OCP: ) self.ddp = crocoddyl.SolverFDDP(self.problem) - self.t_update_last_model = 0 - self.t_shift = 0 - def initialize_models(self): self.nodes = [] for t in range(self.pd.T): - task = self.make_task(self.target.evaluate_in_t(t), self.target.contactSequence[t]) + task = self.make_task( + self.target.evaluate_in_t(t), self.target.contactSequence[t] + ) self.nodes.append( - Node(self.pd, self.state, self.target.contactSequence[t], task)) - self.terminal_node = Node(self.pd, self.state, self.target.contactSequence[self.pd.T], isTerminal=True) + Node(self.pd, self.state, self.target.contactSequence[t], task) + ) + self.terminal_node = Node( + self.pd, self.state, self.target.contactSequence[self.pd.T], isTerminal=True + ) self.models = [node.model for node in self.nodes] self.terminal_model = self.terminal_node.model @@ -56,15 +56,20 @@ class OCP: t_FK = time() self.t_FK = t_FK - t_start - + if self.initialized: - task = self.make_task(self.target.evaluate_in_t(self.pd.T-1), self.target.contactSequence[self.pd.T-1]) # model without contact for this task - self.nodes[0].update_model(self.target.contactSequence[self.pd.T-1], task) + task = self.make_task( + self.target.evaluate_in_t(self.pd.T - 1), + self.target.contactSequence[self.pd.T - 1], + ) # model without contact for this task + self.nodes[0].update_model(self.target.contactSequence[self.pd.T - 1], task) t_update_last_model = time() self.t_update_last_model = t_update_last_model - t_FK - self.problem.circularAppend(self.nodes[0].model, self.nodes[0].model.createData()) + self.problem.circularAppend( + self.nodes[0].model, self.nodes[0].model.createData() + ) t_shift = time() self.t_shift = t_shift - t_update_last_model @@ -76,7 +81,7 @@ class OCP: self.initialized = True - def solve(self, x0, guess=None): + def solve(self, x0, xs_init=None, us_init=None): self.x0 = x0 @@ -87,17 +92,17 @@ class OCP: t_update = time() self.t_update = t_update - t_start - if not guess: + if xs_init is None or us_init is None: xs = [x0] * (self.ddp.problem.T + 1) us = self.ddp.problem.quasiStatic([x0] * self.ddp.problem.T) else: - xs = guess["xs"] - us = guess["us"] + xs = xs_init + us = us_init t_warm_start = time() self.t_warm_start = t_warm_start - t_update - #self.ddp.setCallbacks([crocoddyl.CallbackVerbose()]) + # self.ddp.setCallbacks([crocoddyl.CallbackVerbose()]) self.ddp.solve(xs, us, 1, False) t_ddp = time() @@ -118,10 +123,12 @@ class OCP: return [idf for idf in self.pd.allContactIds if idf not in contactIds] def get_results(self): - self.results.x = self.ddp.xs.tolist() - self.results.u = self.ddp.us.tolist() - self.results.K = self.ddp.K - return self.results + return ( + self.ddp.xs.tolist().copy(), + self.ddp.us.tolist().copy(), + self.ddp.K.copy(), + self.t_ddp, + ) def get_croco_forces(self): d = self.ddp.problem.runningDatas[0] @@ -152,13 +159,14 @@ class OCP: def get_croco_acc(self): acc = [] - [acc.append(m.differential.xout) - for m in self.ddp.problem.runningDatas] + [acc.append(m.differential.xout) for m in self.ddp.problem.runningDatas] return acc class Node: - def __init__(self, pd, state, supportFootIds=[], swingFootTask = [], isTerminal=False): + def __init__( + self, pd, state, supportFootIds=[], swingFootTask=[], isTerminal=False + ): self.pd = pd self.isTerminal = isTerminal @@ -167,15 +175,14 @@ class Node: self.actuation = crocoddyl.ActuationModelFloatingBase(self.state) else: self.actuation = crocoddyl.ActuationModelFull(self.state) - self.control = crocoddyl.ControlParametrizationModelPolyZero( - self.actuation.nu) + self.control = crocoddyl.ControlParametrizationModelPolyZero(self.actuation.nu) self.nu = self.actuation.nu self.createStandardModel(supportFootIds) if isTerminal: self.make_terminal_model() else: - self.make_running_model(supportFootIds ,swingFootTask) + self.make_running_model(supportFootIds, swingFootTask) def createStandardModel(self, supportFootIds): """Action model for a swing foot phase. @@ -190,8 +197,7 @@ class Node: self.contactModel = crocoddyl.ContactModelMultiple(self.state, self.nu) for i in supportFootIds: supportContactModel = crocoddyl.ContactModel3D( - self.state, i, np.array( - [0.0, 0.0, 0.0]), self.nu, np.array([0.0, 0.0]) + self.state, i, np.array([0.0, 0.0, 0.0]), self.nu, np.array([0.0, 0.0]) ) self.contactModel.addContact( self.pd.model.frames[i].name + "_contact", supportContactModel @@ -200,8 +206,7 @@ class Node: # Creating the cost model for a contact phase costModel = crocoddyl.CostModelSum(self.state, self.nu) - stateResidual = crocoddyl.ResidualModelState( - self.state, self.pd.xref, self.nu) + stateResidual = crocoddyl.ResidualModelState(self.state, self.pd.xref, self.nu) stateActivation = crocoddyl.ActivationModelWeightedQuad( self.pd.state_reg_w**2 ) @@ -224,8 +229,7 @@ class Node: self.contactModel = crocoddyl.ContactModelMultiple(self.state, self.nu) for i in supportFootIds: supportContactModel = crocoddyl.ContactModel3D( - self.state, i, np.array( - [0.0, 0.0, 0.0]), self.nu, np.array([0.0, 0.0]) + self.state, i, np.array([0.0, 0.0, 0.0]), self.nu, np.array([0.0, 0.0]) ) self.dmodel.contacts.addContact( self.pd.model.frames[i].name + "_contact", supportContactModel @@ -233,8 +237,7 @@ class Node: def make_terminal_model(self): self.isTerminal = True - stateResidual = crocoddyl.ResidualModelState( - self.state, self.pd.xref, self.nu) + stateResidual = crocoddyl.ResidualModelState(self.state, self.pd.xref, self.nu) stateActivation = crocoddyl.ActivationModelWeightedQuad( self.pd.terminal_velocity_w**2 ) @@ -265,18 +268,14 @@ class Node: ctrlReg = crocoddyl.CostModelResidual(self.state, ctrlResidual) self.costModel.addCost("ctrlReg", ctrlReg, self.pd.control_reg_w) - ctrl_bound_residual = crocoddyl.ResidualModelControl( - self.state, self.nu) + ctrl_bound_residual = crocoddyl.ResidualModelControl(self.state, self.nu) ctrl_bound_activation = crocoddyl.ActivationModelQuadraticBarrier( - crocoddyl.ActivationBounds(-self.pd.effort_limit, - self.pd.effort_limit) + crocoddyl.ActivationBounds(-self.pd.effort_limit, self.pd.effort_limit) ) ctrl_bound = crocoddyl.CostModelResidual( self.state, ctrl_bound_activation, ctrl_bound_residual ) - self.costModel.addCost("ctrlBound", ctrl_bound, - self.pd.control_bound_w) - + self.costModel.addCost("ctrlBound", ctrl_bound, self.pd.control_bound_w) self.tracking_cost(swingFootTask) diff --git a/python/quadruped_reactive_walking/WB_MPC/OcpResult.py b/python/quadruped_reactive_walking/WB_MPC/OcpResult.py index 0c7684d2501e7e01cc9bd3f7057ecb07ab5d9240..c6ce3c5e8945ec69f24aeffd2a24e397c2f26eb7 100644 --- a/python/quadruped_reactive_walking/WB_MPC/OcpResult.py +++ b/python/quadruped_reactive_walking/WB_MPC/OcpResult.py @@ -1,8 +1,8 @@ class OcpResult: def __init__(self): - self.x = None + self.xs = None self.a = None - self.u = None + self.us = None self.fs = None self.K = None self.f_world = None diff --git a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py index fc2c766d08c4d0703565e107841464f92545641f..a2ad642859be78423e5f60c1e8e1bbb9360ea011 100644 --- a/python/quadruped_reactive_walking/WB_MPC_Wrapper.py +++ b/python/quadruped_reactive_walking/WB_MPC_Wrapper.py @@ -11,18 +11,12 @@ from .WB_MPC.CrocoddylOCP import OCP import quadruped_reactive_walking as qrw -class DataInCtype(Structure): - """ - Ctype data structure for the shared memory between processes. - """ - - params = qrw.Params() - # TODO add the data exchanged with the OCP - _fields_ = [ - ("k", ctypes.c_int64), - ("x0", ctypes.c_double * 6), - ("guess", ctypes.c_double * 12), - ] +class Result: + def __init__(self, pd): + self.xs = np.zeros((pd.T + 1, pd.nx)) + self.us = np.zeros((pd.T, pd.nu)) + self.K = None + self.solving_duration = 0.0 class MPC_Wrapper: @@ -37,26 +31,26 @@ class MPC_Wrapper: self.pd = pd self.target = target - n_nodes = 0 - self.multiprocessing = params.enable_multiprocessing if self.multiprocessing: - self.newData = Value("b", False) - self.newResult = Value("b", False) + self.new_data = Value("b", False) + self.new_result = Value("b", False) self.running = Value("b", True) - self.dataIn = Value(DataInCtype) - # TODO: update output size - self.dataOut = Array("d", [0] * 24 * n_nodes) - self.cost = Value("d", 0.0) + self.in_k = Value("i", 0) + self.in_x0 = Array("d", [0] * pd.nx) + self.in_xs = Array("d", [0] * pd.T + 1 * pd.nx) + self.in_us = Array("d", [0] * pd.T * pd.nu) + self.out_xs = Array("d", [0] * pd.T + 1 * pd.nx) + self.out_us = Array("d", [0] * pd.T * pd.nu) + self.out_k = Array("d", [0] * 0 * 0) + self.out_solving_time = Value("d", 0.0) else: self.ocp = OCP(pd, target) - # TODO initialize first result - self.last_available_result = np.zeros((24, n_nodes)) - self.last_cost = 0.0 + self.last_available_result = Result(pd) - def solve(self, k, x0, guess=None): + def solve(self, k, x0, 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 @@ -66,35 +60,43 @@ class MPC_Wrapper: """ if self.multiprocessing: - self.run_MPC_asynchronous(k, x0, guess) + self.run_MPC_asynchronous(k, x0, xs, us) else: - self.run_MPC_synchronous(x0, guess) + self.run_MPC_synchronous(x0, xs, us) def get_latest_result(self): """ Return the desired contact forces that have been computed by the last iteration of the MPC. - If a new result is available, return the new result. + If a new result is available, return the new result. Otherwise return the old result again. """ if self.initialized: - if self.multiprocessing and self.newResult.value: - self.newResult.value = False - self.last_available_result = self.convert_dataOut() - self.last_cost = self.cost.value + if self.multiprocessing and self.new_result.value: + self.new_result.value = False + ( + self.last_available_result.xs, + self.last_available_result.us, + self.last_available_result.K, + self.last_available_result.solving_duration, + ) = self.decompress_dataOut() else: self.initialized = True - return self.last_available_result, self.last_cost + return self.last_available_result - def run_MPC_synchronous(self, x0, guess): + def run_MPC_synchronous(self, x0, xs, us): """ Run the MPC (synchronous version) """ - self.ocp.solve(x0, guess) - self.last_available_result = self.ocp.get_results() - #self.last_cost = self.ocp.retrieve_cost() + self.ocp.solve(x0, xs, us) + ( + self.last_available_result.xs, + self.last_available_result.us, + self.last_available_result.K, + self.last_available_result.solving_duration, + ) = self.ocp.get_results() - def run_MPC_asynchronous(self, k, x0, guess): + def run_MPC_asynchronous(self, k, x0, xs, us): """ Run the MPC (asynchronous version) """ @@ -102,61 +104,96 @@ class MPC_Wrapper: p = Process(target=self.MPC_asynchronous) p.start() - self.add_new_data(k, x0, guess) + self.add_new_data(k, x0, xs, us) def MPC_asynchronous(self): """ Parallel process with an infinite loop that run the asynchronous MPC """ while self.running.value: - if self.newData.value: - self.newData.value = False + if not self.new_data.value: + continue + + self.new_data.value = False - k, x0, guess = self.decompress_dataIn(self.dataIn) + k, x0, xs, us = self.decompress_dataIn(self.dataIn) - if k == 0: - loop_ocp = OCP(self.pd, self.target) + if k == 0: + loop_ocp = OCP(self.pd, self.target) - loop_ocp.solve(x0, guess) - self.dataOut[:] = loop_ocp.get_latest_result().ravel(order="F") - self.cost.value = loop_ocp.retrieve_cost() - self.newResult.value = True + loop_ocp.solve(x0, xs, us) + xs, us, K, solving_time = loop_ocp.get_latest_result() + self.compress_dataOut(xs, us, K, solving_time) - def add_new_data(self, k, x0): + self.new_result.value = True + + def add_new_data(self, k, x0, xs, us): """ - Compress data in a C-type structure that belongs to the shared memory to send + 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 """ - with self.dataIn.get_lock(): - self.dataIn.k = k - # np.frombuffer(self.dataIn.xref).reshape((12, self.n_steps + 1))[ - # :, : - # ] = xref - self.newData.value = True - def decompress_dataIn(self, dataIn): + self.compress_dataIn(k, x0, xs, us) + self.new_data.value = True + + def compress_dataIn(self, k, x0, 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 - - Args: dataIn (Array): shared C-type structure that contains the input data """ + with self.in_k.get_lock(): + self.in_k.value = k + with self.in_x0.get_lock(): + np.frombuffer(self.in_x0).reshape(self.pd.nx)[:] = x0 + with self.in_xs.get_lock(): + np.frombuffer(self.in_xs).reshape((self.pd.T + 1, self.pd.nx))[:, :] = xs + with self.in_us.get_lock(): + np.frombuffer(self.in_us).reshape((self.pd.T, self.pd.nu))[:, :] = us - with dataIn.get_lock(): + return k, x0, xs, us + + def decompress_dataIn(self): + """ + 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 + """ + with self.in_k.get_lock(): k = self.dataIn.k - xref = np.frombuffer(self.dataIn.xref).reshape((12, self.n_steps + 1)) + with self.in_x0.get_lock(): + x0 = np.frombuffer(self.in_x0).reshape(self.pd.nx) + with self.in_xs.get_lock(): + xs = np.frombuffer(self.in_xs).reshape((self.pd.T + 1, self.pd.nx)) + with self.in_us.get_lock(): + us = np.frombuffer(self.in_us).reshape((self.pd.T, self.pd.nu)) - return k, xref + return k, x0, xs, us - def convert_dataOut(self): + def compress_dataOut(self, xs, us, K, solving_time): + """ + Compress data to a C-type structure that belongs to the shared memory to + retrieve data in the main control loop from the asynchronous MPC + """ + with self.out_xs.get_lock(): + np.frombuffer(self.out_xs).reshape((self.pd.T + 1, self.pd.nx))[:, :] = xs + with self.out_us.get_lock(): + np.frombuffer(self.out_us).reshape((self.pd.T, self.pd.nu))[:, :] = us + with self.out_k.get_lock(): + np.frombuffer(self.out_k).reshape((0, 0))[:, :] = K + self.out_solving_time.value = solving_time + + def decompress_dataOut(self): """ Return the result of the asynchronous MPC (desired contact forces) that is stored in the shared memory """ + xs = np.frombuffer(self.out_xs).reshape((self.pd.T + 1, self.pd.nx)) + us = np.frombuffer(self.out_us).reshape((self.pd.T, self.pd.nu)) + K = np.frombuffer(self.out_k).reshape((0, 0)) + solving_time = self.solving_time.value - return np.array(self.dataOut[:]).reshape((24, -1), order="F") + return xs, us, K, solving_time def stop_parallel_loop(self): """ @@ -164,5 +201,3 @@ class MPC_Wrapper: """ self.running.value = False - - return 0 diff --git a/python/quadruped_reactive_walking/main_solo12_control.py b/python/quadruped_reactive_walking/main_solo12_control.py index 48750714cd5c07a509810bfd5094631b5dc956b3..654e65832b72a2adf0c4c5f46240433af6c58513 100644 --- a/python/quadruped_reactive_walking/main_solo12_control.py +++ b/python/quadruped_reactive_walking/main_solo12_control.py @@ -147,7 +147,7 @@ def control_loop(): qc = QualisysClient(ip="140.93.16.160", body_id=0) if params.LOGGING or params.PLOTTING: - loggerControl = LoggerControl(pd, log_size=params.N_SIMULATION-1) + loggerControl = LoggerControl(pd, params, log_size=params.N_SIMULATION-1) if params.SIMULATION: device.Init( diff --git a/python/quadruped_reactive_walking/tools/LoggerControl.py b/python/quadruped_reactive_walking/tools/LoggerControl.py index 9f4833db9630f5e17e8c4172621b533143be1ec0..79794913905440d570fb4f84b435fd985780fa4d 100644 --- a/python/quadruped_reactive_walking/tools/LoggerControl.py +++ b/python/quadruped_reactive_walking/tools/LoggerControl.py @@ -5,13 +5,14 @@ from .kinematics_utils import get_translation, get_translation_array class LoggerControl: - def __init__(self, pd, log_size=60e3, loop_buffer=False, file=None): + def __init__(self, pd, params, log_size=60e3, loop_buffer=False, file=None): if file is not None: self.data = np.load(file, allow_pickle=True) self.log_size = np.int(log_size) self.i = 0 self.loop_buffer = loop_buffer + self.multiprocess_mpc = params.enable_multiprocessing size = self.log_size self.pd = pd @@ -59,10 +60,8 @@ class LoggerControl: self.t_ocp_update_terminal = np.zeros(size) # MPC - self.ocp_storage = { - "xs": np.zeros([size, pd.T + 1, pd.nx]), - "us": np.zeros([size, pd.T, pd.nu]), - } + self.ocp_xs = np.zeros([size, pd.T + 1, pd.nx]) + self.ocp_us = np.zeros([size, pd.T, pd.nu]) self.target = np.zeros([size, 3]) # Whole body control @@ -103,29 +102,32 @@ class LoggerControl: # Controller timings: MPC time, ... self.target[self.i] = controller.point_target - self.t_mpc[self.i] = controller.mpc.ocp.results.solver_time + self.t_mpc[self.i] = controller.mpc.mpc_result.solver_time self.t_send[self.i] = controller.t_send self.t_loop[self.i] = controller.t_loop self.t_measures[self.i] = controller.t_measures # Logging from model predictive control - self.ocp_storage["xs"][self.i] = np.array(controller.mpc.ocp.results.x) - self.ocp_storage["us"][self.i] = np.array(controller.mpc.ocp.results.u) + self.ocp_xs[self.i] = np.array(controller.mpc_result.xs) + self.ocp_us[self.i] = np.array(controller.mpc_result.us) self.t_measures[self.i] = controller.t_measures self.t_mpc[self.i] = controller.t_mpc self.t_send[self.i] = controller.t_send self.t_loop[self.i] = controller.t_loop - self.t_ocp_update[self.i] = controller.mpc.ocp.t_update - self.t_ocp_warm_start[self.i] = controller.mpc.ocp.t_warm_start - self.t_ocp_ddp[self.i] = controller.mpc.ocp.t_ddp - self.t_ocp_solve[self.i] = controller.mpc.ocp.t_solve + if not self.multiprocess_mpc: + self.t_ocp_update[self.i] = controller.mpc.ocp.t_update + self.t_ocp_warm_start[self.i] = controller.mpc.ocp.t_warm_start + self.t_ocp_ddp[self.i] = controller.mpc.ocp.t_ddp + self.t_ocp_solve[self.i] = controller.mpc.ocp.t_solve - self.t_ocp_update_FK[self.i] = controller.mpc.ocp.t_FK - self.t_ocp_shift[self.i] = controller.mpc.ocp.t_shift - self.t_ocp_update_last[self.i] = controller.mpc.ocp.t_update_last_model - self.t_ocp_update_terminal[self.i] = controller.mpc.ocp.t_update_terminal_model + self.t_ocp_update_FK[self.i] = controller.mpc.ocp.t_FK + self.t_ocp_shift[self.i] = controller.mpc.ocp.t_shift + self.t_ocp_update_last[self.i] = controller.mpc.ocp.t_update_last_model + self.t_ocp_update_terminal[ + self.i + ] = controller.mpc.ocp.t_update_terminal_model # Logging from whole body control self.wbc_P[self.i] = controller.result.P @@ -143,18 +145,15 @@ class LoggerControl: def plot(self, save=False, fileName="tmp/"): import matplotlib.pyplot as plt - x_mes = np.concatenate([self.q_mes[:, 3:6], self.v_mes[:, 3:6]], axis = 1) + x_mes = np.concatenate([self.q_mes[:, 3:6], self.v_mes[:, 3:6]], axis=1) - x_mpc = [self.ocp_storage["xs"][0][0, :]] - [x_mpc.append(x[1, :]) for x in self.ocp_storage["xs"][:-1]] + x_mpc = [self.ocp_xs[0][0, :]] + [x_mpc.append(x[1, :]) for x in self.ocp_xs[:-1]] x_mpc = np.array(x_mpc) # Feet positions calcuilated by every ocp all_ocp_feet_p_log = { - idx: [ - get_translation_array(self.pd, x, idx)[0] - for x in self.ocp_storage["xs"] - ] + idx: [get_translation_array(self.pd, x, idx)[0] for x in self.ocp_xs] for idx in self.pd.allContactIds } for foot in all_ocp_feet_p_log: @@ -162,28 +161,15 @@ class LoggerControl: # Measured feet positions m_feet_p_log = { - idx: - get_translation_array(self.pd, x_mes, idx)[0] + idx: get_translation_array(self.pd, x_mes, idx)[0] for idx in self.pd.allContactIds } - # Predicted eet positions + # Predicted feet positions feet_p_log = { - idx: - get_translation_array(self.pd, x_mpc, idx)[0] + idx: get_translation_array(self.pd, x_mpc, idx)[0] for idx in self.pd.allContactIds } - - - - # plt.figure(figsize=(12, 6), dpi=90) - # plt.title("Solver timings") - # plt.hist(self.ocp_timings, 30) - # plt.xlabel("timee [s]") - # plt.ylabel("Number of cases [#]") - # plt.draw() - # if save: - # plt.savefig(fileName + "_solver_timings") legend = ["Hip", "Shoulder", "Knee"] plt.figure(figsize=(12, 6), dpi=90) @@ -235,10 +221,10 @@ class LoggerControl: plt.savefig(fileName + "/joint_torques") legend = ["x", "y", "z"] - plt.figure(figsize=(12, 18), dpi = 90) + plt.figure(figsize=(12, 18), dpi=90) for p in range(3): - plt.subplot(3,1, p+1) - plt.title('Free foot on ' + legend[p]) + plt.subplot(3, 1, p + 1) + plt.title("Free foot on " + legend[p]) plt.plot(self.target[:, p]) plt.plot(m_feet_p_log[self.pd.rfFootId][:, p]) plt.plot(feet_p_log[self.pd.rfFootId][:, p]) @@ -247,8 +233,9 @@ class LoggerControl: plt.savefig(fileName + "/target") self.plot_controller_times() - # self.plot_OCP_times() - # self.plot_OCP_update_times() + if not self.multiprocess_mpc: + self.plot_OCP_times() + self.plot_OCP_update_times() plt.show() @@ -311,7 +298,8 @@ class LoggerControl: np.savez_compressed( name, - ocp_storage=self.ocp_storage, + ocp_xs=self.ocp_xs, + ocp_us=self.ocp_us, t_measures=self.t_measures, t_mpc=self.t_mpc, t_send=self.t_send, @@ -381,7 +369,8 @@ class LoggerControl: self.t_loop = self.data["t_loop"] self.t_measures = self.data["t_meausres"] - self.ocp_storage = self.data["ocp_storage"].item() + self.ocp_xs = self.data["ocp_xs"] + self.ocp_us = self.data["ocp_us"] self.t_measures = self.data["t_measures"] self.t_mpc = self.data["t_mpc"]