跳转至

大作业

📖 阅读信息

阅读时间:5 分钟 | 中文字符:2081 | 有效代码行数:67

数学建模

控制模型的建立

这里的控制模型分为内环(航向角)和外环(横向偏差)两个部分

外环


外环使用的偏差是横向偏差

\[ e_y=y_{ref}-y \]
  • \(y_{ref}\) : 参考轨迹的横向位置
  • \(y{: }\) 车辆当前横向位置
  • \(e_y:\) 横向偏差

就是现在的车的位置离目标位置多远


这一部分的偏差使用 PID 控制,得到了第一个航向修正量 \(\Delta \theta\)

\[ \Delta\theta=k_{py}e_y+k_{iy}\int e_ydt+k_{dy}\frac{de_y}{dt} \]
含义

根据横向偏差,生成一个应该额外补偿的航向角修正量 \(\Delta \theta\)

内环


航向角的偏差:

\[ e_\theta=\theta_{ref}-\theta \]
  • \(θ_{ref}\) : 参考航向角
  • \(θ\) : 车辆当前实际航向角
  • \(e_θ\) : 航向角误差

总的航向角修正

就是结合上面的两个偏差:

\[ e_\theta^*=e_\theta+\Delta\theta \]

前轮转角部分

前轮转角由前馈和后馈两个部分组成:\(\delta=\delta_{ff}+\delta_{fb}\)

前馈


就是由参考的轨迹曲率提前给的应该有的转角:

\[ \delta_{ff}\approx L\cdot\kappa \]

就是轴距乘以参考的轨迹曲率,在汽车理论中
这里的前轮转角也是近似的,在二自由度稳态中的公式为:\(\delta_{ff}=\frac{L}{R}+LKa_y\)

反馈


使用的是上述的 总的航向角修正

\[ \delta_{fb}=k_{p\theta}e_\theta^*+k_{d\theta}\frac{de_\theta^*}{dt}-k_rw_r \]

一个 PD + 横摆角速度反馈

  • 第一项:航向角误差比例控制
  • 第二项:“D 微分抑制”,意思是抑制快速变化,减少振荡
  • 如果车辆当前横摆角速度太大,就反向抑制一下,避免转向过猛、摆振太强。

以上就是控制模型的建立

被控对象的建立

这一部分可以使用 carsim 代替
实际上就是几个物理量之间的关系:

\[ \delta\to w_r\to\theta\to y \]

对应的传递函数关系就是图中的:

\[ \begin{gathered}\frac{w_r(s)}{\delta(s)}=\frac{K_\delta}{Ts+1}\\\frac{\theta(s)}{\delta(s)}=\frac{K_{\delta}}{s(Ts+1)}\\\frac{y(s)}{\delta(s)}=\frac{VK_\delta}{s^2(Ts+1)}\end{gathered} \]

但是上述的公式是比较近似的,实际上的横摆加速度与前轮转角的公式为:

  • 二自由度稳态:
\[ \frac{\omega_r}{\delta})_s=\frac{u/L}{1+Ku^2} \]
  • 二自由度非稳态:
\[ \ddot{\omega}_\mathrm{r}+2\omega_0\zeta\dot{\omega}_\mathrm{r}+\omega_0^2\omega_\mathrm{r}=B_1\dot{\delta}+B_0\delta \]
\[ \frac{\omega_r(s)}{\delta(s)}=\frac{B_1s+B_0}{s^2+2\omega_0\zeta s+\omega_0^2} \]

总的来说:

\[ \begin{gathered}e_{y}=y_{ref}-y\\\Delta\theta=k_{py}e_{y}+k_{iy}\int e_{y}dt+k_{dy}\dot{e}_{y}\\e_\theta^*=\theta_{ref}-\theta+\Delta\theta\\\delta_{fb}=k_{p\theta}e_\theta^*+k_{d\theta}\dot{e}_\theta^*-k_rr\\\delta_{ff}=atan(L\kappa)\\\delta=\delta_{ff}+\delta_{fb}\\\frac{r(s)}{\delta(s)}=\frac{K_{\delta}}{Ts+1}\\\theta(s)=\frac{1}{s}r(s)\\y(s)=\frac{V}{s}\theta(s)\end{gathered} \]

参数的修改

首先是外部的 PID 参数


此时的图像的性质:

优化之后的参数


函数:

主函数:提取 pid 中的参数,调用方法优化参数

Matlab
clc; clear; close all;

mdl = 'VehicleControl';
load_system(mdl);

% 强制打开输出(但是我现在是有输出的)
set_param(mdl, 'SaveOutput', 'on');
set_param(mdl, 'SaveTime', 'on');

% 查找PID
pidBlocks = find_system(mdl,...
    'LookUnderMasks','all',...
    'FollowLinks','on',...
    'Regexp','on',...
    'Name','^PID $');

if isempty(pidBlocks)
    error('找不到PID');
end
pidBlock = pidBlocks{1};
disp(['当前调参PID: ', pidBlock]);

% 优化参数(故意设得差一点)
x0 = [1.5, 0.3, 0.1];
lb = [0.1, 0.001, 0];
ub = [10, 2, 0.5];

% 用 patternsearch 代替 fmincon(更适合 PID!)
opts = optimoptions('patternsearch', ...
    'Display', 'iter', ...
    'MaxIterations', 200);

% 开始优化
[x_opt, J_opt] = patternsearch(@(x) pid_cost_func(x,mdl,pidBlock),...
    x0, [], [], [], [], lb, ub, [], opts);

Kp = x_opt(1);
Ki = x_opt(2);
Kd = x_opt(3);

fprintf('\n======== 最优 PID 参数 ========\n');
fprintf('Kp = %.6f\n', Kp);
fprintf('Ki = %.6f\n', Ki);
fprintf('Kd = %.6f\n', Kd);
fprintf('最优代价 J = %.6f\n', J_opt);

% 写入模型
set_param(pidBlock, 'P', num2str(Kp));
set_param(pidBlock, 'I', num2str(Ki));
set_param(pidBlock, 'D', num2str(Kd));

% 最终仿真
simOut = sim(mdl, 'StopTime', '20');
disp('✅ 自动调参完成!');
patternsearch(模式搜索算法)

从初始点开始,从上下左右试探参数
找到更好的就过去
步子从大到小,越调越精细

损失函数

Matlab
function J = pid_cost_func(x, mdl, pidBlock)
    % 永远返回标量!永远不报错!
    J = 1e10;  % 初始就是标量!

    try
        Kp = x(1);
        Ki = x(2);
        Kd = x(3);

        % 写参数,就是将探索的参数写入PID控制器中
        set_param(pidBlock, 'P', num2str(Kp));
        set_param(pidBlock, 'I', num2str(Ki));
        set_param(pidBlock, 'D', num2str(Kd));

        % 仿真
        simOut = sim(mdl);

        % 100% 能读到的写法
        y = simOut.y_OUT.Data;
        t = simOut.y_OUT.Time;

        % 计算代价(标量!)
        e = 1 - y;
        ISE = sum(e.^2);
        J = ISE;  % 一定是数字!

    catch
        J = 1e10;
    end
end

联合仿真

输入输出部分

  1. 首先我们要新建我们自己的 simulink 的 dataset
    1. 在内部,我们要将这个 dataset 链接到我们的本地的文件中:
    2. 在左侧,我们要打开一个选项:
      ✅ 勾选 Set time step here 复选框 3,(设置方法和时间步长)
      ✅ 设置积分方法 4(如 AB-2、Euler 等),
      ✅ 并明确指定时间步长或频率 6。
      此外还有一个与数值积分方法相关的选项:对于每步仅计算一次的显式方法(如 AB-2 或 Euler),可以选择调整 CarSim 内部变量的计算时机,使其与 Simulink 的计算节奏对齐 5。(就是同步的作用)
      • 7:定义哪些变量将从 Simulink 导入到 CarSim(即 Simulink → CarSim);
      • 8:定义哪些变量将从 CarSim 导出到 Simulink(即 CarSim → Simulink)。
      • Run parallel VS Math Models
        中文:并行运行 VehicleSim 数学模型。
        作用:允许多个模型在多核 CPU 上同时计算,用于加速复杂的联合仿真或参数扫描。
    3. 我们现在定义一下自己的输入和输出
      1. 输入是前轮的转向角:
        选择 import channels,然后进入数据集中进行设置(这个数据集就是要选择 simulink 输入到 carsim 中的数据是个什么数据)

        如上图所示,这个数据集中的子数据集就是主页的车辆模型的数据集(也就是控制这个车辆的什么部分)
        我们现在需要输入的就是 IMP_STEER_SW(deg)注意单位
        我们使用 replace 的模式,也就是直接使用 simulink 控制转角,而不是在驾驶员的基础上叠加
      2. 我们的输出是 export channels
        也是先新建一个数据集,选择这个新建的数据集
        同样,输出的数据集中的子数据集也是主页数据集
        我们输出选择的参数为:Lat_Veh ——车辆到参考路径的横向距离,单位是米(m)(这个直接就是我们的误差量了)(双击可以添加或者删除)
        还有 Rho_Road(驾驶员参考路径曲率)
        Vx:纵向形式速度
        Ay:横向加速度
        Beta:侧偏角(车辆的)
        X、Y_Design:参考路径的 x、y 坐标(可以计算航向角)
    4. 之后我们进入 procedure 中进行车辆控制,还有道路的设置
      1. 我们可以修改运行的车速
      2. 还有下面的 3D 道路:修改道路的形状、道路的附着系数
参数导出

找不到自己想要的参数?
点击右边的 view spreadsheet 键即可

一些需要调整的点

  • 横摆角速度反馈的系数增大一点,增加阻尼项
  • 横向的误差比例增益减小(可以将其余与速度关联起来:\(k_{pg}(V)=\frac{k_0}{1+V}\)
  • 加一个前瞻的距离:\(e_y+L_d\cdot e_\theta\)(就是将后面的 \(e_{\theta}\) 的部分提前了 )
    • 这里的 \(L_d\) 可以取一个与速度相关的项(实际上和前馈部分的曲率比较像,所以曲率部分的系数也可以与速度相关)

模糊 PID 控制

几种常见的隶属函数

高斯和三角形最常用

模糊语句

if A and B then C
上面就是模糊语句的形式

就是精确的离散值,经过隶属函数之后的模糊集合

我们现在介绍的是模糊 PID,所以有着二输入(误差、误差变化率),三输出(pid 参数)

模糊控制器的组成

对于一个模糊输入变量 e,其模糊自己通常可以按照下面的三种方式划分:
序号 模糊输入变量 \(e\) 的划分
1 \(e = \{ \text{负大}, \text{负小}, \text{零}, \text{正小}, \text{正大} \} = \{ NB, NS, ZO, PS, PB \}\)
2 \(e = \{ \text{负大}, \text{负中}, \text{负小}, \text{零}, \text{正小}, \text{正大} \} = \{ NB, NM, NS, ZO, PS, PM, PB \}\)
3 \(e = \{ \text{负大}, \text{负中}, \text{负小}, \text{零负}, \text{零正}, \text{正小}, \text{正中}, \text{正大} \} = \{ NB, NM, NS, NZ, PZ, PS, PM, PB \}\)

通过 E,EC 的输入,将其模糊、语言化

数据库和规则库

  • 数据库:隶属函数(论域为连续)
  • 规则库:
    模糊控制系统输出变量为 e 和 ec,对应的语言变量为 A',B'
    就可以得出一组模糊规则

模糊控制器的设计基本步骤

以下是将图片中内容转换为 Markdown 格式的结果,保留了原有的结构和信息:

  1. 定义输入、输出模糊集
    对误差 \(E\)、误差变化 \(EC\) 及控制量 \(u\) 的模糊集及其论域定义如下:
    • \(E\)\(EC\)\(U\) (pid)的模糊集均为:{NB, NM, NS, ZO, PS, PM, PB}
    • \(E\)\(EC\) 的论域均为:{-3, -2, -1, 0, 1, 2, 3}
    • \(U\) 的论域为:{-4.5, -3, -1.5, 0, 1, 3, 4.5}
  2. 定义输入输出隶属函数
  3. 建立模糊控制规则
  4. 建立模糊控制表
  5. 模糊推理
  6. 反模糊化(常用重心法)

实操

使用 from、goto 的模块进行输入输出的区分

这样,相同名称(就是方框中的名字)的信号就是同一个信号,就不同连成一个很大的圈了

  1. 在 matlab 中输入 fuzzy 并且运行就可以得到我们的模糊控制器设置窗口

    我们可以在 edit 中增加输入和输出的数量:改成两个输出的隶属函数和一个输出的隶属函数
    1. 在这个界面可以编辑某个隶属函数的名字增加输入输出隶属函数
    2. 点击曲线图片,进入内部的设置
      1. 我们可以编辑某个隶属函数的某条曲线的名字(就是语言值名字)(或者说是模糊集个数)
      2. 左边的窗口可以编辑这个隶属函数的论域(就是离散之后的取值)
      3. 右边窗口可以通过点击曲线切换不同的曲线,可以修改形状和形状的取值,形状的名字如下:
      4. 点击上面 edit 中的添加 MF 可以新添加这个隶属曲线,可以调节范围——三角形调节两个交点位置和最高点的位置
      5. 视频中的输入 NB 为 z 型,其余是三角波型,论域为 [-6,6]
      6. 输出的话,波形都是三角波型,论域看 pid 的参数
    3. 模糊控制器做好之后就是模糊控制规则了
      1. 双击中间白色的区域就是控制的规则了
        就是我们的 if and then 这种
  2. 都设置好之后,我们就点击文件 ->输出 ->输出到 workspace->命名即可
  3. 之后我们打开我们的 simulink,双击模糊控制的模块,在名字中填入我们保存的名字
  4. 选择 PID(z) 作为控制器,双击,改为从外部输入

最后的结果

之后调节的目标是控制器,不是被控对象