DC-DC Buck Converter as a Dynamical System
Model
The voltage $V$ and current $I$ of a circuit diagram like the one above 1 can be represented as a non-smooth, non-autonomous system. This is referred to as a DC-DC Buck Converter.
$$ \begin{align*} \dot{V} =& - {{ 1 } \over { RC }} V + {{ 1 } \over { C }} I \\ \dot{I} =& - {{ V } \over { L }} + \begin{cases} 0 & , \text{if } V \ge V_{r} (t) \\ E / L & , \text{if } V < V_{r} (t) \end{cases} \\ V_{r} (t) =& \gamma + \eta \left( t \bmod T \right) \end{align*} $$
Variables
- $V(t)$: Represents the voltage at time $t$.
- $I(t)$: Represents the current at time $t$.
- $V_{r}(t)$: Control signal at time $t$ following the $T$-cycle. If the voltage is lower than this signal, the switch opens to charge the battery, and if higher, the switch closes to supply the current.
Parameters
- $\gamma , \eta , T > 0$: Constants related to the configuration of control. A smaller $\gamma = 11.75238 V$ allows for current supply at lower voltages and a larger $\eta = 1309.524Vs^{-1}$ charges the battery at higher voltages. A smaller $T = 400 \mu s$ results in more frequent switching to maintain a more uniform voltage.
- $C = 47 \mu F$: Capacitance.
- $E = 53.500001$: Battery Voltage.
- $L = 20 mH$: Inductance.
- $R = 22 \Omega$: Resistance.
The default values of parameters used in all simulations reproduced in this post are $m = 10^{-3}$, $\mu = 10^{-6}$. Note that in the main reference (Bernardo), $C$ was $4.7 \mu F$, but in another reference (Deane), when $C = 47 \mu F$ was used, the simulation did not work properly, so $C$ was set differently.
Description
The DC-DC buck converter, simply known as Buck Converter, is a converter for lowering the voltage below the input voltage2. In fact, the author is not very familiar with this ‘item’, and as the title suggests, only intends to explain the system.
Non-Smooth System
As can be seen from the equations, the buck converter system is a non-smooth system because the derivatives are not continuous. The red line is the control signal $V_{r} (t)$, and depending on whether the voltage $V(t)$ exceeds this signal or not, the system jumps and transitions non-smoothly. Accordingly, the system voltage is maintained between approximately 11.5~14.0V.
Chaotic System
If we look at the subsystem when the switch opens and closes, each seems to be a linear system, so it does not appear difficult. However, the problem is that the timing $t$, in other words, the switching on and off, is entirely inconsistent.
The above figure shows when switching occurs in the trajectory. As can be seen, there are bands where the dots form a group, i.e., the region $V \approx V_{r}$, but it is certainly hard to guarantee under what conditions it occurs just by looking at $V,I$. Due to this non-smoothness, this seemingly simple system is actually chaotic and has been studied for a long time3.
Bifurcation
If we change the parameter $E$ and plot the value of $V$ according to the signal’s cycle, we can see the bifurcation as shown in the diagram above4. It is normal for two branches to emerge at $E \approx 25$, cross, and periodic orbits to appear around the branches at $E \approx 30$.
Code
Trajectory
The following is Julia code that can reproduce the figure showing the location of jumping points in the system.
using CSV, DataFrames, Clustering
function RK4(f::Function,v::AbstractVector, h=10^(-2))
V1 = f(v)
V2 = f(v + (h/2)*V1)
V3 = f(v + (h/2)*V2)
V4 = f(v + h*V3)
return v + (h/6)*(V1 + 2V2 + 2V3 + V4)
end
if !isfile("./buck.csv")
m = 10^(-3)
μ = 10^(-6)
R = 22
C = 47μ
L = 20m
T = 400μ
γ = 11.75238
η = 1309.524
E = 53.500001
Vr(t) = γ + η * (mod(t, T))
function buck(v)
V, I, t = v
V̇ = - V/(R*C) + I/C
İ = - (V/L) + ifelse(V < Vr(t), E/L, 0)
return [V̇, İ, 1]
end
u0 = [12.3, 0.55, 0.0]
u_ = [u0]
∇_ = []
dt = 0.00001; tend = 0.25
for t in dt:dt:tend
push!(∇_, buck(u_[end]))
push!(u_, RK4(buck, u_[end], dt))
end
push!(∇_, buck(u_[end]))
U = stack(u_)[Not(end), :]
∇ = stack(∇_)[Not(end), :]
CSV.write("buck.csv", DataFrame(
[collect(0:dt:tend)'; U; ∇; Vr.(0:dt:tend)']'
, ["t", "V", "I", "dV", "dI", "Vr"]))
end
DATA = CSV.read("data/buck.csv", DataFrame)
Y = select(DATA, [:dV, :dI]) |> Matrix .|> Float32
X = select(DATA, [ :V, :I]) |> Matrix .|> Float32
XY = [X Y]
dbs = dbscan(col_normalize(Y)', 0.01); nsubsys = length(dbs.clusters); println(nsubsys, " clusters found!")
a1 = plot(DATA.V, DATA.I, alpha = 0.1, legend = :best, label = "Trajectory", xlabel = L"V", ylabel = L"I")
scatter!(a1,
DATA.V[Not(1)][.!iszero.(diff(dbs.assignments))]
, DATA.I[Not(1)][.!iszero.(diff(dbs.assignments))]
, color = 1, shape = :+, label = "Jumping points")
Bifurcation Diagram
Below is the code for the bifurcation diagram. Note that there is a slight technical issue; to accurately simulate the buck converter, the time interval must be around $h = 10^{-7}$. Despite using the Euler method, it takes about 10 hours on an Intel i7-12700F.
const m = 10^(-3)
const μ = 10^(-6)
const R = 22 # 22
const L = 20m # 20m
const C = 47μ # 22μ
const T = 400μ
const γ = 11.7 # 11.75238
const η = 1309.5 # 1309.524
const RC = R*C
using ProgressBars
packages = [:DataFrames, :CSV, :Plots, :LaTeXStrings]
for package in ProgressBar(packages)
@eval using $(package)
end
println(join(packages, ", "), " loaded!")
function Euler(f::Function,v::AbstractVector, h=10^(-2))
V1 = f(v)
return v + h*V1, V1
end
Vr(t) = γ + η * (mod(t, T))
function factory_buck(idx::Int64, E::Number; flag_filesave = false)
EdL = E/L
controlterm = 0.0
function buck(v::AbstractVector)
V, I = v
V̇ = - V/(RC) + I/C
İ = - (V/L) + controlterm
return [V̇, İ]
end
dt = 10^(-7); tend = 0.5
t_ = 0:dt:tend
Vr_ = Vr.(t_)
ndatapoints = round(Int64, tend/(100dt))
len_t_ = length(t_)
traj = zeros(4, len_t_+1)
u = [12.0, 0.55]
du = buck(u)
traj[1:2, 1] = u
for t in 1:length(t_)
controlterm = ifelse(u[1] < Vr_[t], EdL, 0)
u, du = Euler(buck, u, dt)
if t ≥ ndatapoints
traj[3:4, t] = du
traj[1:2, t+1] = u
end
end
traj = traj[:, 1:(end-1)]'
if flag_filesave
data = DataFrame(
[t_ traj Vr_],
["t", "V", "I", "dV", "dI", "Vr"])
@warn "file saving mode!"
CSV.write("G:/buck/buck_$(lpad(idx, 6, '0')).csv", data)
return nothing
else
data = DataFrame(
[t_ traj Vr_][(end-ndatapoints):end, :],
["t", "V", "I", "dV", "dI", "Vr"])
end
return data
end
# E_range = 15:40
E_range = 15:0.001:40
schedule = DataFrame(idx = eachindex(E_range), E = E_range)
xdots = Float64[]; ydots = Float64[]
for dr in ProgressBar(eachrow(schedule))
data = factory_buck(dr.idx, dr.E)
idx_sampled = diff(data.Vr) .< 0
sampledV = data[Not(1), :V][idx_sampled]
append!(xdots, fill(dr.E, length(sampledV)))
append!(ydots, sampledV)
end
@time a1 = scatter(xdots, ydots,
xlabel = L"E", ylabel = L"V",
label = :none, msw = 0, color = :black, ms = 0.5, alpha = 0.5, size = (700, 480));
png(a1, "buck_bifurcation")
https://upload.wikimedia.org/wikipedia/commons/c/ca/Commutation_cell_in_converters.svg ↩︎
J. H. B. Deane and D. C. Hamill, “Analysis, simulation and experimental study of chaos in the buck converter,” 21st Annual IEEE Conference on Power Electronics Specialists, San Antonio, TX, USA, 1990, pp. 491-498, doi: https://doi.org/10.1109/PESC.1990.131228. ↩︎
M. di Bernardo, F. Garefalo, L. Glielmo and F. Vasca, “Switchings, bifurcations, and chaos in DC/DC converters,” in IEEE Transactions on Circuits and Systems I: Fundamental Theory and Applications, vol. 45, no. 2, pp. 133-141, Feb. 1998, doi: https://doi.org/10.1109/81.661675 ↩︎