Engee 文档
Notebook

设计智能手机相机对焦系统

我们将连接必要的库。

In [ ]:
import Pkg; 
Pkg.add(["ControlSystemIdentification", "ControlSystems"])
   Resolving package versions...
  No Changes to `~/.project/Project.toml`
  No Changes to `~/.project/Manifest.toml`
In [ ]:
using ControlSystems                # основная библиотека для работы с САУ
using ControlSystemIdentification   # библиотека для оценки динамических моделей на основе данных 

管理对象

下面所示的模型模拟了智能手机相机的自动对焦控制系统。

image.png

考虑智能手机的自动对焦模型。 智能手机的电子设备通过摄像头的反馈控制电机。 让我们从技术中抽象出来,并说我们需要过渡过程从我们收到焦点命令的那一刻起不超过0.6秒。 使用PID控制器,我们将尝试实现这样的系统。

我们的电机-镜头系统是非线性的,因此首先我们对模型进行线性化。

控制对象的线性化

让我们使用团队管理运行模型。 结果,我们在工作区中得到一个变量 data,其中包含仿真结果。

In [ ]:
modelName = "lens"
if !(modelName in [m.name for m in engee.get_all_models()]) engee.load( "$(@__DIR__)/$(modelName).engee"); end;
data = engee.run( modelName )
Out[0]:
SimulationResult(
    "Преобразование единиц измерения.1" => WorkspaceArray{Float64}("lens/Преобразование единиц измерения.1")
,
    "Ступенчатая функция.1" => WorkspaceArray{Float64}("lens/Ступенчатая функция.1")

)

让我们将输入和输出信号的数据写入变量并将它们绘制在图形上。

In [ ]:
dataIn = collect(data["Ступенчатая функция.1"]).value;
dataOut = collect(data["Преобразование единиц измерения.1"]).value;
data_t = collect(data["Преобразование единиц измерения.1"]).time;

plot( data_t, [dataIn dataOut], label=["Входной сигнал" "Выходной сигнал"] )
Out[0]:

让我们继续讨论模型的线性化。 识别库模型的所有方法 ControlSystemIdentification 它们接受AbstractIdData类型对象作为输入。 该函数允许您获取时域标识数据的对象 iddata. 接下来,使用方法 subspaceid 我们以状态空间的形式获得系统的描述。

In [ ]:
Ts = data_t[2] - data_t[1]                     # Получим частоту дискретизации системы из вектора времени
d  = iddata(dataOut, dataIn, Ts);              # создаем объект идентификации данных во временном пространстве 
sys_d = subspaceid(d, :auto);                  # запускаем метод идентификации модели пространства состояний

让我们将获得的模型描述与建模环境中组装的模型进行比较。 要做到这一点,使用块 Пространство состояний 作为矩阵的值,我们将写出获得的值A,B,C,D。

In [ ]:
sys = d2c(sys_d);   # перейдем к непрерывной модели
A = sys.A;          # находим матрицу A
B = sys.B;          # находим матрицу B
C = sys.C;          # находим матрицу C
D = sys.D;          # находим матрицу D
Warning: d2c does not handle a non-zero cross covariance S, S will be set to zero
@ ControlSystemIdentification /usr/local/ijulia-demos/packages/ControlSystemIdentification/KM4Nc/src/ControlSystemIdentification.jl:359
Error: Calculated covariance matrix not positive definite
@ ControlSystemsBase /usr/local/ijulia-core/packages/ControlSystemsBase/FUrnq/src/discrete.jl:324

系数的选择:pid()函数

为了评估所选系数值的合适程度,我们将绘制LFX,Lfx和Nyquist hodograph。 并且还要评估过渡过程的质量。 为了方便显示频率响应图,下面实现了该功能 pid_marginplot. 它将得到的板系统的值代入用于构造频率特性的函数。: marginplotnyquistplot,并将它们显示在图形上。

In [ ]:
function pid_marginplot(C)
    f1 = marginplot(sys_tf * C, w)
    f2 = nyquistplot(sys_tf * C, xlims=(-10,1), ylims=(-2, 10), title="")
    plot(f1,f2, titlefontsize=10)
end
Out[0]:
pid_marginplot (generic function with 1 method)

设置其他变量。 让我们将系统的表示转换为传递函数,并设置我们将构建时间和频率特性的频率。

In [ ]:
sys_tf = tf(sys);
w = exp10.(LinRange(-2.5, 3, 500));

我们将使用单元掩码设置系数。 通过移动滑块,您可以设置一个新的变量值。 当一个系数被改变时,掩码下的代码被重新计算。 如果要一次更改多个参数,请在单元格开始按钮下禁用自动刷新并手动启动单元格。

系数的选择是迭代的,因此首先我们将在没有调节器的情况下评估系统的特性,然后我们将开始改变每一个并评估变化对系统的影响。

In [ ]:
kp = 0.4 # @param {type:"slider", min:0.1, max:5, step:0.1}
ki = 1.322 # @param {type:"slider", min:0.0, max:20, step:0.001}
kd = 0.03 # @param {type:"slider", min:0.0, max:0.5, step:0.01}
Tf = 0.012 # @param {type:"slider", min:0.0, max:1, step:0.001}

C0 = pid(kp, ki, kd; Tf, form=:parallel, filter_order=2)
pid_marginplot(C0)
Out[0]:

让我们检查一下我们的系统是否稳定。 这可以让你做的功能 isstable.

In [ ]:
isstable(feedback(sys_tf * C0))     # Проверка: устойчива ли система с полученным ПИД регулятором
Out[0]:
true

此外,如果系统稳定,稳定性储备符合我们的要求,我们将建立一个过渡过程并评估其特性的质量。

In [ ]:
plot(stepinfo(step(feedback(sys_tf * C0), 5)))      # Оценка переходного процесса САУ
Out[0]:

获得的控制器数据可用于仿真环境中的模型。 为此,您可以从工作区中获取系数值,并在PID控制器块中指定它们。

系数选择:loopshapingPID()函数

使用函数选择系数 loopshapingPID 它基于频率方法(环路整形),它允许您设置开环系统的所需频率响应,以确保闭环的所需特性。 因此,如果系统的期望频率属性是已知的(例如,所需带宽或扰动抑制水平),则该方法是方便的。

让我们尝试将此方法应用于我们的示例,并获得所需的特性。

该函数以传递函数的形式对系统进行描述,以及期望实现开放系统的单个增益的频率。 另外,该函数接受附加灵敏度函数Mt和期望相移ζt的参数值。 默认情况下,这些参数具有最佳值,但可以更改它们。 您还可以调整合成PID控制器的形状并显示有助于评估生成的ACS质量的图形。

In [ ]:
ω  = 17
Tf = 1/1000ω   
C0, kp, ki, kd, fig, CF = loopshapingPID(sys_tf, ω; Mt = 1.3, ϕt = 75, form=:parallel, doplot = true)
Out[0]:
(C = TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
0.054635017823986276s^2 + 0.9730264418987029s + 6.385190809107749
-----------------------------------------------------------------
                              1.0s

Continuous-time transfer function model, kp = 0.9730264418987029, ki = 6.385190809107749, kd = 0.054635017823986276, fig = Plot{Plots.PlotlyJSBackend() n=18}, CF = TransferFunction{Continuous, ControlSystemsBase.SisoRational{Float64}}
0.054635017823986276s^2 + 0.9730264418987029s + 6.385190809107749
-----------------------------------------------------------------
    3.4602076124567474e-9s^3 + 8.31890330807703e-5s^2 + 1.0s

Continuous-time transfer function model)

让我们试着改变频率。 如果我们分析奈奎斯特图,我们可以看到特征非常接近临界点。 让我们尝试调整并安装 ω = 17. 现在调整后的系统的特性将在点处触及圆Mt。 ω = 17 在一个角度 ϕt = 75.

In [ ]:
plot(fig)
Out[0]:

让我们确保生成的系统是稳定的。

In [ ]:
isstable(feedback(CF*sys_tf))
Out[0]:
true

我们将建立LCH和LFCH系统,并反映可持续性的储备.

In [ ]:
marginplot([sys_tf, CF*sys_tf])
Out[0]:

我们还将假设接收到的ACS的过渡过程。 我们看到过渡时间约为0.4秒,这符合我们的要求。

In [ ]:
plot(stepinfo(step(feedback(CF * sys_tf), 1)))
Out[0]:

使用获得的PID控制器,我们将在模拟环境中模拟系统。 我们将在图表上显示结果。

In [ ]:
tuned_mdl = engee.load(joinpath("$(@__DIR__)","lens_pid.engee");force=true)
res = engee.run(tuned_mdl);
In [ ]:
res_step = collect(res["Ступенчатая функция.1"]);
res_out = collect(res["Преобразование единиц измерения.1"]);
plot(res_step.time, [res_step.value res_out.value], label=["Входной сигнал" "Выходной сигнал"])
Out[0]: