Engee documentation
Notebook

Designing a smartphone camera focusing system

We will connect the necessary libraries.

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   # библиотека для оценки динамических моделей на основе данных 

Management object

The model shown below simulates the autofocus control system of a smartphone camera.

image.png

Consider the smartphone's autofocus model. The smartphone's electronics control the motor through feedback from the camera. Let's abstract from the technology and say that we need the transition process to take no more than 0.6 seconds from the moment we receive the focus command. Using the PID controller, we will try to implement such a system.

Our motor-lens system is non-linear, so first of all we linearize the model.

Linearization of the control object

Let's run the model using team management. As a result, we get a variable in the workspace data, which contains the simulation results.

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")

)

Let's write the data of the input and output signals into variables and plot them on a graph.

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]:

Let's move on to the linearization of the model. All methods for identifying the library model ControlSystemIdentification they accept an AbstractIdData type object as input. The function allows you to get an object of time domain identification data iddata. Next, using the method subspaceid we obtain a description of the system in the form of a state space.

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

Let's compare the resulting model description with the model assembled in the modeling environment. To do this, use the block Пространство состояний and as the values of the matrices, we will write the obtained values 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

Selection of coefficients: the pid() function

To assess how suitable the selected coefficient values are, we will plot the LFX, LFX and Nyquist hodograph. And also to evaluate the quality of the transition process. For convenient display of frequency response graphs, the function is implemented below pid_marginplot. It substitutes the values of the resulting board system into a function for constructing frequency characteristics.: marginplot and nyquistplot, and displays them on graphs.

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)

Setting additional variables. Let's transform the representation of the system into a transfer function and set the frequencies for which we will build time and frequency characteristics.

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

We will set the coefficients using the cell mask. By moving the sliders, you set a new variable value. When one coefficient is changed, the code under the mask is recalculated. If you want to change several parameters at once, disable auto-refresh under the cell start button and start the cell manually.

The selection of coefficients is iterative, so first we will evaluate the characteristics of the system without a regulator, then we will begin to change each one and evaluate the impact of changes on the system.

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]:

Let's check if our system is stable. This allows you to do the function isstable.

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

Further, if the system is stable and the stability reserves meet our requirements, we will build a transition process and evaluate the quality of its characteristics.

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

The obtained controller data can be used for a model in a simulation environment. To do this, you can take the coefficient values from the workspace and specify them in the PID controller block.

Coefficient selection: loopshapingPID() function

Selection of coefficients using the function loopshapingPID It is based on the frequency approach (Loop Shaping), which allows you to set the desired frequency response of an open-loop system to ensure the desired characteristics of a closed loop. Thus, this method is convenient if the desired frequency properties of the system are known (for example, the required bandwidth or the level of disturbance suppression).

Let's try to apply this method to our example and get the desired characteristics.

The function takes a description of the system in the form of a transfer function and the frequency at which it is desirable to achieve a single gain of the open system. Additionally, the function accepts the parameter values of the additional sensitivity function Mt and the desired phase shift ϕt. By default, these parameters have optimal values, but they can be changed. You can also adjust the shape of the synthesized PID controller and display graphs that will help evaluate the quality of the resulting 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)

Let's try to change the frequency. If we analyze the Nyquist diagram, we can see that the characteristic passes very close to the critical point. Let's try to adjust this and install ω = 17. Now the characteristic of the adjusted system will touch the circle Mt at the point where ω = 17 at an angle ϕt = 75.

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

Let's make sure that the resulting system is stable.

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

We will build LCH and LFCH systems and reflect the reserves of sustainability.

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

We will also postulate the transition process of the received ACS. We see that the transition time is about 0.4 seconds, which meets our requirements.

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

Using the obtained PID controller, we will simulate the system in a simulation environment. And we will display the results on a graph.

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]: