首页 / 资讯中心 / 文章详情

四旋翼仿真从零推导:动力学模型、串级PID与Python代码实现

四旋翼仿真从零推导:动力学模型、串级PID与Python代码实现 ★ FEATURED ARTICLE
1. 为什么值得从公式推导开始做四旋翼仿真1.1 我踩过的直接调现成工具的坑很多朋友一上来就问我四旋翼仿真不是有现成工具吗像Gazebo、MATLAB Simulink里的模块装好就能跑为什么还要自己从公式推导开始做我的回答是如果你只想看飞机飞起来那用现成工具最快但如果你想搞懂飞行器为什么会疯转、为什么定不住、参数调了之后姿态会发生什么变化那你必须从动力学方程开始一步一步推。我自己最初就是图省事直接用现成仿真包结果遇到两个问题。第一黑盒问题。现成工具把模型封装得太好我根本不知道内部用的哪套坐标系定义、欧拉角旋转顺序是什么、输出给电机的油门量做了没有限幅。遇到飞机落地后状态发散这种问题想排查都不知道从哪里下手。第二扩展限制。等我要加一个特定的机架参数、换一种电机响应模型、或者在控制环里插入自定义的滤波器时现成模块的接口反而成了束缚。于是我做了一个决定用Python从零搭一套四旋翼仿真。公式自己推代码自己写跑通了之后再去和真实飞行数据做对照。这个过程的收获比看十遍理论推导还要大。这篇文章就把整套思路、公式推导、代码逻辑和踩坑记录写出来给同样想认真做四旋翼仿真和飞控开发的朋友一个可以直接参考的路线。1.2 仿真到底帮你解决什么问题先说清楚一个概念四旋翼仿真的本质是在计算机里用数学模型复现飞行器的运动规律让控制算法先在虚拟世界犯错而不是直接在真机上炸机。它处于飞控算法开发链路的中段——前面是理论分析后面是硬件在环测试和真机试飞。仿真最核心的价值是三个第一验证控制器逻辑是否正确。比如姿态环的反馈方向有没有反这在真机上试是致命的在仿真里看日志一眼就发现问题。第二提前整定控制参数。虽然仿真参数和真机一定会有偏差但是参数的数量级、临界振荡点、阻尼感在仿真里调出来之后能大大减少真机调参的无效迭代。第三理解系统特性。比如你会发现俯仰和横滚通道几乎对称但偏航通道响应明显慢这是由结构转动惯量决定的而不是控制参数的问题。好明确了仿真能解决什么问题接下来就进入正题把模型的每一块掰开揉碎讲清楚。2. 四旋翼动力学模型从牛顿-欧拉到刚体方程的完整拆解2.1 坐标系约定先定规矩再推导做飞行器仿真第一件事不是列方程而是定义坐标系。坐标系定得不清晰后面所有公式都是空中楼阁。我建议用工程界最常用的两组坐标系地球固定坐标系简称地轴系记作E系原点固定在起飞点X轴指向北、Y轴指向东、Z轴指向下。这是无人机的惯性参考位置和速度都在这个系下表示。机体坐标系简称机体系记作B系原点在飞行器重心X轴指向机头Y轴指向右翼Z轴指向下方。角速度、推力方向都在这个系下表达。这里要特别注意很多教材用北东地NED约定还有一些用东北天ENU。两者会导致旋转矩阵方向完全相反是个特别容易出错的地方。我的习惯是全部使用NED和无人机开源自驾仪的约定保持一致这样后续对接真实飞控数据和传感器数据时更顺畅。从机体系到地轴系的旋转我采用标准的ZYX欧拉角顺序先绕Z轴偏航再绕新的Y轴俯仰最后绕新的X轴横滚。对应的旋转矩阵如下R Rz(ψ) · Ry(θ) · Rx(φ)展开后是一个3×3矩阵每一项都是三角函数组合。这个矩阵是整个仿真里出现频率最高的变量因为推力在机体系下沿负Z轴方向要把它变换到地轴系必须经过R矩阵。我见过不少人在这一步犯迷糊明明推力方向没变为什么机体系下的常值力在地轴系下各个分量都在变因为R矩阵把机体姿态信息带进来了机头指向决定了推力的水平分量方向。弄懂旋转矩阵是彻底理解姿态控制的第一道门槛。2.2 平动方程位置和速度怎么变化现在开始推四旋翼的六自由度刚体方程。先看平动部分它来自牛顿第二定律在地轴系下的投影m · dV/dt m · g R · F_b其中m是飞行器总质量g是重力加速度向量F_b是机体系下四个螺旋桨产生的总推力向量。这里有一个很多初学者会困惑的点为什么力在地轴系下的表达式是R乘以机体系力而不是反过来因为真正的物理矢量是同一个机体系下的F_b只是这个矢量在机体各轴上的分量乘以R矩阵后就变成了它在世界系各轴上的分量。F_b的形式是F_b [0, 0, -T]T是四个螺旋桨的总拉力方向沿机体系负Z轴产生升力所以是负号。把重力加速度也投影进去地轴系下Z轴向下所以重力向量是[0, 0, g]。这样平动方程整理下来就是三个一阶微分方程对应地轴系下三个方向的速度变化dVx/dt (R[0][2] · (-T)) / m dVy/dt (R[1][2] · (-T)) / m dVz/dt g (R[2][2] · (-T)) / m仔细看一下这三个式子会发现一个很有意思的结论在悬停平衡点附近飞行器的水平速度完全靠倾转机体来产生——R矩阵中水平分量的存在意味着机头必须倾斜推力才有水平分量。这就是四旋翼通过倾斜产生水平加速度这句常说的话的数学来源。位置和速度的关系很简单dP/dt VP是地轴系下的位置向量。到这一步平动部分就闭环了。2.3 转动方程姿态角速度怎么变化转动方程比平动方程复杂一些因为刚体绕质心的转动遵循欧拉方程而且在机体坐标系下表达最方便J · dω/dt ω × (J · ω) M_b其中J是3×3转动惯量矩阵。对于四旋翼这种近似对称的结构常把惯量矩阵简化为对角阵绕X轴的Ixx、绕Y轴的Iyy、绕Z轴的Izz。如果机架严格对称Ixx等于Iyy但实际装机时因为各个部件摆放不对称一般Ixx和Iyy略有差异但数量级接近。把叉积项展开得到三个标量方程Ixx · dωx/dt Mx (Iyy - Izz) · ωy · ωz Iyy · dωy/dt My (Izz - Ixx) · ωz · ωx Izz · dωz/dt Mz (Ixx - Iyy) · ωx · ωy这里有三个力矩Mx、My、Mz分别来自螺旋桨的推力差和反扭矩差具体来源下一章详细说。叉积项(Iyy - Izz)·ωy·ωz是陀螺力矩耦合项低速飞行时它很小但如果做大机动动作飞机快速翻滚时这个耦合会很显著这也是为什么仿真里不能简单写成三个独立的单通道模型。得到了机体系下的角速度还需要把角速度换算成欧拉角速率才能获得地轴系下的姿态角。这一步的转换关系是dφ/dt ωx ωy·sinφ·tanθ ωz·cosφ·tanθ dθ/dt ωy·cosφ - ωz·sinφ dψ/dt (ωy·sinφ ωz·cosφ) / cosθ注意这里面出现了tanθ和1/cosθ。当俯仰角接近±90度时这些项会趋于无穷大这就是欧拉角表示的万向节锁问题。很多仿真代码跑大角度机动时突然爆掉根源就在这。下一节我会详细讲这个问题以及我在代码里的处理方式。2.4 欧拉角与万向节锁仿真中容易忽略的雷区写到这里就必须聊聊欧拉角的局限性了。上面那套角速度到欧拉角的转换方程在俯仰角为90度时会数值发散。飞控开发里这确实是个让人头疼的问题。我在仿真里遇到过这样一次把期望滚转角设成60度的同时俯仰角拉到接近80度按理说控制环应该能跟住但位置环突然输出了一组异常大的角速度指令姿态角直接跳到NaN。查了半天问题就出在欧拉角转换方程里出现了1/cosθ发散。解决方案有两条路线。第一条限制姿态角的仿真范围不允许俯仰角超过70度。四旋翼一般确实不会长期大角度飞行悬停姿态角通常都在15度以内短时间机动也很少超过60度。第二条改用四元数表示姿态从根本上避免万向节锁。我在最终代码里两条路线都用了控制环内部为了方便人看仍然用欧拉角做参考输入但动力学更新的核心部分改用四元数。四元数的更新方程是dq/dt 0.5 · q ⊗ ω_quat其中ω_quat [0, ωx, ωy, ωz]符号⊗表示四元数乘法。每次积分后将四元数归一化再转回欧拉角用于显示和控制反馈。这样既避免了发散问题又能保持直觉上的可读性。有了完整的刚体运动方程下一步要解决的是力和力矩到底怎么算也就是电机和螺旋桨模型。3. 电机与螺旋桨模型力和力矩的源头3.1 螺旋桨拉力与反扭矩为什么四个桨转速各不相同四旋翼的升力来源是四个螺旋桨转动时产生的气动力。工程建模中我们不需要精确计算桨叶表面每一点的压力分布而是用简化的二次关系来描述F_i k_t · Ω_i²这里F_i是第i个螺旋桨产生的拉力Ω_i是第i个电机的转速k_t是拉力系数它和桨的直径、桨距、空气密度都有关系。真实的螺旋桨拉力确实和转速平方成正比这个关系在悬停和中等转速范围内近似成立。同时旋转的螺旋桨还会产生反扭矩方向和桨的转动方向相反M_i k_m · Ω_i²k_m是反扭矩系数。为了平衡这个反扭矩四旋翼的四个桨是两两正反装的——1号和3号桨顺时针、2号和4号桨逆时针。偏航控制靠的就是打破正反桨力矩的平衡这也是四旋翼偏航通道的基本原理。这两条公式看着简单但整个四旋翼控制的力矩分配就是从它们出发的。让我继续往下推。3.2 混控矩阵从期望推力力矩到四个电机转速现在关键问题来了飞控层输出的是一组期望值——总推力T和三个力矩Mx、My、Mz但实际执行器是四个电机。怎么把4个期望物理量分配到4个电机转速上列一下几何关系。假设电机臂长为L以X型机架为例四个电机的位置分布在机架对角方向电机1在机头右前方产生绕X轴正方向的滚转力矩分量电机2在机头左后方电机3在机尾左前方电机4在机尾右后方每个电机贡献的拉力对质心产生的力矩是力臂叉乘力。把这些关系整理成矩阵形式[ T ] [ 1 1 1 1 ] [ F1 ] [ Mx] [ l1x l2x l3x l4x ] [ F2 ] [ My] [ l1y l2y l3y l4y ] [ F3 ] [ Mz] [ -c c -c c ] [ F4 ]最后一行的正负号和电机安装方向有关用于产生偏航力矩c等于反扭矩系数除以拉力系数。实际工程中这4×4矩阵的数值还要考虑信号极性、电机转向验证等细节。xAircraft里常用的混控矩阵写法虽然形式略不同但本质都是这样一个线性映射。既然要分配的目标是4个电机拉力而方程组的矩阵是常值、可逆的那么直接求逆矩阵就能从期望的T、Mx、My、Mz算出每个电机需要的拉力再反解转速Ω_i sqrt(F_i / k_t)这样闭环就通了控制器算出期望力矩混控矩阵把它分解成四个电机转速指令电机响应、产生拉力和力矩刚体动力学方程更新姿态和位置。整套仿真的数据流就是这样串起来的。3.3 电机响应模型一个常常被忽略的延迟环节很多仿真教程到这里就停了——给出拉力公式混控矩阵完事。但实际飞行中你会发现电机转速不是瞬间到达指令值的它有一套电气和机械动态。这个响应延迟对控制带宽的影响非常大尤其是整定姿态环参数的时候。我把电机简化为一阶惯性环节dΩ_i/dt (Ω_cmd_i - Ω_i) / τ其中Ω_cmd_i是混控矩阵输出给电机的目标转速τ是时间常数一般小电机的响应时间在20毫秒到50毫秒之间。一阶惯性环节的含义是转速以指数方式逼近目标值时间常数越大响应越慢。这个参数的引入会显著改变仿真行为。同一个PID参数电机模型用瞬时响应仿真能稳住的加上30毫秒延迟之后很可能高频振荡——这是我在实际仿真中反复遇到的情况。反过来这也让你调出来的控制器带宽更接近真实飞行器的物理极限。此外还有执行器饱和问题就是油门指令有个上界和下界电机转速不可能无限大也不可能为负。由于混控矩阵在某些情况下比如需求很大的偏航力矩时可能解出负的拉力所以饱和处理必须放在混控输出之后。这看似是个小细节实际影响控制效果很大。4. 控制回路设计从姿态环到位置环的级联结构4.1 串级PID外环里环各自干什么四旋翼自动控制的典型结构是串级PID核心思想是把位置控制和姿态控制分开外环算该往哪个方向倾斜内环算该出多大角速度。为什么用串级而不是单环最直接的原因是内环角速度环需要非常快的响应外环角度环/位置环只需要较慢的响应两者分开后各自可以用合适的控制周期和增益。真机上角速度环通常跑200到500赫兹角度环100到250赫兹而位置环往往只有10到50赫兹。这种频率差直接决定了每个环的更新周期设置仿真时也要按这个层次去设定。串级的另一个好处是抗扰动能力更强。比如一阵风突然把飞机吹倾了角度环还没反应过来角速度内环已经捕捉到了角速度的变化开始主动抵抗。这就好比一个人站不稳的时候中枢神经先通过肌肉张力变化稳住身体而不是等大脑做完视觉判断再去指挥。4.2 姿态环设计角速度内环加角度外环姿态环的目标是让实际欧拉角跟随期望欧拉角。典型实现方式如下内环角速度环输入是角速度误差输出是期望力矩。这里用PD控制就足够。外环角度环输入是姿态角误差输出是期望角速度。这里通常用P控制加上一个前馈项改善动态响应。写出来就是这样期望角速度 Kp_angle · (期望角度 - 实际角度) 期望力矩 Kp_rate · (期望角速度 - 实际角速度) Kd_rate · (期望角加速度 - 实际角加速度)注意角速度环的D项用的是角加速度误差这能起到阻尼作用。在仿真里因为我们可以直接读到真实的角速度这个D项比真机上用陀螺仪估算更干净不容易引入噪声放大问题。姿态角控制有个诀窍期望角速度不要直接等于比例输出最好加上角度环输出的前馈量。否则当期望角度持续变化时内环会一直存在一个跟踪误差这个误差在大机动时会明显限制响应速度。我通常的做法是把角度环的P输出乘以比例系数后直接作为内环的期望角速度同时再加上期望角度的微分项作为前馈。4.3 位置环设计期望姿态是怎么生成的位置环是控制器最外层的部分它接收期望位置或期望速度输出期望的滚转角和俯仰角。原理如下。水平位置误差通过P控制器换算成期望的水平加速度有了水平加速度反推需要的机体倾斜角。由于水平加速度近似等于g·tan(倾斜角)所以期望滚转角 atan2(期望Y轴加速度, g) 期望俯仰角 atan2(期望X轴加速度, g)垂直方向直接用油门控制。位置环的P参数整定要特别注意位置环增益过大会导致期望倾斜角超限出现飞机像荡秋千一样来回晃。我在仿真里踩过这个坑最终在位置环输出后加了一个倾斜角限幅器把期望滚转和俯仰角限制在正负30度以内这既是出于安全考虑也让位置环和姿态环的衔接更稳定。4.4 参数整定的思路仿真里先调内环还是外环参数的整定顺序是有讲究的最忌讳的就是内环外环一起调出了问题根本不知道是哪里错。我的流程是先把外环冻结给定固定期望角度比如常值15度单独整定姿态环。从小到大加角速度内环的Kp观察响应曲线。出现等幅振荡时说明接近临界增益把Kp回退到临界值的一半左右再加一些Kd增加阻尼。姿态环稳定后再放开角度外环同样从小的Kp开始调。最后才接上位置环。仿真中一个常见的问题如果你跳过内环直接调位置环会发现位置响应看起来也能工作但一旦遇到扰动或者期望位置突变控制输出会非常激进甚至是振荡发散。原因就是位置环产生的期望姿态角变化太快姿态环根本没有能力跟踪。所以一层层从内到外整定不是流程形式而是让每个环节都在能力范围内工作的保证。5. 代码实现用Python从零写一套可复用的仿真5.1 整体架构设计状态量定义与数据流理论推导做完到写代码这一步我建议不要一上来就堆代码先把架构想清楚。四旋翼仿真本质是一个时间推进的循环已知当前状态量通过控制律算控制量再通过动力学方程算状态变化率积分后得到下一时刻状态。我把整体状态量定义为12维向量[位置(3), 速度(3), 四元数(4), 角速度(3)]为什么不直接用欧拉角当状态量前面说了欧拉角在大角度时会遇到万向节锁和数值发散问题。四元数虽然不如欧拉角直观但作为积分对象是数值稳定的。如果实在习惯用欧拉角可以把欧拉角保留为输出量只在内部积分时用四元数。整个仿真循环的核心逻辑可以写成这样class QuadrotorSim: def __init__(self, params): self.state np.zeros(13) # [px, py, pz, vx, vy, vz, qw, qx, qy, qz, wx, wy, wz] self.params params def step(self, control_input, dt): # 1. 解析控制输入 # 2. 计算电机转速动态 # 3. 计算总推力和力矩 # 4. 更新刚体动力学 # 5. 积分推进状态 pass控制循环和仿真循环分离是这个架构的关键。仿真循环只关心物理规律——你给它什么推力和力矩它就更新什么状态。控制循环只关心控制率——它读取当前状态量输出期望推力力矩。两者之间通过混控矩阵衔接互不干扰。这样后续替换控制器、换机架参数都非常方便。5.2 数值积分为什么我推荐用RK4而不是欧拉法状态方程写好了接下来是数值积分。这里有一个很多初学者容易忽视、但影响很大的选择积分算法。最基本的欧拉法是这样state_new state_old f(state_old) * dt它的问题是误差随着仿真时间累积较快。对于四旋翼这种快动态系统尤其当控制器引起高频振荡时欧拉法极易发散——状态量在一个周期内变化过大积分步长跟不上。我推荐使用四阶龙格-库塔法RK4。它的核心思想是在一个步长内取四个采样点对变化率做加权平均从而把单步误差降到O(dt^5)比欧拉的O(dt^2)高了好几个数量级。代价只是每个步长多算几次函数值对现代计算机来说完全不是负担。在同样的控制参数和初始条件下我用欧拉法算到第10秒时就出现明显偏移换RK4之后轨迹平滑稳定完全可接受。RK4的标准实现如下def rk4(f, state, t, dt): k1 f(state, t) k2 f(state 0.5 * dt * k1, t 0.5 * dt) k3 f(state 0.5 * dt * k2, t 0.5 * dt) k4 f(state dt * k3, t dt) return state (dt / 6.0) * (k1 2 * k2 2 * k3 k4)这里f就是动力学状态方程函数。在实现时state是一个numpy数组所有运算用向量化写法这样能保证仿真速度足够快。一个5分钟的飞行仿真用RK4加10kHz的积分步长在普通笔记本上跑下来也就是几秒钟的事。5.3 核心代码动力学更新与控制更新我把动力学更新的核心函数拆开来看。首先要从当前状态里解析出四元数和角速度然后计算出机体坐标系下的推力与力矩再换算成地轴系加速度最后转换到四元数导数。来看关键部分的伪代码实现def dynamics(self, state, motor_omega): params self.params q state[6:10] omega state[10:13] # 计算单个螺旋桨拉力 forces params.kt * motor_omega**2 total_thrust np.sum(forces) # 计算机体轴力矩 torque_x params.arm_l * (forces[0] - forces[1] - forces[2] forces[3]) torque_y params.arm_l * (forces[0] forces[1] - forces[2] - forces[3]) torque_z params.km / params.kt * (forces[0] - forces[1] forces[2] - forces[3]) # 机体加速度 accel_b np.array([0.0, 0.0, -total_thrust]) / params.mass # 地轴系加速度R是机体系到地轴系的旋转矩阵 accel_world rotate_by_quat(accel_b, q) np.array([0, 0, params.gravity]) # 角加速度 inertia params.inertia dot_omega np.array([ (torque_x (inertia[1] - inertia[2]) * omega[1] * omega[2]) / inertia[0], (torque_y (inertia[2] - inertia[0]) * omega[2] * omega[0]) / inertia[1], (torque_z (inertia[0] - inertia[1]) * omega[0] * omega[1]) / inertia[2] ]) # 四元数导数 omega_quat np.array([0, omega[0], omega[1], omega[2]]) dot_q 0.5 * quat_multiply(q, omega_quat) # 组装状态导数 return np.concatenate([state[3:6], accel_world, dot_q, dot_omega])注意这里的rotate_by_quat是把机体系矢量转到地轴系的四元数旋转函数实现时直接用四元数的共轭变换公式。力矩分配那四行公式虽然看起来是简化的但和前面混控矩阵推导的结论完全一致。控制器这边的代码逻辑稍微复杂因为涉及串级结构。可以这样组织def controller(self, state, setpoint, dt): # 位置环计算期望姿态 pos_err setpoint.pos - state[0:3] vel_err setpoint.vel - state[3:6] desired_acc self.kp_pos * pos_err self.kv_pos * vel_err # 期望滚转角、俯仰角 roll_cmd np.arctan2(-desired_acc[1], self.params.gravity) pitch_cmd np.arctan2(desired_acc[0], self.params.gravity) # 姿态环角度误差 - 期望角速度 euler quat_to_euler(state[6:10]) roll_rate_cmd self.kp_att * (roll_cmd - euler[0]) pitch_rate_cmd self.kp_att * (pitch_cmd - euler[1]) yaw_rate_cmd self.kp_yaw * (setpoint.yaw - euler[2]) # 角速度环角速度误差 - 期望力矩 rate_err np.array([roll_rate_cmd, pitch_rate_cmd, yaw_rate_cmd]) - state[10:13] torque_cmd self.kp_rate * rate_err - self.kd_rate * state[10:13] # 油门分配 thrust_cmd self.kp_thr * (setpoint.z - state[2]) self.kv_thr * (setpoint.vz - state[5]) return thrust_cmd, torque_cmd严格来说姿态角误差处理应该区分小角度和大角度情况。在小角度近似下直接用欧拉角差当误差没问题但大角度时应改用旋转向量误差。我在代码里做了个判断角度误差模大于30度时用四元数旋转向量来计算误差保证控制方向正确。5.4 可视化与结果分析让你的仿真看得见跑完仿真光有数据不够一定要可视化。我推荐的组合是matplotlib画二维曲线另外可以配合简单3D动画直观地看飞行轨迹和姿态。二维曲线至少要画这些状态量随时间变化曲线位置、速度、欧拉角、角速度每个一张图电机转速看四个电机是否饱和、是否振荡控制器输出看力矩指令是否在合理范围有没有频繁限幅这些图能帮你定位的问题非常具体。比如姿态角收敛慢看角度曲线就知道超调量和调节时间电机转速出现锯齿形波动说明控制输出在限幅边缘反复切换轨迹出现逐渐发散的振荡多半是位置环增益过大。三维轨迹图我用mpl_toolkits的3D坐标轴画把机体坐标系画成三个箭头这样可以看到飞机姿态在空间中的指向。加上旋转动画后调试体验提升不是一点半点——你甚至能直观地看出飞机为什么翻了个个儿。6. 仿真调试中的高频坑与我的排查心得6.1 数值发散先查积分步长再查模型参数仿真中遇到最频繁的问题就是数值发散——状态量在几步之内跳到NaN或者无限大。很多人第一反应是控制器写错了但我建议先按这个顺序排查第一步检查积分步长。如果步长太大RK4也可能扛不住。四旋翼的角速度环带宽通常到几十赫兹控制周期我一般设为1毫秒到2毫秒而动力学积分步长要再细分到0.1毫秒到0.5毫秒。步长和动力学本身的快vs慢也有关模型里电机时间常数越大越需要小步长去捕获动态。第二步检查模型参数有没有物理意义。比如转动惯量设成非常小的值角加速度就会异常大控制稍微有点误差就会发散。我见过有人把惯量设成零点零零几然后用一组正常的PID参数去飞结果必然是数值爆炸。合理的惯量量级应该和飞行器的质量和尺寸匹配比如1到2公斤的四旋翼惯量通常在0.002到0.02之间。第三步检查执行器限幅。如果控制器输出的力矩远大于混控矩阵能表达的物理极限转速指令会超出实际范围导致整个模型失真。限幅处理一定要放在电机转速输出之前而不是让仿真在一个不可能的物理状态下硬算。6.2 振荡问题Kd加得太大也会引起高频抖动整定姿态环时最常见的是振荡但有意思的是振荡也有区分。低频大幅度的来回晃一般是比例增益太大高频小幅度抖动则常常是微分项引起的。我在仿真里遇到过一次高频抖动姿态角曲线看起来能稳定但电机转速曲线剧烈震荡频率很高。查了一圈发现是我给的Kd_rate太大微分项对陀螺仪角速度的一阶微分非常敏感仿真步长下产生了放大效应。现实中的陀螺仪信号不是理想平滑的Kd过大会把传感器噪声放大到失控这在仿真里也一样成立。解决方法是限制微分项的带宽。一个简单的做法是给微分项加一个低通滤波器比如一阶低通filtered_d alpha * raw_d (1 - alpha) * filtered_dalpha取0.2到0.5根据控制频率调整。加了滤波之后高频抖动基本消失而且相位延迟增加很小不影响控制效果。6.3 电机模型延迟对控制带宽的限制一个实际案例分析最后分享一个让我印象很深的仿真案例。起先我为了调试方便把电机响应设成理想的瞬时响应姿态环参数整定得很漂亮各项指标都很好。然后我换成了一组更真实的电机参数带上30毫秒的一阶惯性延迟结果姿态环开始高频振荡。问题本质是电机的响应延迟给整个控制回路增加了一个相位滞后相当于给控制器一个额外的等待时间。为了让系统稳定只能降低控制增益——这直接体现了电机性能对飞控系统带宽的约束。我把角速度环Kp降到原来的一半左右同时给期望力矩输出增加了一点阻尼项系统才重新稳定下来但响应速度也明显变慢了。这给我一个重要的启发仿真模型的保真度直接决定了调试结果是否可信。如果你的仿真把所有的执行器动态都当成理想环节那整定出来的控制器在真机上多半偏激进到了真机还得从头调。所以我的建议是从第一天就在仿真里加入电机延迟模型即使不知道精确参数用一个合理估计值也比理想化要好得多。四旋翼仿真做到这里其实就是把物理、控制和数值方法三块系统地串了一遍。从坐标系的定义到刚体运动方程再到电机模型和串级控制器最后用RK4把整个系统推进起来。这个过程没有捷径但每一步走扎实之后回头再看任何现成的飞控代码和仿真框架你都能一眼看出它的设计逻辑和潜在问题。我个人的体会是仿真调通的成就感比不上终于看懂了整个系统那种通通透透的踏实感。如果你也在做四旋翼相关的开发建议务必亲手从公式推到代码走一遍收获绝对超出你的预期。
阅读完成 · 觉得有帮助?
咨询建站