class FixedWingController: """Controls a fixed wing aircraft""" def __init__(self, params, control_total_energy_divider, mode): """ :param control_total_energy_divider run total enery controller every xth time """ self.mode = mode # since we run on python2, make sure that all params are floats for p in params: params[p] = float(params[p]) self.params = params # Attitude Control self.c_roll = PyECLRollController() self.c_pitch = PyECLPitchController() self.c_yaw = PyECLYawController() att_tc = params["att_tc"] k_p = params["k_p"] k_ff = params["k_ff"] k_i = params["k_i"] i_max = params["i_max"] self.c_roll.set_time_constant(att_tc) self.c_roll.set_k_p(k_p) self.c_roll.set_k_i(k_i) self.c_roll.set_integrator_max(i_max) self.c_roll.set_k_ff(k_ff) self.c_pitch.set_time_constant(att_tc) self.c_pitch.set_k_p(k_p) self.c_pitch.set_k_i(k_i) self.c_pitch.set_integrator_max(i_max) self.c_pitch.set_k_ff(k_ff) self.c_pitch.set_max_rate_pos(params["pitch_max_rate_pos"]) self.c_pitch.set_max_rate_neg(params["pitch_max_rate_neg"]) self.c_pitch.set_roll_ff(params["pitch_roll_ff"]) self.c_yaw.set_time_constant(att_tc) self.c_yaw.set_k_p(k_p) self.c_yaw.set_k_i(k_i) self.c_yaw.set_integrator_max(i_max) self.c_yaw.set_k_ff(k_ff) self.c_yaw.set_coordinated_min_speed(params["coordinated_min_speed"]) self.c_yaw.set_coordinated_method(params["coordinated_method"]) # Altitude and speed control (total energy control) # XXX load values from params argument where it makes sense c_te_params = { "MT_ENABLED": 1, "MT_THR_FF": params["mtecs_throttle_ff"], "MT_THR_P": params["mtecs_throttle_p"], "MT_THR_I": params["mtecs_throttle_i"], "MT_THR_OFF": params["throttle_default"], "MT_PIT_FF": params["mtecs_pitch_ff"], "MT_PIT_P": params["mtecs_pitch_p"], "MT_PIT_I": params["mtecs_pitch_i"], "MT_PIT_OFF": 0.0, "MT_THR_MIN": 0.0, "MT_THR_MAX": 1.0, "MT_PIT_MIN": -45.0, "MT_PIT_MAX": 20.0, "MT_ALT_LP": params["mtecs_altitude_lowpass_cutoff"], "MT_FPA_LP": params["mtecs_flightpathangle_lowpass_cutoff"], "MT_FPA_P": params["mtecs_fpa_p"], "MT_FPA_D": 0.0, "MT_FPA_D_LP": 1.0, "MT_FPA_MIN": -20.0, "MT_FPA_MAX": 30.0, "MT_A_LP": params["mtecs_airspeed_lowpass_cutoff"], "MT_AD_LP": params["mtecs_airspeed_derivative_lowpass_cutoff"], "MT_ACC_P": params["mtecs_acc_p"], "MT_ACC_D": 0.0, "MT_ACC_D_LP": 0.5, "MT_ACC_MIN": -40.0, "MT_ACC_MAX": 40.0, "MT_TKF_THR_MIN": 1.0, "MT_TKF_THR_MAX": 1.0, "MT_TKF_PIT_MIN": 0.0, "MT_TKF_PIT_MAX": 45.0, "MT_USP_THR_MIN": 1.0, "MT_USP_THR_MAX": 1.0, "MT_USP_PIT_MIN": -45.0, "MT_USP_PIT_MAX": 0.0, "MT_LND_THR_MIN": 0.0, "MT_LND_THR_MAX": 0.0, "MT_LND_PIT_MIN": -5.0, "MT_LND_PIT_MAX": 15.0, "MT_THR_I_MAX": 10.0, "MT_PIT_I_MAX": 10.0, "FW_AIRSPD_MIN": params["airspeed_min"], } self.c_te = PyMTecs(c_te_params) self.control_count = 0 self.control_total_energy_divider = control_total_energy_divider def get_ground_speed_vec(self, state): """Retruns a vector of the ground speed""" return np.array([ state["speed_body_u"], state["speed_body_v"], state["speed_body_w"], ]) def call_mtecs(self, state, setpoint): """helper function to pass the right arguments to mtecs""" flightpathangle = 0.0 ground_speed = self.get_ground_speed_vec(state) ground_speed_length = np.linalg.norm(ground_speed) if ground_speed_length > sys.float_info.epsilon: flightpathangle = -np.arcsin(ground_speed[2] / ground_speed_length) limitoverride = PyLimitOverride() # if (_vehicle_status.engine_failure || _vehicle_status.engine_failure_cmd) { # /* Force the slow downwards spiral */ # limitOverride.enablePitchMinOverride(-1.0f); # limitOverride.enablePitchMaxOverride(5.0f); # } else if (climbout_mode) { # limitOverride.enablePitchMinOverride(M_RAD_TO_DEG_F * climbout_pitch_min_rad); # } else { # limitOverride.disablePitchMinOverride(); # } limitoverride.disablePitchMinOverride() # if (pitch_max_special) { # /* Use the maximum pitch from the argument */ # limitOverride.enablePitchMaxOverride(M_RAD_TO_DEG_F * pitch_max_rad); # } else { # /* use pitch max set by MT param */ # limitOverride.disablePitchMaxOverride(); # } limitoverride.disablePitchMaxOverride() self.c_te.updateAltitudeSpeed(flightpathangle, state["altitude"], setpoint["altitude"], state["airspeed"], setpoint["velocity"], self.c_te.mtecs_mode_normal, limitoverride) return flightpathangle def control(self, **kwargs): """ Input: output: control signal normed [-1 1] """ y = kwargs["state"] r = kwargs["setpoint"] params = kwargs["parameters"] control_data = { "roll_setpoint": r["roll"], "pitch_setpoint": r["pitch"], "yaw_setpoint": r["yaw"], "roll_rate_setpoint": r["roll_rate"], "pitch_rate_setpoint": r["pitch_rate"], "yaw_rate_setpoint": r["yaw_rate"], "airspeed_min": params["airspeed_min"], "airspeed_max": params["airspeed_max"], "altitude_setpoint": r["altitude"], "velocity_setpoint": r["velocity"], } for k, v in y.items(): control_data[k] = v if self.mode == "position": if self.control_count % self.control_total_energy_divider == 0: flightpathangle = self.call_mtecs(y, r) control_data["pitch_setpoint"] = self.c_te.getPitchSetpoint() throttle = self.c_te.getThrottleSetpoint() # save other relevant data control_data[ "airspeed_filtered"] = self.c_te.getAirspeedLowpassState() control_data[ "altitude_filtered"] = self.c_te.getAltitudeLowpassState() control_data["flightpathangle"] = flightpathangle control_data[ "flightpathangle_filtered"] = self.c_te.getFlightPathAngleLowpassState( ) control_data[ "airspeed_derivative_filtered"] = self.c_te.getAirspeedDerivativeLowpassState( ) else: throttle = self.params["throttle_default"] control_data["throttle_setpoint"] = throttle control_data["roll_rate_setpoint"] = self.c_roll.control_attitude( control_data) control_data["pitch_rate_setpoint"] = self.c_pitch.control_attitude( control_data) # control_data["yaw_rate_setpoint"] = self.c_yaw.control_attitude(control_data) control_data["yaw_rate_setpoint"] = 0.0 # XXX # print("control data", control_data) aileron = self.c_roll.control_bodyrate(control_data) elevator = self.c_pitch.control_bodyrate(control_data) rudder = self.c_yaw.control_bodyrate(control_data) u = [aileron, elevator, rudder, throttle] # print("u", u) return [u, control_data]
class FixedWingController: """Controls a fixed wing aircraft""" def __init__(self, params, control_total_energy_divider, mode): """ :param control_total_energy_divider run total enery controller every xth time """ self.mode = mode # since we run on python2, make sure that all params are floats for p in params: params[p] = float(params[p]) self.params = params # Attitude Control self.c_roll = PyECLRollController() self.c_pitch = PyECLPitchController() self.c_yaw = PyECLYawController() att_tc = params["att_tc"] k_p = params["k_p"] k_ff = params["k_ff"] k_i = params["k_i"] i_max = params["i_max"] self.c_roll.set_time_constant(att_tc) self.c_roll.set_k_p(k_p) self.c_roll.set_k_i(k_i) self.c_roll.set_integrator_max(i_max) self.c_roll.set_k_ff(k_ff) self.c_pitch.set_time_constant(att_tc) self.c_pitch.set_k_p(k_p) self.c_pitch.set_k_i(k_i) self.c_pitch.set_integrator_max(i_max) self.c_pitch.set_k_ff(k_ff) self.c_pitch.set_max_rate_pos(params["pitch_max_rate_pos"]) self.c_pitch.set_max_rate_neg(params["pitch_max_rate_neg"]) self.c_pitch.set_roll_ff(params["pitch_roll_ff"]) self.c_yaw.set_time_constant(att_tc) self.c_yaw.set_k_p(k_p) self.c_yaw.set_k_i(k_i) self.c_yaw.set_integrator_max(i_max) self.c_yaw.set_k_ff(k_ff) self.c_yaw.set_coordinated_min_speed(params["coordinated_min_speed"]) self.c_yaw.set_coordinated_method(params["coordinated_method"]) # Altitude and speed control (total energy control) # XXX load values from params argument where it makes sense c_te_params = { "MT_ENABLED": 1, "MT_THR_FF": params["mtecs_throttle_ff"], "MT_THR_P": params["mtecs_throttle_p"], "MT_THR_I": params["mtecs_throttle_i"], "MT_THR_OFF": params["throttle_default"], "MT_PIT_FF": params["mtecs_pitch_ff"], "MT_PIT_P": params["mtecs_pitch_p"], "MT_PIT_I": params["mtecs_pitch_i"], "MT_PIT_OFF": 0.0, "MT_THR_MIN": 0.0, "MT_THR_MAX": 1.0, "MT_PIT_MIN": -45.0, "MT_PIT_MAX": 20.0, "MT_ALT_LP": params["mtecs_altitude_lowpass_cutoff"], "MT_FPA_LP": params["mtecs_flightpathangle_lowpass_cutoff"], "MT_FPA_P": params["mtecs_fpa_p"], "MT_FPA_D": 0.0, "MT_FPA_D_LP": 1.0, "MT_FPA_MIN": -20.0, "MT_FPA_MAX": 30.0, "MT_A_LP": params["mtecs_airspeed_lowpass_cutoff"], "MT_AD_LP": params["mtecs_airspeed_derivative_lowpass_cutoff"], "MT_ACC_P": params["mtecs_acc_p"], "MT_ACC_D": 0.0, "MT_ACC_D_LP": 0.5, "MT_ACC_MIN": -40.0, "MT_ACC_MAX": 40.0, "MT_TKF_THR_MIN": 1.0, "MT_TKF_THR_MAX": 1.0, "MT_TKF_PIT_MIN": 0.0, "MT_TKF_PIT_MAX": 45.0, "MT_USP_THR_MIN": 1.0, "MT_USP_THR_MAX": 1.0, "MT_USP_PIT_MIN": -45.0, "MT_USP_PIT_MAX": 0.0, "MT_LND_THR_MIN": 0.0, "MT_LND_THR_MAX": 0.0, "MT_LND_PIT_MIN": -5.0, "MT_LND_PIT_MAX": 15.0, "MT_THR_I_MAX": 10.0, "MT_PIT_I_MAX": 10.0, "FW_AIRSPD_MIN": params["airspeed_min"], } self.c_te = PyMTecs(c_te_params) self.control_count = 0 self.control_total_energy_divider = control_total_energy_divider def get_ground_speed_vec(self, state): """Retruns a vector of the ground speed""" return np.array([state["speed_body_u"], state["speed_body_v"], state["speed_body_w"],]) def call_mtecs(self, state, setpoint): """helper function to pass the right arguments to mtecs""" flightpathangle = 0.0 ground_speed = self.get_ground_speed_vec(state) ground_speed_length = np.linalg.norm(ground_speed) if ground_speed_length > sys.float_info.epsilon: flightpathangle = -np.arcsin(ground_speed[2]/ground_speed_length) limitoverride = PyLimitOverride() # if (_vehicle_status.engine_failure || _vehicle_status.engine_failure_cmd) { # /* Force the slow downwards spiral */ # limitOverride.enablePitchMinOverride(-1.0f); # limitOverride.enablePitchMaxOverride(5.0f); # } else if (climbout_mode) { # limitOverride.enablePitchMinOverride(M_RAD_TO_DEG_F * climbout_pitch_min_rad); # } else { # limitOverride.disablePitchMinOverride(); # } limitoverride.disablePitchMinOverride(); # if (pitch_max_special) { # /* Use the maximum pitch from the argument */ # limitOverride.enablePitchMaxOverride(M_RAD_TO_DEG_F * pitch_max_rad); # } else { # /* use pitch max set by MT param */ # limitOverride.disablePitchMaxOverride(); # } limitoverride.disablePitchMaxOverride(); self.c_te.updateAltitudeSpeed(flightpathangle, state["altitude"], setpoint["altitude"], state["airspeed"], setpoint["velocity"], self.c_te.mtecs_mode_normal, limitoverride); return flightpathangle def control(self, **kwargs): """ Input: output: control signal normed [-1 1] """ y = kwargs["state"] r = kwargs["setpoint"] params = kwargs["parameters"] control_data = { "roll_setpoint": r["roll"], "pitch_setpoint": r["pitch"], "yaw_setpoint": r["yaw"], "roll_rate_setpoint": r["roll_rate"], "pitch_rate_setpoint": r["pitch_rate"], "yaw_rate_setpoint": r["yaw_rate"], "airspeed_min": params["airspeed_min"], "airspeed_max": params["airspeed_max"], "altitude_setpoint": r["altitude"], "velocity_setpoint": r["velocity"], } for k,v in y.items(): control_data[k] = v if self.mode == "position": if self.control_count % self.control_total_energy_divider == 0: flightpathangle = self.call_mtecs(y, r) control_data["pitch_setpoint"] = self.c_te.getPitchSetpoint() throttle = self.c_te.getThrottleSetpoint() # save other relevant data control_data["airspeed_filtered"] = self.c_te.getAirspeedLowpassState() control_data["altitude_filtered"] = self.c_te.getAltitudeLowpassState() control_data["flightpathangle"] = flightpathangle control_data["flightpathangle_filtered"] = self.c_te.getFlightPathAngleLowpassState() control_data["airspeed_derivative_filtered"] = self.c_te.getAirspeedDerivativeLowpassState() else: throttle = self.params["throttle_default"] control_data["throttle_setpoint"] = throttle control_data["roll_rate_setpoint"] = self.c_roll.control_attitude(control_data) control_data["pitch_rate_setpoint"] = self.c_pitch.control_attitude(control_data) # control_data["yaw_rate_setpoint"] = self.c_yaw.control_attitude(control_data) control_data["yaw_rate_setpoint"] = 0.0 # XXX # print("control data", control_data) aileron = self.c_roll.control_bodyrate(control_data) elevator = self.c_pitch.control_bodyrate(control_data) rudder = self.c_yaw.control_bodyrate(control_data) u = [aileron, elevator, rudder, throttle] # print("u", u) return [u, control_data]
def __init__(self, params, control_total_energy_divider, mode): """ :param control_total_energy_divider run total enery controller every xth time """ self.mode = mode # since we run on python2, make sure that all params are floats for p in params: params[p] = float(params[p]) self.params = params # Attitude Control self.c_roll = PyECLRollController() self.c_pitch = PyECLPitchController() self.c_yaw = PyECLYawController() att_tc = params["att_tc"] k_p = params["k_p"] k_ff = params["k_ff"] k_i = params["k_i"] i_max = params["i_max"] self.c_roll.set_time_constant(att_tc) self.c_roll.set_k_p(k_p) self.c_roll.set_k_i(k_i) self.c_roll.set_integrator_max(i_max) self.c_roll.set_k_ff(k_ff) self.c_pitch.set_time_constant(att_tc) self.c_pitch.set_k_p(k_p) self.c_pitch.set_k_i(k_i) self.c_pitch.set_integrator_max(i_max) self.c_pitch.set_k_ff(k_ff) self.c_pitch.set_max_rate_pos(params["pitch_max_rate_pos"]) self.c_pitch.set_max_rate_neg(params["pitch_max_rate_neg"]) self.c_pitch.set_roll_ff(params["pitch_roll_ff"]) self.c_yaw.set_time_constant(att_tc) self.c_yaw.set_k_p(k_p) self.c_yaw.set_k_i(k_i) self.c_yaw.set_integrator_max(i_max) self.c_yaw.set_k_ff(k_ff) self.c_yaw.set_coordinated_min_speed(params["coordinated_min_speed"]) self.c_yaw.set_coordinated_method(params["coordinated_method"]) # Altitude and speed control (total energy control) # XXX load values from params argument where it makes sense c_te_params = { "MT_ENABLED": 1, "MT_THR_FF": params["mtecs_throttle_ff"], "MT_THR_P": params["mtecs_throttle_p"], "MT_THR_I": params["mtecs_throttle_i"], "MT_THR_OFF": params["throttle_default"], "MT_PIT_FF": params["mtecs_pitch_ff"], "MT_PIT_P": params["mtecs_pitch_p"], "MT_PIT_I": params["mtecs_pitch_i"], "MT_PIT_OFF": 0.0, "MT_THR_MIN": 0.0, "MT_THR_MAX": 1.0, "MT_PIT_MIN": -45.0, "MT_PIT_MAX": 20.0, "MT_ALT_LP": params["mtecs_altitude_lowpass_cutoff"], "MT_FPA_LP": params["mtecs_flightpathangle_lowpass_cutoff"], "MT_FPA_P": params["mtecs_fpa_p"], "MT_FPA_D": 0.0, "MT_FPA_D_LP": 1.0, "MT_FPA_MIN": -20.0, "MT_FPA_MAX": 30.0, "MT_A_LP": params["mtecs_airspeed_lowpass_cutoff"], "MT_AD_LP": params["mtecs_airspeed_derivative_lowpass_cutoff"], "MT_ACC_P": params["mtecs_acc_p"], "MT_ACC_D": 0.0, "MT_ACC_D_LP": 0.5, "MT_ACC_MIN": -40.0, "MT_ACC_MAX": 40.0, "MT_TKF_THR_MIN": 1.0, "MT_TKF_THR_MAX": 1.0, "MT_TKF_PIT_MIN": 0.0, "MT_TKF_PIT_MAX": 45.0, "MT_USP_THR_MIN": 1.0, "MT_USP_THR_MAX": 1.0, "MT_USP_PIT_MIN": -45.0, "MT_USP_PIT_MAX": 0.0, "MT_LND_THR_MIN": 0.0, "MT_LND_THR_MAX": 0.0, "MT_LND_PIT_MIN": -5.0, "MT_LND_PIT_MAX": 15.0, "MT_THR_I_MAX": 10.0, "MT_PIT_I_MAX": 10.0, "FW_AIRSPD_MIN": params["airspeed_min"], } self.c_te = PyMTecs(c_te_params) self.control_count = 0 self.control_total_energy_divider = control_total_energy_divider