Source code for udaan.control.quadrotor.position_l1

"""L1 adaptive position controller for quadrotor."""

import numpy as np

from .position_pd import PositionPDController


[docs] class PositionL1Controller(PositionPDController): """Quadrotor position controller using L1 adaption."""
[docs] def __init__(self, **kwargs): super().__init__(**kwargs) self.control_initialized = False # MRAC reference states self.mrac_position = np.array([0.0, 0.0, 0.0]) self.mrac_velocity = np.array([0.0, 0.0, 0.0]) self.Gamma = np.array([1000.0, 1000.0, 100000.0]) I3, O3 = np.eye(3), np.zeros((3, 3)) self.F = np.concatenate( (np.concatenate((O3, I3), axis=1), np.concatenate((O3, O3), axis=1)) ) self.G = np.concatenate((O3, I3)) * (1 / self.mass) import control as ctrl self.K, self.P, _ = ctrl.lqr(self.F, self.G, np.eye(6), np.eye(3) * 1e-2) self.yGain = -self.G.T @ self.P # MRAC parameters self.deltamax = 30.0 self.eps = 0.5 self.lpf_cutoff = 5.0 # Low-pass filter cutoff frequency [Hz] # Unmodeled disturbance states. self.delta = np.zeros(3) self.lpf_delta = np.zeros(3) self.delta_dot = np.zeros(3) self.last_t = 0.0 self.last_mrac_input = np.zeros(3)
[docs] def compute(self, *args): t = args[0] self.last_t = t pos, vel = args[1][0], args[1][1] # Desired (reference) trajectory pos_d, vel_d, acc_d = self.setpoint(t) if not self.control_initialized: self.mrac_position = pos self.mrac_velocity = vel self.last_t = t self.control_initialized = True return np.zeros(3) # Compute time step dt = t - self.last_t if dt <= 0: dt = 1e-6 # Update MRAC reference model states self.mrac_position += self.mrac_velocity * dt + 0.5 * self.last_mrac_input * dt * dt self.mrac_velocity += self.last_mrac_input * dt # Update disturbance estimate self.delta += self.delta_dot * dt # Low-pass filter: lpf_delta += dt * cutoff * (delta - lpf_delta) alpha = min(1.0, dt * self.lpf_cutoff) self.lpf_delta = self.lpf_delta + alpha * (self.delta - self.lpf_delta) # Compute errors eta = np.concatenate((pos - pos_d, vel - vel_d)) mrac_eta = np.concatenate((self.mrac_position - pos_d, self.mrac_velocity - vel_d)) # Compute input u = -self.K @ eta + self.mass * (acc_d + self._ge3) u_mrac = -self.K @ mrac_eta + self.mass * (acc_d + self._ge3) # Remove unmodeled disturbance u -= self.lpf_delta u_mrac += self.delta - self.lpf_delta # Projection operator for disturbance prediction eta_tilde = mrac_eta - eta y = self.yGain @ eta_tilde f_ = (np.linalg.norm(self.delta) ** 2 - self.deltamax**2) / (self.eps * self.deltamax**2) df = 2 * self.delta / (self.eps * self.deltamax**2) df_norm_sq = np.dot(df, df) if f_ > 0 and np.dot(y, df) > 0 and df_norm_sq > 1e-10: proj = y - (np.dot(df, y) / df_norm_sq) * df * f_ else: proj = y self.delta_dot = self.Gamma * proj # Update internal state self.last_mrac_input = u_mrac self.last_t = t self.pos_setpoint = pos_d return u