四旋翼无人机动力学与控制(1)-动力学模型

笔者近来开始接触四旋翼无人机,学习过程中写了一份有关无人机动力学与控制的python代码,在此做一些记录和原理解释。

附上仓库地址:四旋翼无人机仿真建模: 本仓库实现了四旋翼无人机的动力学仿真建模,并在此基础上完成了控制器的设计,包括串级PID、LQR、MPC等,实现了位置跟踪和路径规划。

总体框架与思路如图所示,四旋翼无人机的运动模型可以简单的看作是十二个状态变量(位置、速度、角度、角速度各三个变量)随运动而发生改变的过程,运动学方程如下图右半部分所示。

从框架讲起,作为一名飞行器控制专业的学生,四旋翼无人机在我眼中并不算是严格意义上的飞行器,我对其的理解更偏向于robotic,与固定翼相比,四旋翼的动力学模型更简单,我浅显的理解为依靠四个轴上的电机带动螺旋桨提供升力,通过调节电机转速制造升力差引起角度的偏转进而带动无人机运动,因此就引出了四旋翼无人机常见的分级控制策略,分为角速度、姿态、速度和位置由内到外的四个环,详细内容将在下一篇文章展开。

笔者构建的动力学模型分为上图的几个部分,无人机构型为x型,定义电机顺序如上图左上所示,电机作为执行器,近似看作一个一阶延迟环节,控制分配器由升力和力矩在四轴上的分解和合成计算得出。

选取四阶龙格库塔法进行状态更新,由此可以开始编写程序了:

common_function:

# Author:gyx
# Usage: common function
# Last change: 2024-7-20

import numpy as np


def dcm_from_euler(euler_angles):
    """
    计算方向余弦矩阵(DCM)(Earth2Body)
    :param roll: 滚转角,单位为弧度
    :param pitch: 俯仰角,单位为弧度
    :param yaw: 偏航角,单位为弧度
    :return: 方向余弦矩阵(DCM)
    """
    roll, pitch, yaw = euler_angles
    c_roll = np.cos(roll)
    s_roll = np.sin(roll)
    c_pitch = np.cos(pitch)
    s_pitch = np.sin(pitch)
    c_yaw = np.cos(yaw)
    s_yaw = np.sin(yaw)

    # 构建方向余弦矩阵
    dcm = np.array([
        [c_yaw * c_pitch, c_yaw * s_pitch * s_roll - s_yaw * c_roll, c_yaw * s_pitch * c_roll + s_yaw * s_roll],
        [s_yaw * c_pitch, s_yaw * s_pitch * s_roll + c_yaw * c_roll, s_yaw * s_pitch * c_roll - c_yaw * s_roll],
        [-s_pitch, c_pitch * s_roll, c_pitch * c_roll]
    ])
    return dcm


def rotation_matrix(euler_angles):  # (Body2Earth)
    phi, theta, psi = euler_angles
    R_x = np.array([[1, 0, 0],
                    [0, np.cos(phi), -np.sin(phi)],
                    [0, np.sin(phi), np.cos(phi)]])

    R_y = np.array([[np.cos(theta), 0, np.sin(theta)],
                    [0, 1, 0],
                    [-np.sin(theta), 0, np.cos(theta)]])

    R_z = np.array([[np.cos(psi), -np.sin(psi), 0],
                    [np.sin(psi), np.cos(psi), 0],
                    [0, 0, 1]])

    return R_z @ R_y @ R_x


def angle_body2world(euler_angles):
    phi, theta, psi = euler_angles
    Trans_matrix = np.array([[1, np.tan(theta) * np.sin(phi), np.tan(theta) * np.cos(phi)],
                             [0, np.cos(phi), -np.sin(phi)],
                             [0, np.sin(phi) / np.cos(theta), np.cos(phi) / np.cos(theta)]])
    return Trans_matrix


def rk4_step(f, state, u, t, dt):
    k1 = f(state, u, t)
    print([x * 0.5 * dt for x in k1])
    k2 = f(state + [x * 0.5 * dt for x in k1], u, t + 0.5 * dt)
    k3 = f(state + [x * 0.5 * dt for x in k2], u, t + 0.5 * dt)
    k4 = f(state + [x  * dt for x in k3], u, t + dt)

    return state + (dt / 6) * (k1 + 2 * k2 + 2 * k3 + k4)

dynamic:

# Author:gyx
# Usage: build uav dynamics model
# Last change: 2024-7-20

import numpy as np
from common_function import dcm_from_euler, rotation_matrix


class UAVDynamics:
    def __init__(self, params):
        self.params = params
        self.w_current = params.ModelInit_Rads

    def get_angular_velocity_derivative(self, M, J, omega):
        return np.linalg.inv(J) @ (M - np.cross(omega, J @ omega))

    def get_linear_velocity_derivative(self, F, m):
        return np.clip(F / m, -0.4, 0.4)

    def Obtain_force_torque(self, w, R, Cm, Ct, Vb, Cd, wb, Cdm, Jrp):
        M_rctcm = np.array([
            [-np.sin(np.pi / 4) * R * Ct, np.sin(np.pi / 4) * R * Ct, np.sin(np.pi / 4) * R * Ct,
             -np.sin(np.pi / 4) * R * Ct],  # Roll torque
            [np.sin(np.pi / 4) * R * Ct, -np.sin(np.pi / 4) * R * Ct, np.sin(np.pi / 4) * R * Ct,
             -np.sin(np.pi / 4) * R * Ct],  # Pitch torque
            [Cm, Cm, -Cm, -Cm]  # Yaw torque
        ])

        Mp = np.dot(M_rctcm, w ** 2)  # Torque

        Fp = np.array([0, 0, -np.sum(Ct * (w ** 2))])  # Thrust
        Fd = -Cd * Vb * np.abs(Vb) * 0.5  # Aerodynamic force
        Md = -Cdm * wb * np.abs(wb)  # Aerodynamic moment

        # Gyro moment
        Ga = np.zeros(3)
        Ga[0] = Jrp * wb[1] * (w[0] + w[1] - w[2] - w[3])
        Ga[1] = Jrp * wb[0] * (-w[0] - w[1] + w[2] + w[3])

        return Fp, Fd, Mp, Md, Ga

    def motor_dynamics(self, w_current, w_target, T, dt):
        dw = (w_target - w_current) / T
        return w_current + dw * dt

    def state_derivative(self, state, w_target,dt):
        velB, angEuler, rateB = state

        m = self.params.ModelParam_uavMass
        J = self.params.ModelParam_uavJ
        g = self.params.ModelParam_envGravityAcc
        uavType = self.params.ModelParam_uavType
        R = self.params.ModelParam_uavR
        Cm = self.params.ModelParam_rotorCm
        Ct = self.params.ModelParam_rotorCt
        Cd = self.params.ModelParam_uavCd
        Cdm = self.params.ModelParam_uavCCm
        Jrp = self.params.ModelParam_motorJm
        T = self.params.ModelParam_motorT

        self.w_current = self.motor_dynamics(self.w_current, w_target, T, dt)
        Fp, Fd, Mp, Md, Ga = self.Obtain_force_torque(self.w_current, R, Cm, Ct, velB, Cd, rateB, Cdm, Jrp)
        dcm = dcm_from_euler(angEuler)
        total_force = Fp + Fd + np.array([0, 0, m * g]).dot(dcm)
        total_torque = Mp + Md + Ga
        velB_dot = self.get_linear_velocity_derivative(total_force, m)
        rateB_dot = self.get_angular_velocity_derivative(total_torque, J, rateB)

        return velB_dot, rateB_dot

    def update_dynamics(self, state, w_target, dt):
        velB, angEuler, rateB = state

        # Runge-Kutta 4th Order Method
        k1_velB_dot, k1_rateB_dot = self.state_derivative(state, w_target,dt)
        k2_velB_dot, k2_rateB_dot = self.state_derivative(
            (velB + 0.5 * k1_velB_dot * dt, angEuler, rateB + 0.5 * k1_rateB_dot * dt), w_target,dt)
        k3_velB_dot, k3_rateB_dot = self.state_derivative(
            (velB + 0.5 * k2_velB_dot * dt, angEuler, rateB + 0.5 * k2_rateB_dot * dt), w_target,dt)
        k4_velB_dot, k4_rateB_dot = self.state_derivative(
            (velB + k3_velB_dot * dt, angEuler, rateB + k3_rateB_dot * dt), w_target,dt)

        velB = velB + (k1_velB_dot + 2 * k2_velB_dot + 2 * k3_velB_dot + k4_velB_dot) * dt / 6
        rateB = rateB + (k1_rateB_dot + 2 * k2_rateB_dot + 2 * k3_rateB_dot + k4_rateB_dot) * dt / 6

        return velB, angEuler, rateB

Last,放上笔者的个人主页:郭昱鑫的个人主页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值