Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add JuliaC example #13

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ julia = "1.7"
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["AllocCheck", "Test", "ControlSystemsBase", "FixedPointNumbers"]
test = ["AllocCheck", "Test", "ControlSystemsBase", "FixedPointNumbers", "JET"]
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ plot([res, res_fp], plotu=true, lab=["Float64" "" string(T) ""]); ylabel!("u + d

The fixed-point controller behaves roughly the same in this case, but artifacts are clearly visible. If the number of bits used for the fractional part is decreased, the controller will start to misbehave.

## Compilation using JuliaC
The file `examples/juliac/juliac_pid.jl` contains a JuliaC-compatible interface that can be compiled into a C-callable shared library using JuliaC. To compile the file, run the following from the `examples/juliac` folder:
```bash
julia +nightly --project <PATH_TO_JULIA_REPO>/julia/contrib/juliac.jl --output-lib juliac_pid --trim=unsafe-warn --compile-ccallable juliac_pid.jl
```
where `<PATH_TO_JULIA_REPO>` should be replaced with the path to the Julia repository on your system. The command will generate a shared library `juliac_pid` that can be called from C. The file `examples/juliac/juliac_pid.h` contains the C-compatible interface to the shared library.


## See also
- [TrajectoryLimiters.jl](https://github.com/baggepinnen/TrajectoryLimiters.jl) To generate dynamically feasible reference trajectories with bounded velocity and acceleration given an instantaneous reference $r(t)$ which may change abruptly.
- [SymbolicControlSystems.jl](https://github.com/JuliaControl/SymbolicControlSystems.jl) For C-code generation of LTI systems.
4 changes: 4 additions & 0 deletions examples/juliac/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[deps]
ControlSystemsBase = "aaaaaaaa-a6ca-5380-bf3e-84a91bcd477e"
DiscretePIDs = "c1363496-6848-4723-8758-079b737f6baf"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
50 changes: 50 additions & 0 deletions examples/juliac/juliac_pid.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module JuliacPID
import DiscretePIDs
import DiscretePIDs: DiscretePID
import Base.@ccallable

const T = Float64 # The numeric type used by the controller

# Set the initial PID parameters here
const pid = DiscretePIDs.DiscretePID(; K = T(1), Ti = 1, Td = false, Ts = 1)


@ccallable function calculate_control!(r::T, y::T, uff::T)::T
DiscretePIDs.calculate_control!(pid, r, y, uff)::T
end

function set_K!(K::T, r::T, y::T)::Cvoid
DiscretePIDs.set_K!(pid, K, r, y)
nothing
end

function set_Ti!(Ti::T)::Cvoid
DiscretePIDs.set_Ti!(pid, Ti)
nothing
end

function set_Td!(Td::T)::Cvoid
DiscretePIDs.set_Td!(pid, Td)
nothing
end

@ccallable function reset_state!()::Cvoid
DiscretePIDs.reset_state!(pid)
nothing
end

@ccallable function main()::Cint
println(Core.stdout, "I'm alive and well")
u = calculate_control!(0.0, 0.0, 0.0)
println(Core.stdout, u)

Cint(0)
end


end

# compile using something like
# cd(@__DIR__)
# run(`julia +nightly --project --experimental /home/fredrikb/repos/julia/contrib/juliac.jl --output-lib juliac_pid --trim=unsafe-warn --compile-ccallable juliac_pid.jl`)
# run(`ls -ltrh`) # marvel at the smallness of the binary
30 changes: 30 additions & 0 deletions examples/juliac/test_juliac_pid.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Run this file with the same version of julia that you used to compile the shared library.
cd(@__DIR__)

const T = Float64
lib = Libc.Libdl.dlopen("/home/fredrikb/.julia/dev/DiscretePIDs/examples/juliac/juliac_pid.so")
const calc = Libc.Libdl.dlsym(lib, :calculate_control!)

function pid(r::T, y::T, uff::T)
ccall(calc, T, (T, T, T), r, y, uff)
end

pid(0.0, 0.0, 0.0) # test

using ControlSystemsBase, Plots
Tf = 15 # Simulation time
Ts = 0.01 # sample time

P = c2d(ss(tf(1, [1, 1])), Ts) # Process to be controlled, discretized using zero-order hold

ctrl = function(x,t)
y = (P.C*x)[] # measurement
d = 1 # disturbance
r = 0 # reference
u = pid(T(r), T(y), T(0))
u + d # Plant input is control signal + disturbance
end

res = lsim(P, ctrl, Tf)

plot(res, plotu=true); ylabel!("u + d", sp=2)
4 changes: 4 additions & 0 deletions src/DiscretePIDs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ function set_K!(pid::DiscretePID, K, r, y)
pid.bi = K * pid.Ts / pid.Ti
pid.I = pid.I + Kold*(pid.b*r - y) - K*(pid.b*r - y)
end
nothing
end

"""
Expand All @@ -139,6 +140,7 @@ function set_Ti!(pid::DiscretePID{T}, Ti) where T
else
pid.bi = zero(T)
end
nothing
end

"""
Expand All @@ -151,6 +153,7 @@ function set_Td!(pid::DiscretePID, Td)
pid.Td = Td
pid.ad = Td / (Td + pid.N * pid.Ts)
pid.bd = pid.K * pid.N * pid.ad
nothing
end


Expand Down Expand Up @@ -193,6 +196,7 @@ function reset_state!(pid::DiscretePID)
pid.I = zero(pid.I)
pid.D = zero(pid.D)
pid.yold = zero(pid.yold)
nothing
end

end
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ using DiscretePIDs
using Test
using ControlSystemsBase
using AllocCheck
using JET

@testset "DiscretePIDs.jl" begin

Expand Down Expand Up @@ -95,6 +96,10 @@ reset_state!(pid)
res3 = lsim(P, ctrl, Tf)
@test res3.y == res2.y

@test_opt pid(1.0, 1.0)
@test_opt pid(1.0, 1.0, 1.0)
# @report_call pid(1.0, 1.0)

## Test with FixedPointNumbers
using FixedPointNumbers
T = Fixed{Int16, 10} # 16-bit signed fixed-point with 10 bits for the fractional part
Expand Down
Loading