1. 四旋翼与滑模控制:为什么是“黄金搭档”?

如果你玩过或者了解过四旋翼无人机,比如大疆的那些航拍机,你肯定知道它们飞起来有多稳。但你可能不知道,这背后是控制算法在默默“打工”。今天我想跟你聊的,就是一种在工程上特别“抗造”、特别“硬核”的控制方法——滑模控制。我第一次把它用在四旋翼上时,感觉就像给无人机装上了一套“自动驾驶防抖系统”,哪怕有风或者模型参数不准,它也能死死“咬住”预设的轨迹,这种鲁棒性让我印象非常深刻。

那么,滑模控制到底是什么?你可以把它想象成开车。传统的控制方法,比如PID,就像是一个温和的司机,发现车子偏左了,就轻轻向右打一点方向,慢慢修正。而滑模控制呢,更像是一个“强迫症”司机,他脑子里有一条绝对正确的“理想路径”(我们称之为滑模面)。一旦车子稍微偏离这条路径,他不会温柔地修正,而是会立刻、果断、甚至有点“粗暴”地打满方向盘,把车强行“拉”回正轨。这种“非此即彼”的强控制策略,使得系统对外部干扰和内部参数变化非常不敏感,这正是四旋翼这种容易受风扰、模型还不完全准确的系统所急需的。

为什么说它和四旋翼是“黄金搭档”?因为四旋翼的动力学模型是非线性、强耦合的。简单说,你动一个控制量(比如增加某个电机的转速),它影响的不仅仅是高度,还会连带影响俯仰、横滚和偏航。这种复杂的特性让很多控制方法头疼。但滑模控制恰恰擅长处理这种非线性和不确定性,它不依赖于极度精确的模型,只要知道模型的大致结构和边界,就能设计出有效的控制器。这对于我们这些做工程的人来说,简直是福音,因为我们永远无法获得一个百分百准确的真实系统模型。接下来,我就带你从最基础的数学建模开始,一步步把这套强大的控制方法实现出来,并用Simulink仿真和C代码让你亲眼看到它的效果。

2. 第一步:给四旋翼“写简历”——数学建模

做控制就像给人看病,得先了解病人的身体结构。给四旋翼设计控制器,第一步就是为它建立一个数学模型,也就是用数学方程来描述它的“一举一动”。这个过程虽然涉及公式,但我会尽量用大白话解释清楚。

首先,我们需要两套坐标系来“观察”无人机。一套是地球坐标系,固定在地面上,好比我们站在地面看飞机。在这个坐标系里,我们用 [x, y, z] 表示无人机的位置,用 [φ, θ, ψ](分别是滚转角、俯仰角、偏航角)描述它的姿态。另一套是机体坐标系,牢牢“粘”在无人机身上,跟着它一起动。在这个坐标系里,我们用 [u, v, w] 表示它前进、侧飞、上下的速度,用 [p, q, r] 表示它绕自身三个轴旋转的角速度。搞清楚这两套坐标系的转换(通过旋转矩阵),是建模的基石。

基于牛顿-欧拉方程,我们可以写出无人机运动和旋转的核心方程。简单来说, 产生 加速度力矩 产生 角加速度。对于四旋翼,它受到的力主要来自四个螺旋桨产生的总升力、重力,还有我们今天要处理的一个特殊场景——跨介质(比如从空气进入水中)时受到的浮力和阻力变化。力矩则是由四个电机转速差异产生的。

为了让模型更贴近一个真实的复杂场景,我们参考了一篇关于跨介质无人机的论文。它设定了一个高度区间 H。当无人机在空中时,只受重力和空气阻力;当它开始入水,在 H 区间内时,受到的浮力、阻力和其自身的质量、转动惯量都会随入水深度连续变化;完全入水后,这些参数又变为新的常值。这比简单的空中模型复杂得多,但也更能考验控制器的能力。

综合这些因素,我们可以得到一组最终的运动方程。对于高度通道(z方向),方程包含了总升力、重力、变化的浮力 f(z)、变化的阻力项 C0(z)z˙,以及各种未建模的干扰 d_f, d_k, d_l。对于姿态通道,方程则包含了控制力矩、科里奥利力(由于旋转产生的效应)和外部干扰力矩 d_Γ。这些方程看起来复杂,但本质上就是“力/力矩 = 质量/转动惯量 × 加速度/角加速度”的具体展开。我把它们写下来,这样我们后面设计控制器时就有了明确的“靶子”。

% 这是一个示意性的模型核心方程描述
% 高度通道动力学
m3 * z_ddot = U1 * cos(phi) * cos(theta) - m0*g + f(z) - C0(z)*z_dot + total_disturbance_z;

% 姿态通道动力学
J * eta2_ddot = Gamma - C(eta2_dot) + disturbance_attitude;

其中,U1 是四个螺旋桨产生的总升力,Gamma = [U2, U3, U4]^T 是分别控制滚转、俯仰、偏航的三个力矩。m3J 是随高度变化的等效质量和转动惯量。我们的目标,就是设计 U1Gamma 这三个控制量,让 z 能跟踪我们期望的高度 z_d,让 [φ, θ, ψ] 能跟踪我们期望的姿态 [φ_d, θ_d, ψ_d]

3. 设计控制器的“大脑”:滑模与自适应律

有了数学模型这个“身体”,现在我们来设计控制器的“大脑”。滑模控制的核心思想,就是设计一个“滑模面”,一旦系统的状态轨迹“滑”到这个面上,就会像上了轨道一样,自动滑向平衡点,并且对外部干扰具有极强的免疫力。

3.1 姿态控制器设计:让无人机“听话”地转身

首先对付姿态控制。我们定义姿态跟踪误差 e = η2 - η2_d,也就是当前姿态减去期望姿态。滑模面 s 我们设计为误差和误差导数的线性组合:s1 = e_dot + λ1 * e。你可以把 λ1 理解为一个调节“松紧”的参数:λ1 越大,系统对误差越敏感,收敛到滑模面越快。

我们的目标是让 s1 趋于0。当 s1=0 时,根据定义 e_dot = -λ1 * e,这是一个指数收敛的微分方程,意味着姿态误差 e 会以指数速度衰减到0,完美!那么如何让 s1 趋于0呢?我们需要设计控制力矩 Gamma

通过对 s1 求导,并代入姿态动力学方程,我们会得到 s1_dot 的表达式,其中包含了控制量 Gamma。我们希望 s1_dot 是一个负定的形式(比如 -c1 * s1),这样就能保证 s1 会收敛到0。通过反解这个关系,我们就可以得到 Gamma 的表达式。这个表达式里,包含了期望姿态的加速度 η2_d_ddot、误差 ee_dot,以及滑模面 s1 本身。

但是,这里有个问题:我们的模型参数(转动惯量 J)和外部干扰 d_Γ 是未知或不确定的。如果直接用名义值,控制效果会大打折扣。怎么办?这里就用上了自适应 的思想。我们不是不知道准确的 Jd_Γ 吗?那我们就在控制器里实时地“估计”它们!我们引入估计值 J_hatdΓ_hat,并设计一套自适应律,告诉这些估计值应该如何根据当前的控制效果(具体来说,就是根据滑模面 s1)进行更新。

% 姿态控制器核心公式示意
% 滑模面
s1 = e_dot + lambda1 * e;

% 控制量计算(包含自适应估计值)
Gamma_hat = eta2_d_ddot - lambda1 * e_dot - c1 * s1;
Gamma = J_hat * Gamma_hat + C * eta2_dot - dGamma_hat;

% 自适应律(估计值的更新规则)
dGamma_hat_dot = gamma1 * s1;
J_hat_dot = -gamma2 * Gamma_hat * s1';

这套自适应律的设计非常巧妙,它是通过李雅普诺夫稳定性理论 严格推导出来的。我们构造一个包含 s1、参数估计误差的能量函数 V1,然后设计自适应律,使得这个能量函数的导数 V1_dot 是负半定的(V1_dot = -c1 * s1^T * s1 ≤ 0)。这就从数学上证明了,整个闭环系统是稳定的,跟踪误差最终会收敛,而且参数估计误差也是有界的。这就是自适应滑模控制的强大之处:边控制,边学习,边调整。

3.2 高度控制器设计:稳如泰山的悬停

姿态控制是内环,保证了无人机自身的稳定。高度控制则是外环,它给内环姿态控制器提供姿态指令(比如,想上升就需要先微微前倾),并最终控制高度。

高度控制器的设计思路和姿态控制器类似。定义高度误差 e_z = z - z_d,设计滑模面 s2 = e_z_dot + λ2 * e_z。同样,对 s2 求导,代入高度通道动力学方程。

这里的关键点在于,高度通道的控制量是总升力 U1,而 U1 的表达式中包含 cos(φ)cos(θ) 项。这意味着高度控制和姿态控制是耦合的:要想精准控制高度,必须知道当前的姿态角。在实际系统中,我们通常采用级联控制 结构:外环高度控制器计算出需要的总升力 U1,同时也会解算出期望的俯仰和滚转角度(θ_d, φ_d),传递给内环的姿态控制器去跟踪。这样,内外环协同工作,才能实现精准的定点悬停或轨迹跟踪。

同样地,高度通道也存在质量 m3 变化和未知干扰 ρ 的问题。我们再次引入自适应估计 m3_hatrho_hat,并设计相应的自适应律。控制量 U1 的最终表达式会包含一个符号函数 sgn(s2),这是滑模控制的典型特征,它提供了强制的“切换”控制力,确保系统状态不偏离滑模面。

% 高度控制器核心公式示意
% 滑模面
s2 = e_z_dot + lambda2 * e_z;

% 中间计算量 L,包含鲁棒项
L = -tau * s2 - k * sign(s2) + z_d_ddot - lambda2 * e_z_dot;

% 控制量 U1 (注意除以 cos(phi)cos(theta),需要姿态信息)
U1 = ( m3_hat * L + m0*g - f(z) + C0(z)*z_dot - rho_hat ) / (cos(phi)*cos(theta));

% 自适应律
m3_hat_dot = -k1 * L * s2;
rho_hat_dot = k2 * s2;

通过构造另一个李雅普诺夫函数 V2,我们可以证明高度闭环系统也是稳定的。至此,控制器的“大脑”就设计完成了。它融合了滑模控制的强鲁棒性和自适应控制的在线学习能力,理论上可以应对四旋翼在复杂环境(如跨介质)下的控制挑战。

4. 在Simulink中“搭积木”:从框图到动态仿真

理论设计得再漂亮,不上仿真验证心里总没底。Simulink就是一个图形化的“实验室”,让我们能用搭积木的方式把数学模型和控制算法连接起来,看到动态的运行效果。下面我带你一步步搭建这个自适应滑模控制器的仿真模型。

首先,我们需要建立被控对象,也就是四旋翼的动力学模型模块。根据第2部分的方程,在Simulink里用基本的加减乘除、三角函数、积分模块把它搭建出来。输入是三个控制力矩 [U2, U3, U4] 和总升力 U1,以及当前姿态角(用于计算 cos(φ)cos(θ)),输出是位置、姿态、速度、角速度等所有状态量。这个模块我通常命名为 Plant_ModelQuadrotor_Dynamics

接下来是核心——控制器模块。我们需要建立两个子模块:Attitude_ControllerAltitude_Controller。每个模块内部,都严格按照第3部分推导的公式来搭建。

  • 在姿态控制器模块里,你需要实现滑模面 s1 的计算、控制量 Gamma 的计算,以及自适应律 dGamma_hat_dotJ_hat_dot 的积分更新。记得用 Integrator 模块来对 dGamma_hat_dotJ_hat_dot 进行积分,得到 dGamma_hatJ_hat
  • 在高度控制器模块里,同样实现 s2LU1 的计算,以及对 m3_hatrho_hat 的自适应积分更新。

然后,将整个系统连接起来。这是一个典型的级联控制结构:

  1. 外环:给定期望高度 z_d,与当前高度 z 比较得到误差,送入 Altitude_Controller,计算出 U1 和期望姿态角 φ_d, θ_d(偏航角 ψ_d 通常单独给定)。
  2. 内环:期望姿态 [φ_d, θ_d, ψ_d] 与当前姿态比较,误差送入 Attitude_Controller,计算出三个控制力矩 [U2, U3, U4]
  3. 被控对象:将 U1[U2, U3, U4] 输入给四旋翼模型 Plant_Model,模型输出新的状态量,反馈回控制器,形成闭环。

在搭建时,有几点需要特别注意:

  • 初始值:所有积分器模块(比如用于计算速度、角速度、自适应参数的)都要设置合理的初始值,例如姿态角初始为0,自适应参数初始为名义值。
  • 符号函数处理sign(s2) 函数在过零点会产生高频抖振,在实际仿真和工程中,通常用饱和函数 sat(s/Φ)连续近似函数(如 s/(|s|+δ) 来代替,其中 Φδ 是很小的正数。这能在保持性能的同时,显著减轻抖振。
  • 采样时间:如果你准备最终生成代码跑在硬件上,仿真时最好就设置一个固定的离散采样步长,比如0.001秒或0.01秒,这样更贴近数字控制器的实际情况。

5. 让算法“跑起来”:从仿真到C代码实现

仿真成功,只完成了工作的一半。最终我们的算法是要在真实的飞控硬件(比如STM32, Pixhawk)上运行的。这就需要我们把Simulink模型转换成C代码。MATLAB/Simulink提供了强大的代码自动生成工具——Embedded Coder,可以帮我们完成这个转换。

第一步是模型配置。在Simulink的“模型设置”里,选择求解器为固定步长离散求解器,设置好步长。在“代码生成”选项中,选择目标为“嵌入式实时系统”,工具链选择你使用的编译器(比如GCC for ARM)。这一步的目的是告诉Simulink,我们要生成的是嵌入式C代码,而不是普通的桌面仿真代码。

第二步是模块与接口配置。我们需要明确哪些模块是需要生成代码的算法部分(如两个控制器),哪些是仿真用的(如四旋翼模型、信号源、示波器)。对于算法模块,要确保其内部使用的函数和运算都是代码生成支持的。对于模型的输入输出,我们需要创建 Simulink.SignalSimulink.Parameter 对象来定义接口变量,这样生成的代码会有清晰的结构体来对应这些输入输出。

第三步是关键的一步:将连续时间模型离散化。我们设计的控制器是连续时间的,但数字控制器是离散执行的。我们需要使用离散积分器(如离散时间积分模块)替换模型中的连续积分器,或者使用 c2d 命令对连续控制器进行离散化。控制器的差分方程形式要写清楚。例如,自适应律的积分 dGamma_hat = ∫ (gamma1 * s1) dt 在离散时间下就变成了 dGamma_hat[k] = dGamma_hat[k-1] + gamma1 * s1[k] * Ts,其中 Ts 是采样时间。

第四步,执行代码生成。点击“Build”按钮,Embedded Coder会自动将模型编译生成C代码、头文件以及编译报告。生成的代码通常包含几个主要部分:一个初始化函数 controller_initialize(),一个单步执行函数 controller_step(),以及一个终止函数 controller_terminate()。我们最关心的是 controller_step(),它在一个采样周期内被调用一次,读取当前状态(姿态、高度等)和期望值,计算并输出控制量。

// 这是一个高度简化的代码结构示意
void altitude_controller_step(float z, float z_d, float phi, float theta, float* U1, float* phi_d, float* theta_d) {
    // 1. 计算高度误差和滑模面
    float e_z = z - z_d;
    static float e_z_prev = 0.0f;
    float e_z_dot = (e_z - e_z_prev) / Ts; // 简单差分求导,实际中可能用观测器
    e_z_prev = e_z;
    float s2 = e_z_dot + lambda2 * e_z;

    // 2. 计算中间量L (使用连续近似函数代替sign)
    float sat_s2 = s2 / (fabs(s2) + delta);
    float L = -tau * s2 - k * sat_s2 + z_d_ddot - lambda2 * e_z_dot;

    // 3. 更新自适应参数 (前向欧拉法)
    m3_hat += (-k1 * L * s2) * Ts;
    rho_hat += (k2 * s2) * Ts;

    // 4. 计算总升力U1
    float denominator = cosf(phi) * cosf(theta);
    denominator = (fabs(denominator) < 1e-6) ? 1e-6 : denominator; // 防止除零
    *U1 = ( m3_hat * L + m0*GRAVITY - buoyancy_f(z) + C0(z)*z_dot - rho_hat ) / denominator;

    // 5. (可选)根据U1解算期望姿态角,这里简化处理
    // *phi_d = ...;
    // *theta_d = ...;
}

最后一步是集成与测试。将生成的C文件、头文件加入到你的飞控工程中。在飞控的主循环里,以固定的频率(如500Hz)调用 controller_step() 函数。将传感器读取的真实状态(通过滤波和姿态解算得到)作为输入,计算出的控制量 U1[U2, U3, U4] 再转换为电机的PWM信号输出。在真机测试前,务必进行硬件在环仿真,用真实的飞控硬件运行生成的代码,连接Simulink中的虚拟无人机模型进行测试,这是保证安全、降低炸机风险的关键一步。

6. 仿真结果分析与工程实践中的“坑”

当我们运行搭建好的Simulink仿真,结果通常会非常直观地展示滑模控制的特性。在姿态和高度跟踪曲线上,你会看到系统状态能快速、准确地跟踪上期望的指令,即使我们在模型中加入了额外的干扰力矩或质量突变。自适应参数 J_hatm3_hat 等会不断调整,虽然不一定收敛到真实值(理论证明是有界估计),但足以保证控制性能的稳定。

然而,一个无法回避的现象是抖振。在控制量 U1Gamma 的曲线图上,当系统状态接近平衡点时,你会看到高频、小幅度的振荡。这正是滑模控制中符号函数 sgn(s) 带来的固有特性。在理想的数学世界里,切换是瞬间的;但在现实中,任何系统都有惯性、延迟和执行机构的带宽限制,无法实现无限快的切换,这就导致了实际控制量在理想值附近来回抖振。

抖振在工程上是“大敌”,它会白白消耗能量,加剧执行机构(电机、舵机)的磨损,在高频下还可能激发系统未建模的谐振模态,导致不稳定。因此,抑制抖振是滑模控制工程化的核心。除了前面提到的用饱和函数代替符号函数,还有几种常用方法:

  1. 边界层法:在滑模面 s=0 附近设置一个薄层 |s| < Φ。在层内,采用连续的线性反馈(如PD控制);在层外,仍采用滑模控制。这相当于把“硬切换”软化成了“软过渡”。
  2. 高阶滑模:比如超螺旋算法,它通过引入积分作用,使得控制量的导数不连续,而控制量本身是连续的,从而从理论上消除了抖振,但对参数设计更敏感。
  3. 观测器补偿:设计扰动观测器,将估计出的扰动前馈补偿到控制律中,从而降低滑模切换项的增益 k,减轻抖振。

在实际飞控项目中,我通常会采用“饱和函数+边界层”的组合,并精心调节边界层厚度 Φ 和切换增益 kΦ 越大,抖振越小,但跟踪精度会下降;k 需要足够大以克服干扰,但过大会加剧抖振。这需要一个折中和反复调试的过程。另外,自适应律中的学习率 γ1, γ2, k1, k2 也需要仔细调节。过大的学习率会导致估计值振荡,过小则自适应速度太慢,影响鲁棒性。

最后,我想说的是,从数学推导到Simulink仿真,再到C代码飞控实现,这个过程是控制系统工程师的典型工作流。滑模控制为我们提供了一种强大的工具,但它不是“银弹”。理解其原理,正视其缺点(抖振),并通过工程技巧(如边界层、观测器)加以改进,才能真正让它在实际系统中发挥出强悍的性能。当你第一次看到自己编写的滑模控制器让无人机在风中稳稳悬停时,那种成就感,绝对是理论学习无法比拟的。希望这篇文章的梳理,能为你实践滑模控制提供一个清晰的路线图。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐