# coding: utf8 import numpy as np import gamepadClient as gC import libquadruped_reactive_walking as lqrw class Joystick: """Joystick-like controller that outputs the reference velocity in local frame Args: predefined (bool): use either a predefined velocity profile (True) or a gamepad (False) """ def __init__(self, params, multi_simu=False): # Controller parameters self.dt_wbc = params.dt_wbc self.dt_mpc = params.dt_mpc # Reference velocity in local frame self.v_ref = np.array([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).T self.v_gp = np.array([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).T self.reduced = False self.stop = False self.alpha = 0.0005 # Coefficient to low pass the joystick velocity # Bool to modify the update of v_ref # Used to launch multiple simulations self.multi_simu = multi_simu # If we are using a predefined reference velocity (True) or a joystick (False) self.predefined = params.predefined_vel # If we are performing an analysis from outside self.analysis = False # Joystick variables (linear and angular velocity and their scaling for the joystick) self.vX = 0. self.vY = 0. self.vYaw = 0. self.VxScale = 0.5 self.VyScale = 0.8 self.vYawScale = 0.8 self.Vx_ref = 0.3 self.Vy_ref = 0.0 self.Vw_ref = 0.0 # Y, B, A and X buttons (in that order) self.northButton = False self.eastButton = False self.southButton = False self.westButton = False self.joystick_code = 0 # Code to carry information about pressed buttons self.joyCpp = lqrw.Joystick() def update_v_ref(self, k_loop, velID): """Update the reference velocity of the robot along X, Y and Yaw in local frame by listening to a gamepad handled by an independent thread Args: k_loop (int): numero of the current iteration velID (int): Identifier of the current velocity profile to be able to handle different scenarios """ if self.predefined: if self.multi_simu: self.update_v_ref_multi_simu(k_loop) elif self.analysis: self.handle_v_switch(k_loop) else: self.update_v_ref_predefined(k_loop, velID) else: self.update_v_ref_gamepad(k_loop) return 0 def update_v_ref_gamepad(self, k_loop): """Update the reference velocity of the robot along X, Y and Yaw in local frame by listening to a gamepad handled by an independent thread Args: k_loop (int): numero of the current iteration """ # Create the gamepad client if k_loop == 0: self.gp = gC.GamepadClient() # Get the velocity command based on the position of joysticks self.vX = self.gp.leftJoystickX.value * self.VxScale self.vY = self.gp.leftJoystickY.value * self.VyScale self.vYaw = self.gp.rightJoystickX.value * self.vYawScale if self.gp.L1Button.value: # If L1 is pressed the orientation of the base is controlled self.v_gp = np.array( [[0.0, 0.0, - self.vYaw * 0.25, - self.vX * 5, - self.vY * 2, 0.0]]).T else: # Otherwise the Vx, Vy, Vyaw is controlled self.v_gp = np.array( [[- self.vY, - self.vX, 0.0, 0.0, 0.0, - self.vYaw]]).T # Reduce the size of the support polygon by pressing Start if self.gp.startButton.value: self.reduced = not self.reduced # Switch to safety controller if the Back key is pressed if self.gp.backButton.value: self.stop = True # Switch gaits if self.gp.northButton.value: self.northButton = True self.eastButton = False self.southButton = False self.westButton = False elif self.gp.eastButton.value: self.northButton = False self.eastButton = True self.southButton = False self.westButton = False elif self.gp.southButton.value: self.northButton = False self.eastButton = False self.southButton = True self.westButton = False elif self.gp.westButton.value: self.northButton = False self.eastButton = False self.southButton = False self.westButton = True # Low pass filter to slow down the changes of velocity when moving the joysticks self.v_gp[(self.v_gp < 0.004) & (self.v_gp > -0.004)] = 0.0 self.v_ref = self.alpha * self.v_gp + (1-self.alpha) * self.v_ref # Update joystick code depending on which buttons are pressed self.computeCode() return 0 def computeCode(self): # Check joystick buttons to trigger a change of gait type self.joystick_code = 0 if self.northButton: self.joystick_code = 1 self.northButton = False elif self.eastButton: self.joystick_code = 2 self.eastButton = False elif self.southButton: self.joystick_code = 3 self.southButton = False elif self.westButton: self.joystick_code = 4 self.westButton = False def handle_v_switch(self, k): """Handle the change of reference velocity according to the chosen predefined velocity profile Args: k (int): numero of the current iteration """ self.v_ref[:, 0] = self.joyCpp.handle_v_switch(k, self.k_switch.reshape((-1, 1)), self.v_switch) def update_v_ref_predefined(self, k_loop, velID): """Update the reference velocity of the robot along X, Y and Yaw in local frame according to a predefined sequence Args: k_loop (int): numero of the current iteration velID (int): identifier of the current velocity profile to be able to handle different scenarios """ if (k_loop == 0): if velID == 0: self.t_switch = np.array([0, 1, 5.5, 8.5, 13, 26, 40, 60]) self.v_switch = np.array([[0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) elif velID == 1: V_max = 1.0 R_max = 0.3 self.t_switch = np.array([0, 2, 6, 16, 24, 32, 40, 44, 46, 52, 60, 66, 68, 80, 82, 86, 88, 90]) self.v_switch = np.zeros((6, self.t_switch.shape[0])) self.v_switch[0, :] = np.array([0.0, 0.0, V_max, V_max, 0.0, 0.0, 0.0, 0.0, -V_max, -V_max, 0.0, 0.0, 0.0, V_max, V_max, V_max, V_max, V_max]) self.v_switch[1, :] = np.array([0.0, 0.0, 0.0, 0.0, -V_max*0.5, -V_max*0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) self.v_switch[5, :] = np.array([0.0, 0.0, R_max, R_max, R_max, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, R_max, R_max, 0.0, 0.0, -R_max, 0.0]) elif velID == 2: self.t_switch = np.array([0, 3, 4, 5, 7, 6, 8 ,13, 14]) self.v_switch = np.array([[0.0, 0., 0., 0.3, 0.6, 0.5 , 0.4,0.4,0. ], [0.0, 0.4, 0.5, 0.2, 0., 0.,0.,0.,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.,0.,0.,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.,0.,0.,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.,0.,0.,0.], [0.0, 0., 0.7, 0.7, 0.7 ,0.7, 0.7,0., 0. ]]) elif velID == 3: # PLANNER > NL > LIN self.t_switch = np.array([0, 3, 4, 5, 10,15]) self.v_switch = np.array([[0.0, 0., 0., 0., 0., 0. ], [0.0, 0.0, 0.0, 0.0, 0., 0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.8, 1.5, 1.8, 2.5 ,2.8]]) elif velID == 4: # PLANNER > NL == LIN self.t_switch = np.array([0, 3, 4, 5, 10,15]) self.v_switch = np.array([[0.0, 0.4, 0.6, 0.6, 0., 0. ], [0.0, 0.4, 0.6, 0.6, 0.6 ,0.6], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.5, 0.5 ,0.5]]) elif velID == 5: # PLANNER GOOD ROLL / PITCH self.t_switch = np.array([0, 3, 4, 5, 10,15]) self.v_switch = np.array([[0.0, 0.4, 0.4, 0.4, 0.4, 0. ], [0.0, 0.4, 0.4, 0.4, 0. ,0.4], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.7, 0.7 ,0.7]]) elif velID == 6: self.t_switch = np.array([0, 3, 4, 5, 10,15]) self.v_switch = np.array([[0.0, 0.4, 0.5, 0.6, 0.6, 0.6 ], [0.0, 0., 0., 0., 0. ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0.0, 0.0 ,0.], [0.0, 0.0, 0.0, 0., 0. ,0.]]) self.k_switch = (self.t_switch / self.dt_wbc).astype(int) self.handle_v_switch(k_loop) return 0 def update_v_ref_multi_simu(self, k_loop): """Update the reference velocity of the robot along X, Y and Yaw in local frame according to a predefined sequence Args: k_loop (int): number of MPC iterations since the start of the simulation velID (int): Identifier of the current velocity profile to be able to handle different scenarios """ # Moving forwards """if k_loop == self.k_mpc*16*3: self.v_ref = np.array([[1.0, 0.0, 0.0, 0.0, 0.0, 0.0]]).T""" beta_x = int(max(abs(self.Vx_ref)*10000, 100.0)) alpha_x = np.max([np.min([(k_loop-self.k_mpc*16*3)/beta_x, 1.0]), 0.0]) beta_y = int(max(abs(self.Vy_ref)*10000, 100.0)) alpha_y = np.max([np.min([(k_loop-self.k_mpc*16*3)/beta_y, 1.0]), 0.0]) beta_w = int(max(abs(self.Vw_ref)*2500, 100.0)) alpha_w = np.max([np.min([(k_loop-self.k_mpc*16*3)/beta_w, 1.0]), 0.0]) # self.v_ref = np.array([[0.3*alpha, 0.0, 0.0, 0.0, 0.0, 0.0]]).T self.v_ref = np.array( [[self.Vx_ref*alpha_x, self.Vy_ref*alpha_y, 0.0, 0.0, 0.0, self.Vw_ref*alpha_w]]).T return 0 def update_for_analysis(self, des_vel_analysis, N_analysis, N_steady): self.analysis = True self.k_switch = np.array([0, int(1/self.dt_wbc), N_analysis, N_analysis + N_steady]) self.v_switch = np.zeros((6, 4)) self.v_switch[:, 2] = des_vel_analysis self.v_switch[:, 3] = des_vel_analysis return 0 if __name__ == "__main__": from matplotlib import pyplot as plt import libquadruped_reactive_walking as lqrw from time import clock params = lqrw.Params() # Object that holds all controller parameters params.predefined_vel = False joystick = Joystick(params) k = 0 vx = [0.0] * 1000 fig = plt.figure() ax = plt.gca() ax.set_ylim([-0.5, 0.5]) h, = plt.plot(np.linspace(0.001, 1.0, 1000), vx, "b", linewidth=2) plt.xlabel("Time [s]") plt.ylabel("Forward reference velocity [m/s]") plt.show(block=False) print("Start") while True: # Update the reference velocity coming from the gamepad joystick.update_v_ref(k, 0) vx.pop(0) vx.append(joystick.v_ref[0, 0]) if k % 50 == 0: h.set_ydata(vx) print("Joystick raw: ", joystick.v_gp[0, 0]) print("Joystick filtered: ", joystick.v_ref[0, 0]) plt.pause(0.0001) k += 1