예제 #1
0
    def _update_velocity_data(self, wind=np.zeros((6, 1))):
        #   The first three elements are the steady state wind in the inertial frame
        #   The second three elements are the gust in the body frame

        Vw_b = Quaternion2Rotation(self._state[6], self._state[7], self._state[8], self._state[9]) \
                @ np.array([wind[0],
                            wind[1],
                            wind[2]]) + np.array([wind[3],
                                                  wind[4],
                                                  wind[5]])

        Va_b = np.array([[self._state[3]],
                          [self._state[4]],
                          [self._state[5]]]) - Vw_b
        # compute airspeed (in body frame):
        self._Va = np.linalg.norm(Va_b)
        if self._Va == 0:
            self._alpha = 0
            self._beta = 0

        else:
            # compute angle of attack
            self._alpha = np.arctan2(Va_b.item(2), Va_b.item(0))
            self._alpha = self._alpha.item(0)

            # compute sideslip angle
            self._beta = np.arcsin(Va_b.item(1) / self._Va)

        # compute ground velocity
        Vg_v = Quaternion2Rotation(self._state[6], self._state[7], self._state[8], self._state[9]).T @ (Va_b + Vw_b)
        self._Vg = np.linalg.norm(Vg_v)

        self._chi = np.arctan2(Vg_v.item(1), Vg_v.item(0))
        self._gamma = np.arctan2(Vg_v[2], Vg_v.item(0))
예제 #2
0
    def calcForcesAndMoments(self, delta):
        # Calculate gravitational forces in the body frame
        Rb_v = Quaternion2Rotation(self._state[6:10]).T
        fb_grav = Rb_v @ np.array([[0, 0, MAV.mass * MAV.gravity]]).T

        # Calculating longitudinal forces and moments
        fx, fz, m = self.calcLongitudinalForcesAndMoments(delta.item(0))
        fx += fb_grav.item(0)
        fz += fb_grav.item(2)

        # Calculating lateral forces and moments
        fy, l, n = self.calcLateralForcesAndMoments(delta.item(2),
                                                    delta.item(3))
        fy += fb_grav.item(1)

        # Propeller force and moments
        #These may act a little fast
        fp, qp = self.calcThrustForceAndMoment(delta.item(1), self._Va)
        fx += fp
        l += -qp

        self._forces[0] = fx
        self._forces[1] = fy
        self._forces[2] = fz
        return np.array([[fx, fy, fz, l, m, n]]).T
예제 #3
0
    def _forces_moments(self, delta):
        """
        return the forces on the UAV based on the state, wind, and control surfaces
        :param delta: np.matrix(delta_a, delta_e, delta_r, delta_t)
        :return: Forces and Moments on the UAV np.matrix(Fx, Fy, Fz, Ml, Mn, Mm)
        """
        fb_grav = Quaternion2Rotation(self._state[6:10]).T @ np.array(
            [[0, 0, MAV.mass * MAV.gravity]]).T

        # longitudinal forces and moments
        fx, fz, m = self.calcLonDynamics(delta.item(0))
        fx += fb_grav.item(0)
        fz += fb_grav.item(2)

        # lateral forces and moments
        fy, l, n = self.calcLatDynamics(delta.item(2), delta.item(3))
        fy += fb_grav.item(1)

        # propeller/motor forces and moments
        fp, mp = self.calcMotorDynamics(self._Va, delta.item(1))
        fx += fp
        l -= mp

        self._forces[0] = fx
        self._forces[1] = fy
        self._forces[2] = fz
        return np.array([[fx, fy, fz, l, m, n]]).T
예제 #4
0
    def _update_velocity_data(self, wind=np.zeros((6, 1))):
        Rb_v = Quaternion2Rotation(self._state[6:10])

        self._wind = Rb_v.T @ wind[:3] + wind[3:]
        V = self._state[3:6]
        Vr = V - self._wind

        # compute airspeed
        self._Va = np.linalg.norm(Vr)

        ur = Vr.item(0)
        vr = Vr.item(1)
        wr = Vr.item(2)

        # compute angle of attack
        if ur == 0:
            self._alpha = np.sign(wr) * np.pi / 2.0
        else:
            self._alpha = np.arctan2(wr, ur)

        # compute sideslip angle
        if ur == 0 and wr == 0:
            self._beta = np.sign(vr) * np.pi / 2.0
        else:
            self._beta = np.arcsin(vr / self._Va)
예제 #5
0
    def _update_velocity_data(self, wind=np.zeros((6, 1))):
        # Pull the wind data
        steady_state = wind[0:3]
        gust = wind[3:6]

        # Pull MAV angles and rotate
        e = self._state[6:10]
        R = Quaternion2Rotation(e)

        # Rotate steady to body and add gusts
        steady_state_NED = R.T @ steady_state + gust

        # Calculate airspeed components
        u, v, w = self._state[3:6]

        # Get relative u,v,w componentns in NED
        u_r = u - steady_state_NED[0]
        v_r = v - steady_state_NED[1]
        w_r = w - steady_state_NED[2]

        # compute airspeed
        self._Va = np.sqrt(u_r**2 + v_r**2 + w_r**2).item(0)
        # compute angle of attack
        self._alpha = np.arctan(w_r / u_r).item(0)
        # compute sideslip angle
        self._beta = np.sin(v_r / self._Va).item(0)
예제 #6
0
    def _derivatives(self, state, forces_moments):
        """
        for the dynamics xdot = f(x, u), returns f(x, u)
        """
        # extract the states
        pn = state.item(0)
        pe = state.item(1)
        pd = state.item(2)
        u = state.item(3)
        v = state.item(4)
        w = state.item(5)
        e0 = state.item(6)
        e1 = state.item(7)
        e2 = state.item(8)
        e3 = state.item(9)
        p = state.item(10)
        q = state.item(11)
        r = state.item(12)
        #   extract forces/moments
        fx = forces_moments.item(0)
        fy = forces_moments.item(1)
        fz = forces_moments.item(2)
        l = forces_moments.item(3)
        m = forces_moments.item(4)
        n = forces_moments.item(5)

        # position kinematics
        Rv_b = Quaternion2Rotation(np.array([e0, e1, e2, e3]))

        pos_dot = Rv_b @ np.array([u, v, w]).T
        pn_dot = pos_dot.item(0)
        pe_dot = pos_dot.item(1)
        pd_dot = pos_dot.item(2)

        # position dynamics
        u_dot = r * v - q * w + 1 / MAV.mass * fx
        v_dot = p * w - r * u + 1 / MAV.mass * fy
        w_dot = q * u - p * v + 1 / MAV.mass * fz

        # rotational kinematics
        e0_dot = (-p * e1 - q * e2 - r * e3) * 0.5
        e1_dot = (p * e0 + r * e2 - q * e3) * 0.5
        e2_dot = (q * e0 - r * e1 + p * e3) * 0.5
        e3_dot = (r * e0 + q * e1 - p * e2) * 0.5

        # rotatonal dynamics
        p_dot = MAV.gamma1 * p * q - MAV.gamma2 * q * r + MAV.gamma3 * l + MAV.gamma4 * n
        q_dot = MAV.gamma5 * p * r - MAV.gamma6 * (p**2 -
                                                   r**2) + 1 / MAV.Jy * m
        r_dot = MAV.gamma7 * p * q - MAV.gamma1 * q * r + MAV.gamma4 * l + MAV.gamma8 * n

        # collect the derivative of the states
        x_dot = np.array([[
            pn_dot, pe_dot, pd_dot, u_dot, v_dot, w_dot, e0_dot, e1_dot,
            e2_dot, e3_dot, p_dot, q_dot, r_dot
        ]]).T
        return x_dot
예제 #7
0
    def _update_gamma_chi(self):
        Rv_b = Quaternion2Rotation(self._state[6:10])
        Vg = Rv_b @ self._state[3:6]

        gamma = asin(-Vg.item(2) / np.linalg.norm(Vg))
        self.msg_true_state.gamma = gamma

        #Vg_horz = Vg * np.cos(gamma)
        #e1 = np.array([[1,0,0]]).T
        #chi = acos(e1.T @ Vg_horz / np.linalg.norm(Vg_horz))
        #if Vg_horz.item(1) < 0:
        #    chi *= -1
        chi = np.arctan2(Vg.item(1), Vg.item(0))
        self.msg_true_state.chi = chi
예제 #8
0
    def updateVelocityData(self, wind=np.zeros((6, 1))):
        Rb_v = Quaternion2Rotation(self._state[6:10]).T
        #wind in body frame
        self._wind = Rb_v @ wind[0:3] + wind[3:]
        V = self._state[3:6]

        #Compute Va
        Vr = V - self._wind
        self._Va = np.linalg.norm(Vr)

        #Compute alpha
        self._alpha = np.arctan2(Vr.item(2), Vr.item(0))

        #Compute beta
        self._beta = asin(Vr.item(1)/self._Va)
예제 #9
0
    def calcGammaAndChi(self):
        Rv_b = Quaternion2Rotation(self._state[6:10])
        Vg = Rv_b @ self._state[3:6]

        gamma = asin(
            -Vg.item(2) /
            np.linalg.norm(Vg))  #negative because h_dot = Vg sin(gamma)
        self.msg_true_state.gamma = gamma

        Vg_horz = Vg * np.cos(gamma)
        e1 = np.array([[1, 0, 0]]).T

        chi = acos(np.dot(e1.T, Vg_horz) / np.linalg.norm(Vg_horz))
        if (Vg_horz.item(1) < 0):
            chi *= -1
        self.msg_true_state.chi = chi
예제 #10
0
    def updateVelocityData(self, wind=np.zeros((6, 1))):
        Rb_v = Quaternion2Rotation(self._state[6:10]).T
        #wind in body frame
        self._wind = Rb_v @ wind[0:3] + wind[3:]
        V = self._state[3:6]

        #Compute Va
        Vr = V - self._wind
        self._Va = np.linalg.norm(Vr)

        #Compute alpha
        if Vr.item(0) == 0:
            self._alpha = np.sign(Vr.item(2)) * np.pi / 2.0
        else:
            self._alpha = np.arctan2(Vr.item(2), Vr.item(0))

        #Compute beta
        temp = np.sqrt(Vr.item(0)**2 + Vr.item(2)**2)
        if temp == 0:
            self._beta = np.sign(Vr.item(1)) * np.pi / 2.0
        else:
            self._beta = asin(Vr.item(1) / self._Va)
예제 #11
0
    def calcForcesAndMoments(self, delta):
        # Calculate gravitational forces in the body frame
        fb_grav = Quaternion2Rotation(self._state[6:10]).T @ np.array(
            [[0, 0, MAV.mass * MAV.gravity]]).T

        # Calculating longitudinal forces and moments
        fx, fz, m = self.calcLongitudinalForcesAndMoments(delta.item(0))
        fx += fb_grav[0]
        fz += fb_grav[2]

        # Calculating lateral forces and moments
        fy, l, n = self.calcLateralForcesAndMoments(delta.item(2),
                                                    delta.item(3))
        fy += fb_grav[1]

        # Propeller force and moments
        #These may act a little fast
        fp, mp = self.calcThrustForceAndMoment(delta.item(1))
        fx += fp
        l += mp

        return np.array([fx, fy, fz, l, m, n])
 def _update_msg_true_state(self):
     # update the class structure for the true state:
     #   [pn, pe, h, Va, alpha, beta, phi, theta, chi, p, q, r, Vg, wn, we, psi, gyro_bx, gyro_by, gyro_bz]
     phi, theta, psi = Quaternion2Euler(self._state[6:10])
     pdot = Quaternion2Rotation(self._state[6:10]) @ self._state[3:6]
     self.msg_true_state.pn = self._state.item(0)
     self.msg_true_state.pe = self._state.item(1)
     self.msg_true_state.h = -self._state.item(2)
     self.msg_true_state.Va = self._Va
     self.msg_true_state.alpha = self._alpha
     self.msg_true_state.beta = self._beta
     self.msg_true_state.phi = phi
     self.msg_true_state.theta = theta
     self.msg_true_state.psi = psi
     self.msg_true_state.Vg = np.linalg.norm(pdot)
     self.msg_true_state.gamma = np.arcsin(
         pdot.item(2) / self.msg_true_state.Vg)
     self.msg_true_state.chi = np.arctan2(pdot.item(1), pdot.item(0))
     self.msg_true_state.p = self._state.item(10)
     self.msg_true_state.q = self._state.item(11)
     self.msg_true_state.r = self._state.item(12)
     self.msg_true_state.wn = self._wind.item(0)
     self.msg_true_state.we = self._wind.item(1)
예제 #13
0
    def _forces_moments(self, delta):
        """
        return the forces on the UAV based on the state, wind, and control surfaces
        :param delta: np.matrix(delta_a, delta_e, delta_r, delta_t)
        :return: Forces and Moments on the UAV np.matrix(Fx, Fy, Fz, Ml, Mn, Mm)
        """
        mass = UAV.mass
        g = UAV.gravity
        rho = UAV.rho
        S = UAV.S_wing
        a = self._alpha
        beta = self._beta
        b = UAV.b
        c = UAV.c
        p = self._state.item(10)
        q = self._state.item(11)
        r = self._state.item(12)
        Va = self._Va

        # control surface offsets
        delta_a = delta.item(0)
        delta_e = delta.item(1)
        delta_r = delta.item(2)
        delta_t = delta.item(3)
        if delta_t < 0:
            delta_t = 0.0

        # drag coefficients
        C_D_q = UAV.C_D_q
        C_L_q = UAV.C_L_q
        C_D_de = UAV.C_D_delta_e
        C_L_de = UAV.C_L_delta_e
        C_Y_0 = UAV.C_Y_0
        C_Y_b = UAV.C_Y_beta
        C_Y_p = UAV.C_Y_p
        C_Y_r = UAV.C_Y_r
        C_Y_da = UAV.C_Y_delta_a
        C_Y_dr = UAV.C_Y_delta_r
        C_l_0 = UAV.C_ell_0
        C_l_b = UAV.C_ell_beta
        C_l_p = UAV.C_ell_p
        C_l_r = UAV.C_ell_r
        C_l_da = UAV.C_ell_delta_a
        C_l_dr = UAV.C_ell_delta_r
        C_m_0 = UAV.C_m_0
        C_m_a = UAV.C_m_alpha
        C_m_q = UAV.C_m_q
        C_m_de = UAV.C_m_delta_e
        C_n_0 = UAV.C_n_0
        C_n_b = UAV.C_n_beta
        C_n_p = UAV.C_n_p
        C_n_r = UAV.C_n_r
        C_n_da = UAV.C_n_delta_a
        C_n_dr = UAV.C_n_delta_r

        f_g = Quaternion2Rotation(self._state[6:10]).transpose() @ np.array(
            [[0], [0], [mass * g]])  # gravitational force

        # Propeller Thrust Calculations
        # map delta throttle command (0 to 1) into motor input voltage
        V_in = UAV.V_max * delta_t

        # Quadratic formula to solve for motor speed
        a1 = UAV.rho * UAV.D_prop**5 / ((2.0 * np.pi)**2) * UAV.C_Q0
        b1 = UAV.rho * UAV.D_prop**4 / (2.0 * np.pi) * UAV.C_Q1 * self._Va + (
            UAV.KQ**2) / UAV.R_motor
        c1 = UAV.rho * UAV.D_prop**3 * UAV.C_Q2 * self._Va**2 - UAV.KQ / UAV.R_motor * V_in + UAV.KQ * UAV.i0

        # Consider only positive root
        Omega_op = (-b1 + np.sqrt(b1**2 - 4 * a1 * c1)) / (2.0 * a1)

        # compute advance ratio
        J_op = 2 * np.pi * self._Va / (Omega_op * UAV.D_prop)

        # compute non-dimensionalized coefficients of thrust and torque
        C_T = UAV.C_T2 * J_op**2 + UAV.C_T1 * J_op + UAV.C_T0
        C_Q = UAV.C_Q2 * J_op**2 + UAV.C_Q1 * J_op + UAV.C_Q0

        # add thrust and torque due to propeller
        n = Omega_op / (2.0 * np.pi)
        f_p = np.array([[UAV.rho * n**2 * UAV.D_prop**4 * C_T], [0], [0]])
        m_p = np.array([[-UAV.rho * n**2 * UAV.D_prop**5 * C_Q], [0], [0]])

        # # Alternative Simplified Thrust model
        # f_p = (0.5*UAV.rho*UAV.S_prop*UAV.C_prop) * np.array([[(UAV.k_motor*delta_t)**2 - self.msg_true_state.Va**2], [0], [0]])  # prop thrust
        # m_p = np.array([[-UAV.kTp*(UAV.kOmega*delta_t)**2], [0], [0]])  # prop torque

        C_D = lambda alpha: UAV.C_D_p + (UAV.C_L_0 + UAV.C_L_alpha * alpha
                                         )**2 / (np.pi * UAV.e * UAV.AR)
        sig = lambda alpha: (1 + np.exp(-UAV.M * (alpha - UAV.alpha0)) + np.
                             exp(UAV.M * (alpha + UAV.alpha0))) / (
                                 (1 + np.exp(-UAV.M * (alpha - UAV.alpha0))) *
                                 (1 + np.exp(UAV.M * (alpha + UAV.alpha0))))
        C_L = lambda alpha: (1 - sig(alpha)) * (
            UAV.C_L_0 + UAV.C_L_alpha * alpha) + sig(alpha) * (2 * np.sign(
                alpha) * np.sin(alpha)**2 * np.cos(alpha))
        C_X = lambda alpha: -C_D(alpha) * np.cos(alpha) + C_L(alpha) * np.sin(
            alpha)
        C_X_q = lambda alpha: -C_D_q * np.cos(alpha) + C_L_q * np.sin(alpha)
        C_X_de = lambda alpha: -C_D_de * np.cos(alpha) + C_L_de * np.sin(alpha)
        C_Z = lambda alpha: -C_D(alpha) * np.sin(alpha) - C_L(alpha) * np.cos(
            alpha)
        C_Z_q = lambda alpha: -C_D_q * np.sin(alpha) - C_L_q * np.cos(alpha)
        C_Z_de = lambda alpha: -C_D_de * np.sin(alpha) - C_L_de * np.cos(alpha)

        f_a = (0.5 * rho * Va**2 * S) * np.array(
            [[C_X(a) + C_X_q(a) * (c / (2 * Va)) * q + C_X_de(a) * delta_e],
             [
                 C_Y_0 + C_Y_b * beta + C_Y_p * (b / (2 * Va)) * p + C_Y_r *
                 (b / (2 * Va)) * r + C_Y_da * delta_a + C_Y_dr * delta_r
             ], [C_Z(a) + C_Z_q(a) * (c /
                                      (2 * Va)) * q + C_Z_de(a) * delta_e]])
        f_total = f_g + f_a + f_p
        self.thrust = f_p.item(0)
        self._forces = np.array([[f_total.item(0)], [f_total.item(1)],
                                 [f_total.item(2)]])

        m_a = (0.5 * rho * Va**2 * S) * np.array(
            [[
                b * (C_l_0 + C_l_b * beta + C_l_p * (b /
                                                     (2 * Va)) * p + C_l_r *
                     (b / (2 * Va)) * r + C_l_da * delta_a + C_l_dr * delta_r)
            ],
             [
                 c * (C_m_0 + C_m_a * a + C_m_q *
                      (c / (2 * Va)) * q + C_m_de * delta_e)
             ],
             [
                 b * (C_n_0 + C_n_b * beta + C_n_p * (b /
                                                      (2 * Va)) * p + C_n_r *
                      (b / (2 * Va)) * r + C_n_da * delta_a + C_n_dr * delta_r)
             ]])
        m_total = m_a + m_p

        return np.array([[
            f_total.item(0),
            f_total.item(1),
            f_total.item(2),
            m_total.item(0),
            m_total.item(1),
            m_total.item(2)
        ]]).T