Skip to content

Commit 33ae6df

Browse files
authored
Merge pull request #721 from JuliaControl/deadzone
add deadzone nonlinearity
2 parents 4988ad1 + ce27ca5 commit 33ae6df

File tree

4 files changed

+132
-52
lines changed

4 files changed

+132
-52
lines changed

docs/src/lib/nonlinear.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ plot(f1,f2, size=(1300,800))
238238
- Discrete-time support.
239239
- Basic support for nonlinear analysis such as stability proof through the circle criterion etc. In particular, predefined nonlinear functions may specify sector bounds for the gain, required by the circle-criterion calculations.
240240
- Additional nonlinear components, such as
241-
- Dead zone
242241
- Integrator anti-windup
243242
- Friction models?
244243

@@ -249,4 +248,5 @@ ControlSystems.nonlinearity
249248
ControlSystems.offset
250249
ControlSystems.saturation
251250
ControlSystems.ratelimit
251+
ControlSystems.deadzone
252252
```

src/hammerstein_weiner.jl

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -229,54 +229,3 @@ function impulse(sys::HammersteinWienerSystem{T}, t::AbstractVector; kwargs...)
229229
end
230230

231231

232-
struct Saturation{T} <: Function
233-
l::T
234-
u::T
235-
end
236-
Saturation(u) = Saturation(-u, u)
237-
238-
(s::Saturation)(x) = clamp(x, s.l, s.u)
239-
240-
"""
241-
saturation(val)
242-
saturation(lower, upper)
243-
244-
Create a saturating nonlinearity. Connect it to the output of a controller `C` using
245-
```
246-
Csat = saturation(val) * C
247-
```
248-
249-
$nonlinear_warning
250-
251-
Note: when composing linear systems with nonlinearities, it's often important to handle operating points correctly.
252-
See [`ControlSystems.offset`](@ref) for handling operating points.
253-
"""
254-
saturation(args...) = nonlinearity(Saturation(args...))
255-
saturation(v::AbstractVector, args...) = nonlinearity(Saturation.(v, args...))
256-
Base.show(io::IO, f::Saturation) = f.u == -f.l ? print(io, "saturation($(f.u))") : print(io, "saturation($(f.l), $(f.u))")
257-
258-
struct Offset{T} <: Function
259-
o::T
260-
end
261-
262-
(s::Offset)(x) = x .+ s.o
263-
264-
"""
265-
offset(val)
266-
267-
Create a constant-offset nonlinearity `x -> x + val`.
268-
269-
$nonlinear_warning
270-
271-
# Example:
272-
To create a linear system that operates around operating point `y₀, u₀`, use
273-
```julia
274-
offset_sys = offset(y₀) * sys * offset(-u₀)
275-
```
276-
note the sign on the offset `u₀`. This ensures that `sys` operates in the coordinates
277-
`Δu = u-u₀, Δy = y-y₀` and the inputs and outputs to the offset system are in their non-offset coordinate system. If the system is linearized around `x₀`, `y₀` is given by `C*x₀`. Additional information and an example is available here https://juliacontrol.github.io/ControlSystems.jl/latest/lib/nonlinear/#Non-zero-operating-point
278-
"""
279-
offset(val::Number) = nonlinearity(Offset(val))
280-
offset(v::AbstractVector) = nonlinearity(Offset.(v))
281-
282-
Base.show(io::IO, f::Offset) = print(io, "offset($(f.o))")

src/nonlinear_components.jl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,73 @@
1+
struct Saturation{T} <: Function
2+
l::T
3+
u::T
4+
end
5+
Saturation(u) = Saturation(-u, u)
6+
7+
(s::Saturation)(x) = clamp(x, s.l, s.u)
8+
9+
"""
10+
saturation(val)
11+
saturation(lower, upper)
12+
13+
Create a saturating nonlinearity. Connect it to the output of a controller `C` using
14+
```
15+
Csat = saturation(val) * C
16+
```
17+
18+
```
19+
y▲ ────── upper
20+
│ /
21+
│ /
22+
│/
23+
──────────┼────────► u
24+
/│
25+
/ │
26+
/ │
27+
lower────
28+
```
29+
30+
$nonlinear_warning
31+
32+
Note: when composing linear systems with nonlinearities, it's often important to handle operating points correctly.
33+
See [`ControlSystems.offset`](@ref) for handling operating points.
34+
"""
35+
saturation(args...) = nonlinearity(Saturation(args...))
36+
saturation(v::AbstractVector, args...) = nonlinearity(Saturation.(v, args...))
37+
Base.show(io::IO, f::Saturation) = f.u == -f.l ? print(io, "saturation($(f.u))") : print(io, "saturation($(f.l), $(f.u))")
38+
39+
## Offset ======================================================================
40+
41+
struct Offset{T} <: Function
42+
o::T
43+
end
44+
45+
(s::Offset)(x) = x .+ s.o
46+
47+
"""
48+
offset(val)
49+
50+
Create a constant-offset nonlinearity `x -> x + val`.
51+
52+
$nonlinear_warning
53+
54+
# Example:
55+
To create a linear system that operates around operating point `y₀, u₀`, use
56+
```julia
57+
offset_sys = offset(y₀) * sys * offset(-u₀)
58+
```
59+
note the sign on the offset `u₀`. This ensures that `sys` operates in the coordinates
60+
`Δu = u-u₀, Δy = y-y₀` and the inputs and outputs to the offset system are in their non-offset coordinate system. If the system is linearized around `x₀`, `y₀` is given by `C*x₀`. Additional information and an example is available here https://juliacontrol.github.io/ControlSystems.jl/latest/lib/nonlinear/#Non-zero-operating-point
61+
"""
62+
offset(val::Number) = nonlinearity(Offset(val))
63+
offset(v::AbstractVector) = nonlinearity(Offset.(v))
64+
65+
Base.show(io::IO, f::Offset) = print(io, "offset($(f.o))")
66+
67+
68+
69+
## Ratelimit ===================================================================
70+
171
"""
272
ratelimit(val; Tf)
373
ratelimit(lower, upper; Tf)
@@ -17,3 +87,48 @@ function ratelimit(args...; Tf)
1787
integrator*saturation(args...)*F
1888
end
1989

90+
91+
92+
## DeadZone ====================================================================
93+
94+
struct DeadZone{T} <: Function
95+
l::T
96+
u::T
97+
end
98+
DeadZone(u) = DeadZone(-u, u)
99+
100+
function (s::DeadZone)(x)
101+
if x > s.u
102+
x - s.u
103+
elseif x < s.l
104+
x - s.l
105+
else
106+
zero(x)
107+
end
108+
end
109+
110+
"""
111+
deadzone(val)
112+
deadzone(lower, upper)
113+
114+
Create a dead-zone nonlinearity.
115+
116+
```
117+
y▲
118+
│ /
119+
│ /
120+
lower │ /
121+
─────|──┼──|───────► u
122+
/ │ upper
123+
/ │
124+
/ │
125+
```
126+
127+
$nonlinear_warning
128+
129+
Note: when composing linear systems with nonlinearities, it's often important to handle operating points correctly.
130+
See [`ControlSystems.offset`](@ref) for handling operating points.
131+
"""
132+
deadzone(args...) = nonlinearity(DeadZone(args...))
133+
deadzone(v::AbstractVector, args...) = nonlinearity(DeadZone.(v, args...))
134+
Base.show(io::IO, f::DeadZone) = f.u == -f.l ? print(io, "deadzone($(f.u))") : print(io, "deadzone($(f.l), $(f.u))")

test/test_nonlinear_components.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@ end
1212

1313
##
1414

15+
using ControlSystems
16+
using ControlSystems: deadzone
17+
18+
th = 0.7
19+
@show nl = deadzone(th)
20+
res = lsim(nl, (x,t)->sin(t), 0:0.01:4pi)
21+
# plot(res)
22+
@test minimum(res.y) -0.3 rtol=1e-3
23+
@test maximum(res.y) 0.3 rtol=1e-3
24+
25+
@show nl = deadzone(-2th, th)
26+
res = lsim(nl, (x,t)->2sin(t), 0:0.01:4pi)
27+
# plot(res)
28+
@test minimum(res.y) -0.6 rtol=1e-3
29+
@test maximum(res.y) 1.3 rtol=1e-3
30+

0 commit comments

Comments
 (0)