Skip to content

Commit ed7561a

Browse files
authored
Merge pull request #16 from JuliaComputing/expfilt
add exponential filter component
2 parents ba6afb5 + c22fa2b commit ed7561a

File tree

5 files changed

+65
-6
lines changed

5 files changed

+65
-6
lines changed

docs/src/examples/dc_motor_pi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ Plots.plot(p1, p2, layout = (2, 1))
104104

105105
## Discrete-time controller
106106

107-
Until now, we have modeled both the physical part of the system, the DC motor, and the control systems, in continuous time. In practice, it is common to implement control systems on a computer operating at a fixed sample rate, i.e, in discrete time. A system containing both continuous-time parts and discrete-time parts is often referred to as a "sampled-data system", and the ModelingToolkit standard library contains several components to model such systems.
107+
Until now, we have modeled both the physical part of the system, the DC motor, and the control systems, in continuous time. In practice, it is common to implement control systems on a computer operating at a fixed sample rate, i.e, in discrete time. A system containing both continuous-time parts and discrete-time parts is often referred to as a "sampled-data system", and the ModelingToolkit standard library together with ModelingToolkitSampledData.jl contain several components to model such systems.
108108

109109
Below, we re-model the system, this time with a discrete-time controller: [`DiscretePIDStandard`](@ref). To interface between the continuous and discrete parts of the model, we make use of a [`Sampler`](@ref) and [`ZeroOrderHold`](@ref) blocks. Apart from the three aforementioned components, the model is the same as before.
110110

docs/src/tutorials/noise.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Measurement noise and corruption
2-
Measurement noise is practically always present in signals originating from real-world sensors. In a sampled-data system, analyzing the influence of measurement noise using simulation is relatively straight forward. Below, we add Gaussian white noise to the speed sensor signal in the DC motor example. The noise is added using the [`NormalNoise`](@ref) block.
2+
Measurement noise is practically always present in signals originating from real-world sensors. In a sampled-data system, analyzing the influence of measurement noise using simulation is relatively straight forward. Below, we add Gaussian white noise to the speed sensor signal in the DC-motor example from [DC Motor with PI-controller](@ref). The noise is added using the [`NormalNoise`](@ref) block.
33

44
This block has two modes of operation
55
1. If `additive = false` (default), the block has the connector `output` only, and this output is the noise signal.
66
2. If `additive = true`, the block has the connectors `input` and `output`, and the output is the sum of the input and the noise signal, i.e., the noise is _added_ to the input signal. This mode makes it convenient to add noise to a signal in a sampled-data system.
77

88
## Example: Noise
9+
This example is a continuation of the DC-motor example from [DC Motor with PI-controller](@ref). We add Gaussian white noise with ``σ^2 = 0.1^2`` to the speed sensor signal.
910
```@example NOISE
1011
using ModelingToolkit
1112
using ModelingToolkit: t_nounits as t
@@ -84,9 +85,9 @@ plot(figy, figu, plot_title = "DC Motor with Discrete-time Speed Controller")
8485
```
8586

8687
## Noise filtering
87-
No discrete-time filter components are available yet. You may, e.g.
88-
- Add exponential filtering using `xf(k) ~ (1-α)xf(k-1) + α*x(k)`, where `α` is the filter coefficient and `x` is the signal to be filtered.
89-
- Add moving average filtering using `xf(k) ~ 1/N sum(i->x(k-i), i=0:N-1)`, where `N` is the number of samples to average over.
88+
You may, e.g.
89+
- Use [`ExponentialFilter`](@ref) to add exponential filtering using `y(k) ~ (1-a)y(k-1) + a*u(k)`, where `a` is the filter coefficient and `u` is the signal to be filtered.
90+
- Add moving average filtering using `y(k) ~ 1/N sum(i->u(k-i), i=0:N-1)`, where `N` is the number of samples to average over.
9091

9192
## Colored noise
9293
Colored noise can be achieved by filtering white noise through a filter with the desired spectrum. No components are available for this yet.

src/ModelingToolkitSampledData.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export get_clock
77
export DiscreteIntegrator, DiscreteDerivative, Delay, Difference, ZeroOrderHold, Sampler,
88
ClockChanger,
99
DiscretePIDParallel, DiscretePIDStandard, DiscreteStateSpace,
10-
DiscreteTransferFunction, NormalNoise, UniformNoise, Quantization
10+
DiscreteTransferFunction, NormalNoise, UniformNoise, Quantization,
11+
ExponentialFilter
1112
include("discrete_blocks.jl")
1213

1314
end

src/discrete_blocks.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ A quantization block that quantizes the input signal to a specified number of bi
888888
- `y_min`: Lower limit of output
889889
- `bits`: Number of bits of quantization
890890
- `quantized`: If quantization effects shall be computed. If false, the output is equal to the input, which may be useful for, e.g., linearization.
891+
- `midrise`: (structural) If true (default), the quantizer is a midrise quantizer, otherwise it is a midtread quantizer. See [Docs: Quantization](https://juliacomputing.github.io/ModelingToolkitSampledData.jl/dev/tutorials/noise/#Quantization) for more details.
891892
892893
# Connectors:
893894
- `input`
@@ -940,3 +941,33 @@ function quantize(u, bits, y_min, y_max, midrise)
940941
end
941942

942943
@register_symbolic quantize(u::Real, bits::Real, y_min::Real, y_max::Real, midrise::Bool)
944+
945+
"""
946+
ExponentialFilter(a = 0.1)
947+
948+
Exponential filtering with input-output relation ``y(z) ~ (1 - a) y(z-1) + a u(z-1)``
949+
950+
# Parameters:
951+
- `a`: Filter parameter `[0, 1]`, a small value implies stronger filtering.
952+
953+
# Variables:
954+
- `u`: Input signal
955+
- `y`: Output signal
956+
957+
# Connectors:
958+
- `input::RealInput`: Input signal
959+
- `output::RealOutput`: Output signal
960+
"""
961+
@mtkmodel ExponentialFilter begin
962+
@extend u, y = siso = SISO()
963+
@structural_parameters begin
964+
z = ShiftIndex()
965+
end
966+
@parameters begin
967+
a = 0.1, [description = "Filter parameter"]
968+
end
969+
@equations begin
970+
y(z) ~ (1 - a) * y(z-1) + a * u(z)
971+
end
972+
end
973+

test/test_discrete_blocks.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,29 @@ end
462462
end
463463

464464

465+
@testset "ExponentialFilter" begin
466+
@info "Testing ExponentialFilter"
467+
z = ShiftIndex(Clock(0.1))
468+
@mtkmodel ExponentialFilterModel begin
469+
@components begin
470+
input = Step(start_time=1, smooth=false)
471+
filter = ExponentialFilter(; a = 0.1, z)
472+
end
473+
@variables begin
474+
x(t) = 0 # Dummy variable to workaround JSCompiler bug
475+
end
476+
@equations begin
477+
connect(input.output, filter.input)
478+
D(x) ~ 0
479+
end
480+
end
481+
@named m = ExponentialFilterModel()
482+
m = complete(m)
483+
ssys = structural_simplify(IRSystem(m))
484+
prob = ODEProblem(ssys, [m.filter.y(z-1) => 0], (0.0, 10.0))
485+
sol = solve(prob, Tsit5(), dtmax=0.1)
486+
@test sol(10, idxs=m.filter.y) 1 atol=0.001
487+
@test sol(0.999, idxs=m.filter.y) == 0
488+
@test sol(1.1, idxs=m.filter.y) > 0
489+
490+
end

0 commit comments

Comments
 (0)