跳转至

大作业

📖 阅读信息

阅读时间:1 分钟 | 中文字符:549 | 有效代码行数: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}=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