import time import numpy as np import pinocchio as pin import pybullet as pyb from . import MPC_Wrapper, quadruped_reactive_walking as qrw from .solo3D.utils import quaternionToRPY from .tools.utils_mpc import init_robot class Result: """ Object to store the result of the control loop It contains what is sent to the robot (gains, desired positions and velocities, feedforward torques) """ def __init__(self, params): self.P = np.array(params.Kp_main.tolist() * 4) self.D = np.array(params.Kd_main.tolist() * 4) self.FF = params.Kff_main * np.ones(12) self.q_des = np.zeros(12) self.v_des = np.zeros(12) self.tau_ff = np.zeros(12) class DummyDevice: def __init__(self): self.imu = self.IMU() self.joints = self.Joints() self.base_position = np.zeros(3) self.base_position[2] = 0.1944 self.b_base_velocity = np.zeros(3) class IMU: def __init__(self): self.linear_acceleration = np.zeros(3) self.gyroscope = np.zeros(3) self.attitude_euler = np.zeros(3) self.attitude_quaternion = np.zeros(4) class Joints: def __init__(self): self.positions = np.zeros(12) self.velocities = np.zeros(12) class Controller: def __init__(self, params, q_init, t): """Function that runs a simulation scenario based on a reference velocity profile, an environment and various parameters to define the gait Args: params (Params object): store parameters q_init (array): initial position of actuators t (float): time of the simulation """ self.DEMONSTRATION = params.DEMONSTRATION self.SIMULATION = params.SIMULATION self.solo3D = params.solo3D self.dt_mpc = params.dt_mpc self.dt_wbc = params.dt_wbc self.k_mpc = int(params.dt_mpc / params.dt_wbc) self.type_MPC = params.type_MPC self.enable_pyb_GUI = params.enable_pyb_GUI self.enable_corba_viewer = params.enable_corba_viewer self.q_display = np.zeros(19) self.q_security = np.array( [1.2, 2.1, 3.14, 1.2, 2.1, 3.14, 1.2, 2.1, 3.14, 1.2, 2.1, 3.14] ) # Init joint torques to correct shape self.jointTorques = np.zeros((12, 1)) # List to store the IDs of debug lines self.ID_deb_lines = [] # Disable perfect estimator if we are not in simulation if not params.SIMULATION: params.perfectEstimator = ( False # Cannot use perfect estimator if we are running on real robot ) # Initialisation of the solo model/data and of the Gepetto viewer self.solo = init_robot(q_init, params) # Create Joystick object self.joystick = qrw.Joystick() self.joystick.initialize(params) # Enable/Disable hybrid control self.enable_hybrid_control = True self.h_ref = params.h_ref self.h_ref_mem = params.h_ref self.q = np.zeros((18, 1)) # Orientation part is in roll pitch yaw self.q[:6, 0] = np.array([0.0, 0.0, self.h_ref, 0.0, 0.0, 0.0]) self.q[6:, 0] = q_init self.q_init = q_init.copy() self.v = np.zeros((18, 1)) self.b_v = np.zeros((18, 1)) self.o_v_filt = np.zeros((18, 1)) self.q_wbc = np.zeros((18, 1)) self.dq_wbc = np.zeros((18, 1)) self.xgoals = np.zeros((12, 1)) self.xgoals[2, 0] = self.h_ref self.gait = qrw.Gait() self.gait.initialize(params) self.estimator = qrw.Estimator() self.estimator.initialize(params) self.wbcWrapper = qrw.WbcWrapper() self.wbcWrapper.initialize(params) # 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 if params.solo3D: from solo3D.SurfacePlannerWrapper import Surface_planner_wrapper if self.SIMULATION: from solo3D.pyb_environment_3D import PybEnvironment3D self.enable_multiprocessing_mip = params.enable_multiprocessing_mip self.offset_perfect_estimator = 0.0 self.update_mip = False if self.solo3D: self.surfacePlanner = Surface_planner_wrapper(params) self.statePlanner = qrw.StatePlanner3D() self.statePlanner.initialize(params) self.footstepPlanner = qrw.FootstepPlannerQP() self.footstepPlanner.initialize( params, self.gait, self.surfacePlanner.floor_surface ) # Trajectory Generator Bezier x_margin_max_ = 0.06 # margin inside convex surfaces [m]. t_margin_ = ( 0.3 # 100*t_margin_% of the curve around critical point. range: [0, 1] ) z_margin_ = 0.06 # 100*z_margin_% of the curve after the critical point. range: [0, 1] N_sample = ( 8 # Number of sample in the least square optimisation for Bezier coeffs ) N_sample_ineq = 10 # Number of sample while browsing the curve degree = 7 # Degree of the Bezier curve self.footTrajectoryGenerator = qrw.FootTrajectoryGeneratorBezier() self.footTrajectoryGenerator.initialize( params, self.gait, self.surfacePlanner.floor_surface, x_margin_max_, t_margin_, z_margin_, N_sample, N_sample_ineq, degree, ) if self.SIMULATION: self.pybEnvironment3D = PybEnvironment3D( params, self.gait, self.statePlanner, self.footstepPlanner, self.footTrajectoryGenerator, ) else: self.statePlanner = qrw.StatePlanner() self.statePlanner.initialize(params) self.footstepPlanner = qrw.FootstepPlanner() self.footstepPlanner.initialize(params, self.gait) self.footTrajectoryGenerator = qrw.FootTrajectoryGenerator() self.footTrajectoryGenerator.initialize(params, self.gait) # ForceMonitor to display contact forces in PyBullet with red lines # import ForceMonitor # myForceMonitor = ForceMonitor.ForceMonitor(pyb_sim.robotId, pyb_sim.planeId) self.t = t self.k = 0 self.qmes12 = np.zeros((19, 1)) self.vmes12 = np.zeros((18, 1)) self.q_display = np.zeros((19, 1)) self.v_ref = np.zeros((18, 1)) self.a_ref = np.zeros((18, 1)) self.h_v = np.zeros((18, 1)) self.h_v_windowed = np.zeros((6, 1)) self.yaw_estim = 0.0 self.RPY_filt = np.zeros(3) self.feet_a_cmd = np.zeros((3, 4)) self.feet_v_cmd = np.zeros((3, 4)) self.feet_p_cmd = np.zeros((3, 4)) self.q_filter = np.zeros((18, 1)) self.h_v_filt_mpc = np.zeros((6, 1)) self.vref_filt_mpc = np.zeros((6, 1)) self.filter_mpc_q = qrw.Filter() self.filter_mpc_q.initialize(params) self.filter_mpc_v = qrw.Filter() self.filter_mpc_v.initialize(params) self.filter_mpc_vref = qrw.Filter() self.filter_mpc_vref.initialize(params) self.nle = np.zeros((6, 1)) self.p_ref = np.zeros((6, 1)) self.treshold_static = False self.error = False self.last_q_perfect = np.zeros(6) self.last_b_vel = np.zeros(3) self.n_nan = 0 self.result = Result(params) device = DummyDevice() device.joints.positions = q_init self.compute(device) def compute(self, device, qc=None): """Run one iteration of the main control loop Args: device (object): Interface with the masterboard or the simulation """ t_start = time.time() self.joystick.update_v_ref(self.k, self.gait.is_static()) q_perfect = np.zeros(6) b_baseVel_perfect = np.zeros(3) if self.solo3D and qc == None: q_perfect[:3] = device.base_position q_perfect[3:] = device.imu.attitude_euler b_baseVel_perfect = device.b_base_velocity elif self.solo3D and qc != None: if self.k <= 1: self.initial_pos = [0.0, 0.0, -0.046] self.initial_matrix = pin.rpy.rpyToMatrix(0.0, 0.0, 0.0).transpose() q_perfect[:3] = self.initial_matrix @ (qc.getPosition() - self.initial_pos) q_perfect[3:] = quaternionToRPY(qc.getOrientationQuat())[:, 0] b_baseVel_perfect[:] = ( qc.getOrientationMat9().reshape((3, 3)).transpose() @ qc.getVelocity().reshape((3, 1)) ).ravel() if np.isnan(np.sum(q_perfect)): print("Error: nan values in perfect position of the robot") q_perfect = self.last_q_perfect self.n_nan += 1 if not np.any(self.last_q_perfect) or self.n_nan >= 5: self.error = True elif np.isnan(np.sum(b_baseVel_perfect)): print("Error: nan values in perfect velocity of the robot") b_baseVel_perfect = self.last_b_vel self.n_nan += 1 if not np.any(self.last_b_vel) or self.n_nan >= 5: self.error = True else: self.last_q_perfect = q_perfect self.last_b_vel = b_baseVel_perfect self.n_nan = 0 self.estimator.run( self.gait.matrix, self.footTrajectoryGenerator.get_foot_position(), device.imu.linear_acceleration, device.imu.gyroscope, device.imu.attitude_euler, device.joints.positions, device.joints.velocities, q_perfect, b_baseVel_perfect, ) # Update state vectors of the robot (q and v) + transformation matrices between world and horizontal frames self.estimator.update_reference_state(self.joystick.get_v_ref()) oRh = self.estimator.get_oRh() hRb = self.estimator.get_hRb() oTh = self.estimator.get_oTh().reshape((3, 1)) self.a_ref[:6, 0] = self.estimator.get_base_acc_ref() self.v_ref[:6, 0] = self.estimator.get_base_vel_ref() self.h_v[:6, 0] = self.estimator.get_h_v() self.h_v_windowed[:6, 0] = self.estimator.get_h_v_filtered() if self.solo3D: self.q[:3, 0] = self.estimator.get_q_estimate()[:3] self.q[6:, 0] = self.estimator.get_q_estimate()[7:] self.q[3:6] = quaternionToRPY(self.estimator.get_q_estimate()[3:7]) else: self.q[:, 0] = self.estimator.get_q_reference() self.v[:, 0] = self.estimator.get_v_reference() self.yaw_estim = self.q[5, 0] # Quantities go through a 1st order low pass filter with fc = 15 Hz (avoid >25Hz foldback) self.q_filter[:6, 0] = self.filter_mpc_q.filter(self.q[:6, 0], True) self.q_filter[6:, 0] = self.q[6:, 0].copy() self.h_v_filt_mpc[:, 0] = self.filter_mpc_v.filter(self.h_v[:6, 0], False) self.vref_filt_mpc[:, 0] = self.filter_mpc_vref.filter(self.v_ref[:6, 0], False) if self.solo3D: oTh_3d = np.zeros((3, 1)) oTh_3d[:2, 0] = self.q_filter[:2, 0] oRh_3d = pin.rpy.rpyToMatrix(0.0, 0.0, self.q_filter[5, 0]) t_filter = time.time() self.t_filter = t_filter - t_start self.gait.update(self.k, self.k_mpc, self.joystick.get_joystick_code()) self.update_mip = self.k % self.k_mpc == 0 and self.gait.is_new_step() if self.solo3D: if self.update_mip: self.statePlanner.update_surface( self.q_filter[:6, :1], self.vref_filt_mpc[:6, :1] ) if self.surfacePlanner.initialized: self.error = self.surfacePlanner.get_latest_results() self.footstepPlanner.update_surfaces( self.surfacePlanner.potential_surfaces, self.surfacePlanner.selected_surfaces, self.surfacePlanner.mip_success, self.surfacePlanner.mip_iteration, ) self.o_targetFootstep = self.footstepPlanner.update_footsteps( self.k % self.k_mpc == 0 and self.k != 0, int(self.k_mpc - self.k % self.k_mpc), self.q_filter[:, 0], self.h_v_windowed[:6, :1].copy(), self.v_ref[:6, :1], ) self.statePlanner.compute_reference_states( self.q_filter[:6, :1], self.h_v_filt_mpc[:6, :1].copy(), self.vref_filt_mpc[:6, :1], ) xref = self.statePlanner.get_reference_states() fsteps = self.footstepPlanner.get_footsteps() gait_matrix = self.gait.matrix if self.update_mip and self.solo3D: configs = self.statePlanner.get_configurations().transpose() self.surfacePlanner.run( configs, gait_matrix, self.o_targetFootstep, self.vref_filt_mpc[:3, 0].copy(), ) self.surfacePlanner.initialized = True if not self.enable_multiprocessing_mip and self.SIMULATION: self.pybEnvironment3D.update_target_SL1M( self.surfacePlanner.all_feet_pos_syn ) t_planner = time.time() self.t_planner = t_planner - t_filter # Solve MPC if (self.k % self.k_mpc) == 0: 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 = oRh.transpose() @ (self.o_targetFootstep - oTh) self.mpc_wrapper.solve( self.k, xref, fsteps, gait_matrix, l_targetFootstep, oRh, oTh, self.footTrajectoryGenerator.get_foot_position(), self.footTrajectoryGenerator.get_foot_velocity(), self.footTrajectoryGenerator.get_foot_acceleration(), self.footTrajectoryGenerator.get_foot_jerk(), self.footTrajectoryGenerator.get_phase_durations() - self.footTrajectoryGenerator.get_elapsed_durations(), ) else: self.mpc_wrapper.solve( self.k, xref, fsteps, gait_matrix, np.zeros((3, 4)) ) except ValueError: print("MPC Problem") self.x_f_mpc, self.mpc_cost = self.mpc_wrapper.get_latest_result() t_mpc = time.time() self.t_mpc = t_mpc - t_planner # If the MPC optimizes footsteps positions then we use them if self.k > 100 and self.type_MPC == 3: for foot in range(4): if gait_matrix[0, foot] == 0: id = 0 while gait_matrix[id, foot] == 0: id += 1 self.o_targetFootstep[:2, foot] = self.x_f_mpc[ 24 + 2 * foot : 24 + 2 * foot + 2, id + 1 ] # Update pos, vel and acc references for feet if self.solo3D: self.footTrajectoryGenerator.update( self.k, self.o_targetFootstep, self.surfacePlanner.selected_surfaces, self.q_filter, ) else: self.footTrajectoryGenerator.update(self.k, self.o_targetFootstep) if not self.error and not self.joystick.get_stop(): if self.DEMONSTRATION and self.gait.is_static(): hRb = np.eye(3) # Desired position, orientation and velocities of the base self.xgoals[:6, 0] = np.zeros((6,)) if self.DEMONSTRATION and self.joystick.get_l1() and self.gait.is_static(): self.p_ref[:, 0] = self.joystick.get_p_ref() self.xgoals[[3, 4], 0] = self.p_ref[[3, 4], 0] self.h_ref = self.p_ref[2, 0] hRb = pin.rpy.rpyToMatrix(0.0, 0.0, self.p_ref[5, 0]) else: self.xgoals[[3, 4], 0] = xref[[3, 4], 1] self.h_ref = self.h_ref_mem # If the four feet are in contact then we do not listen to MPC (default contact forces instead) if self.DEMONSTRATION and self.gait.is_static(): self.x_f_mpc[12:24, 0] = [0.0, 0.0, 9.81 * 2.5 / 4.0] * 4 # Update configuration vector for wbc with filtered roll and pitch and reference angular positions of previous loop self.q_wbc[3:5, 0] = self.q_filter[3:5, 0] self.q_wbc[6:, 0] = self.wbcWrapper.qdes[:] # Update velocity vector for wbc self.dq_wbc[:6, 0] = self.estimator.get_v_estimate()[ :6 ] # Velocities in base frame (not horizontal frame!) self.dq_wbc[6:, 0] = self.wbcWrapper.vdes[ : ] # with reference angular velocities of previous loop # Feet command position, velocity and acceleration in base frame if self.solo3D: # Use estimated base frame self.feet_a_cmd = ( self.footTrajectoryGenerator.get_foot_acceleration_base_frame( oRh_3d.transpose(), np.zeros((3, 1)), np.zeros((3, 1)) ) ) self.feet_v_cmd = ( self.footTrajectoryGenerator.get_foot_velocity_base_frame( oRh_3d.transpose(), np.zeros((3, 1)), np.zeros((3, 1)) ) ) self.feet_p_cmd = ( self.footTrajectoryGenerator.get_foot_position_base_frame( oRh_3d.transpose(), oTh_3d + np.array([[0.0], [0.0], [xref[2, 1]]]), ) ) else: # Use ideal base frame self.feet_a_cmd = ( self.footTrajectoryGenerator.get_foot_acceleration_base_frame( hRb @ oRh.transpose(), np.zeros((3, 1)), np.zeros((3, 1)) ) ) self.feet_v_cmd = ( self.footTrajectoryGenerator.get_foot_velocity_base_frame( hRb @ oRh.transpose(), np.zeros((3, 1)), np.zeros((3, 1)) ) ) self.feet_p_cmd = ( self.footTrajectoryGenerator.get_foot_position_base_frame( hRb @ oRh.transpose(), oTh + np.array([[0.0], [0.0], [self.h_ref]]), ) ) self.xgoals[6:, 0] = self.vref_filt_mpc[ :, 0 ] # Velocities (in horizontal frame!) # Run InvKin + WBC QP self.wbcWrapper.compute( self.q_wbc, self.dq_wbc, (self.x_f_mpc[12:24, 0:1]).copy(), np.array([gait_matrix[0, :]]), self.feet_p_cmd, self.feet_v_cmd, self.feet_a_cmd, self.xgoals, ) self.result.q_des[:] = self.wbcWrapper.qdes[:] self.result.v_des[:] = self.wbcWrapper.vdes[:] self.result.tau_ff[:] = self.wbcWrapper.tau_ff self.clamp_result(device) self.nle[:3, 0] = self.wbcWrapper.nle[:3] # Display robot in Gepetto corba viewer if self.enable_corba_viewer and (self.k % 5 == 0): self.q_display[:3, 0] = self.q_wbc[:3, 0] self.q_display[3:7, 0] = pin.Quaternion( pin.rpy.rpyToMatrix(self.q_wbc[3:6, 0]) ).coeffs() self.q_display[7:, 0] = self.q_wbc[6:, 0] self.solo.display(self.q_display) self.t_wbc = time.time() - t_mpc self.security_check() if self.error or self.joystick.get_stop(): self.set_null_control() # Update PyBullet camera if not self.solo3D: self.pyb_camera(device, 0.0) else: # Update 3D Environment if self.SIMULATION: self.pybEnvironment3D.update(self.k) self.pyb_debug(device, fsteps, gait_matrix, xref) self.t_loop = time.time() - t_start self.k += 1 return self.error def pyb_camera(self, device, yaw): """ Update position of PyBullet camera on the robot position to do as if it was attached to the robot """ if self.k > 10 and self.enable_pyb_GUI: pyb.resetDebugVisualizerCamera( cameraDistance=0.6, cameraYaw=45, cameraPitch=-39.9, cameraTargetPosition=[ device.dummyHeight[0], device.dummyHeight[1], 0.0, ], ) def pyb_debug(self, device, fsteps, gait_matrix, xref): if self.k > 1 and self.enable_pyb_GUI: # Display desired feet positions in WBC as green spheres oTh_pyb = device.base_position.reshape((-1, 1)) oRh_pyb = pin.rpy.rpyToMatrix(0.0, 0.0, device.imu.attitude_euler[2]) for i in range(4): if not self.solo3D: pos = oRh_pyb @ self.feet_p_cmd[:, i : (i + 1)] + oTh_pyb pyb.resetBasePositionAndOrientation( device.pyb_sim.ftps_Ids_deb[i], pos[:, 0].tolist(), [0, 0, 0, 1] ) else: pos = self.o_targetFootstep[:, i] pyb.resetBasePositionAndOrientation( device.pyb_sim.ftps_Ids_deb[i], pos, [0, 0, 0, 1] ) # Display desired footstep positions as blue spheres for i in range(4): j = 0 cpt = 1 status = gait_matrix[0, i] while ( cpt < gait_matrix.shape[0] and j < device.pyb_sim.ftps_Ids.shape[1] ): while cpt < gait_matrix.shape[0] and gait_matrix[cpt, i] == status: cpt += 1 if cpt < gait_matrix.shape[0]: status = gait_matrix[cpt, i] if status: pos = ( oRh_pyb @ fsteps[cpt, (3 * i) : (3 * (i + 1))].reshape((-1, 1)) + oTh_pyb - np.array([[0.0], [0.0], [oTh_pyb[2, 0]]]) ) pyb.resetBasePositionAndOrientation( device.pyb_sim.ftps_Ids[i, j], pos[:, 0].tolist(), [0, 0, 0, 1], ) else: pyb.resetBasePositionAndOrientation( device.pyb_sim.ftps_Ids[i, j], [0.0, 0.0, -0.1], [0, 0, 0, 1], ) j += 1 # Hide unused spheres underground for k in range(j, device.pyb_sim.ftps_Ids.shape[1]): pyb.resetBasePositionAndOrientation( device.pyb_sim.ftps_Ids[i, k], [0.0, 0.0, -0.1], [0, 0, 0, 1] ) # Display reference trajectory xref_rot = np.zeros((3, xref.shape[1])) for i in range(xref.shape[1]): xref_rot[:, i : (i + 1)] = ( oRh_pyb @ xref[:3, i : (i + 1)] + oTh_pyb + np.array([[0.0], [0.0], [0.05 - self.h_ref]]) ) if len(device.pyb_sim.lineId_red) == 0: for i in range(xref.shape[1] - 1): device.pyb_sim.lineId_red.append( pyb.addUserDebugLine( xref_rot[:3, i].tolist(), xref_rot[:3, i + 1].tolist(), lineColorRGB=[1.0, 0.0, 0.0], lineWidth=8, ) ) else: for i in range(xref.shape[1] - 1): device.pyb_sim.lineId_red[i] = pyb.addUserDebugLine( xref_rot[:3, i].tolist(), xref_rot[:3, i + 1].tolist(), lineColorRGB=[1.0, 0.0, 0.0], lineWidth=8, replaceItemUniqueId=device.pyb_sim.lineId_red[i], ) # Display predicted trajectory x_f_mpc_rot = np.zeros((3, self.x_f_mpc.shape[1])) for i in range(self.x_f_mpc.shape[1]): x_f_mpc_rot[:, i : (i + 1)] = ( oRh_pyb @ self.x_f_mpc[:3, i : (i + 1)] + oTh_pyb + np.array([[0.0], [0.0], [0.05 - self.h_ref]]) ) if len(device.pyb_sim.lineId_blue) == 0: for i in range(self.x_f_mpc.shape[1] - 1): device.pyb_sim.lineId_blue.append( pyb.addUserDebugLine( x_f_mpc_rot[:3, i].tolist(), x_f_mpc_rot[:3, i + 1].tolist(), lineColorRGB=[0.0, 0.0, 1.0], lineWidth=8, ) ) else: for i in range(self.x_f_mpc.shape[1] - 1): device.pyb_sim.lineId_blue[i] = pyb.addUserDebugLine( x_f_mpc_rot[:3, i].tolist(), x_f_mpc_rot[:3, i + 1].tolist(), lineColorRGB=[0.0, 0.0, 1.0], lineWidth=8, replaceItemUniqueId=device.pyb_sim.lineId_blue[i], ) def security_check(self): """ Check if the command is fine and set the command to zero in case of error """ if not (self.error or self.joystick.get_stop()): if (np.abs(self.estimator.get_q_estimate()[7:]) > self.q_security).any(): print("-- POSITION LIMIT ERROR --") print(self.estimator.get_q_estimate()[7:]) print(np.abs(self.estimator.get_q_estimate()[7:]) > self.q_security) self.error = True elif (np.abs(self.estimator.get_v_security()) > 100.0).any(): print("-- VELOCITY TOO HIGH ERROR --") print(self.estimator.get_v_security()) print(np.abs(self.estimator.get_v_security()) > 100.0) self.error = True elif (np.abs(self.wbcWrapper.tau_ff) > 8.0).any(): print("-- FEEDFORWARD TORQUES TOO HIGH ERROR --") print(self.wbcWrapper.tau_ff) print(np.abs(self.wbcWrapper.tau_ff) > 8.0) self.error = True def clamp(self, num, min_value=None, max_value=None): clamped = False if min_value is not None and num <= min_value: num = min_value clamped = True if max_value is not None and num >= max_value: num = max_value clamped = True return clamped def clamp_result(self, device, set_error=False): """ Clamp the result """ hip_max = 120.0 * np.pi / 180.0 knee_min = 5.0 * np.pi / 180.0 for i in range(4): if self.clamp(self.result.q_des[3 * i + 1], -hip_max, hip_max): print("Clamping hip n " + str(i)) self.error = set_error if self.q_init[3 * i + 2] >= 0.0 and self.clamp( self.result.q_des[3 * i + 2], knee_min ): print("Clamping knee n " + str(i)) self.error = set_error elif self.q_init[3 * i + 2] <= 0.0 and self.clamp( self.result.q_des[3 * i + 2], max_value=-knee_min ): print("Clamping knee n " + str(i)) self.error = set_error for i in range(12): if self.clamp( self.result.q_des[i], device.joints.positions[i] - 4.0, device.joints.positions[i] + 4.0, ): print("Clamping position difference of motor n " + str(i)) self.error = set_error if self.clamp( self.result.v_des[i], device.joints.velocities[i] - 100.0, device.joints.velocities[i] + 100.0, ): print("Clamping velocity of motor n " + str(i)) self.error = set_error if self.clamp(self.result.tau_ff[i], -8.0, 8.0): print("Clamping torque of motor n " + str(i)) self.error = set_error def set_null_control(self): """ Send default null values to the robot """ self.result.P = np.zeros(12) self.result.D = 0.1 * np.ones(12) self.result.q_des[:] = np.zeros(12) self.result.v_des[:] = np.zeros(12) self.result.FF = np.zeros(12) self.result.tau_ff[:] = np.zeros(12)