Skip to content
Merged
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
37 changes: 1 addition & 36 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run: |
docker run --rm --volume ${{ github.workspace }}:/data fsfe/reuse lint

test:
test-with-code-coverage:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
Expand Down Expand Up @@ -86,41 +86,6 @@ jobs:
if-no-files-found: ignore
retention-days: 7

test-with-code-coverage:
name: Julia 1.11 - ubuntu-latest - x64 - with code coverage
runs-on: ubuntu-latest
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ github.event.client_payload.ref || github.head_ref || github.ref }}

- name: Copy default Manifest files
run: |
cp Manifest-v1.11.toml.default Manifest.toml ||
echo "No default available for Manifest-v1.11.toml"
shell: bash

- name: Install matplotlib
run: sudo apt-get install -y python3-matplotlib
shell: bash

- uses: julia-actions/setup-julia@v2
with:
version: '1.11'
arch: x64

- uses: julia-actions/cache@v2

- uses: julia-actions/julia-buildpkg@v1

- name: Build PyCall
run: julia --project -e 'ENV["SAM_PRECOMPILE"]="false"; ENV["PYTHON"]=""; using Pkg; Pkg.instantiate(); Pkg.add("PyCall"); Pkg.build("PyCall")'
shell: bash

- uses: julia-actions/julia-runtest@v1

- uses: julia-actions/julia-processcoverage@v1

- uses: codecov/codecov-action@v5
Expand Down
3 changes: 2 additions & 1 deletion docs/src/exported_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ Pulley(idx, segment_idxs, type)
Tether
Tether(idx, segment_idxs, winch_idx)
Winch
Winch(idx, model, tether_idxs; tether_len, tether_vel, brake)
Winch(idx, set::Settings, tether_idxs; tether_len, tether_vel, brake)
Winch(idx, tether_idxs, gear_ratio, drum_radius, f_coulomb, c_vf, inertia_total; tether_len, tether_vel, brake)
Wing
Wing(idx, vsm_aero, vsm_wing, vsm_solver, group_idxs, R_b_c, pos_cad; transform_idx)
Transform
Expand Down
3 changes: 1 addition & 2 deletions docs/src/private_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ SymbolicAWEModels.get_sys_struct_hash
## Physics and Geometry Helpers

```@docs
SymbolicAWEModels.calc_speed_acc
SymbolicAWEModels.calc_moment_acc
SymbolicAWEModels.calc_angle_of_attack
SymbolicAWEModels.calc_heading
SymbolicAWEModels.calc_R_t_w
SymbolicAWEModels.calc_R_v_w
SymbolicAWEModels.cad_to_body_frame
SymbolicAWEModels.calc_pos
SymbolicAWEModels.calc_steady_torque
SymbolicAWEModels.find_axis_point
SymbolicAWEModels.quaternion_to_rotation_matrix
SymbolicAWEModels.rotation_matrix_to_quaternion
Expand Down
6 changes: 3 additions & 3 deletions docs/src/tutorial_system_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ set.v_wind = 0.0
tethers = [Tether(1,[segment.idx for segment in segments])]
```
As you can see, we just add all of the segments from the simple tether to our [`Tether`](@ref) struct.
The next step is to create a winch. Each winch can be connected to one or more tethers, so it is possible to connect multiple tethers to the same winch. We have to specify which kind of winch we want to use. For now, only the `TorqueControlledMachine` from `WinchModels.jl` is supported.
The next step is to create a winch. Each winch can be connected to one or more tethers, so it is possible to connect multiple tethers to the same winch.
The winch is a torque controlled winch.

```julia
using WinchModels
wm = TorqueControlledMachine(set)
winches = [Winch(1, wm, [1])]
winches = [Winch(1, set, [1])]
```

The 2d plot of the [`SystemStructure`](@ref) should still look the same, so we don't have to plot that. We can just create the system, and simulate it. We just need to be sure that we call plot with `t=0.0` to reset the plot.
Expand Down
2 changes: 1 addition & 1 deletion examples/ram_air_kite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sam = SymbolicAWEModel(set)
SymbolicAWEModels.init!(sam)

find_steady_state!(sam)
bias = 0.15
bias = 0.2
if set.physical_model == "4_attach_ram"
bias = 0.05
end
Expand Down
64 changes: 41 additions & 23 deletions src/generate_system.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,6 @@

# Implementation of the ram air wing model using ModelingToolkit.jl

"""
calc_speed_acc(winch::AsyncMachine, tether_vel, norm_, set_speed)

Calculate winch acceleration for a speed-controlled asynchronous machine model.
"""
function calc_speed_acc(winch::AsyncMachine, tether_vel, norm_, set_speed)
calc_acceleration(winch, tether_vel, norm_; set_speed, set_torque=nothing, use_brake=false) # TODO: add brake setting
end

"""
calc_moment_acc(winch::TorqueControlledMachine, tether_vel, norm_, set_torque)

Calculate winch acceleration for a torque-controlled machine model.
"""
function calc_moment_acc(winch::TorqueControlledMachine, tether_vel, norm_, set_torque)
calc_acceleration(winch, tether_vel, norm_; set_speed=nothing, set_torque, use_brake=false)
end

"""
calc_angle_of_attack(va_wing_b)

Expand Down Expand Up @@ -184,6 +166,17 @@ get_fix_point_sphere(sys::SystemStructure, idx::Int16) = sys.points[idx].fix_sph
get_fix_wing_sphere(sys::SystemStructure, idx::Int16) = sys.wings[idx].fix_sphere
@register_symbolic get_fix_wing_sphere(sys::SystemStructure, idx::Int16)

get_winch_gear_ratio(sys::SystemStructure, idx::Int16) = sys.winches[idx].gear_ratio
@register_symbolic get_winch_gear_ratio(sys::SystemStructure, idx::Int16)
get_winch_drum_radius(sys::SystemStructure, idx::Int16) = sys.winches[idx].drum_radius
@register_symbolic get_winch_drum_radius(sys::SystemStructure, idx::Int16)
get_winch_f_coulomb(sys::SystemStructure, idx::Int16) = sys.winches[idx].f_coulomb
@register_symbolic get_winch_f_coulomb(sys::SystemStructure, idx::Int16)
get_winch_c_vf(sys::SystemStructure, idx::Int16) = sys.winches[idx].c_vf
@register_symbolic get_winch_c_vf(sys::SystemStructure, idx::Int16)
get_winch_inertia_total(sys::SystemStructure, idx::Int16) = sys.winches[idx].inertia_total
@register_symbolic get_winch_inertia_total(sys::SystemStructure, idx::Int16)

get_set_mass(set::Settings) = set.mass
@register_symbolic get_set_mass(set::Settings)
get_rho_tether(set::Settings) = set.rho_tether
Expand Down Expand Up @@ -632,6 +625,12 @@ function force_eqs!(s, system, psys, pset, eqs, defaults, guesses;
winch_force(t)[eachindex(winches)]
winch_force_vec(t)[1:3, eachindex(winches)]
brake(t)[eachindex(winches)]
# New symbolic variables for winch dynamics
ω_motor(t)[eachindex(winches)]
tau_friction(t)[eachindex(winches)]
tau_motor(t)[eachindex(winches)]
tau_total(t)[eachindex(winches)]
α_motor(t)[eachindex(winches)]
end
for winch in winches
F = zeros(Num, 3)
Expand All @@ -641,6 +640,18 @@ function force_eqs!(s, system, psys, pset, eqs, defaults, guesses;
error("Point number $winch_idx does not exist.")
F .+= point_force[:, winch_idx]
end

gear_ratio = get_winch_gear_ratio(psys, winch.idx)
drum_radius = get_winch_drum_radius(psys, winch.idx)
f_coulomb = get_winch_f_coulomb(psys, winch.idx)
c_vf = get_winch_c_vf(psys, winch.idx)
inertia_total = get_winch_inertia_total(psys, winch.idx)

function smooth_sign(x)
EPSILON = 6
x / sqrt(x * x + EPSILON * EPSILON)
end

eqs = [
eqs
brake[winch.idx] ~ get_brake(psys, winch.idx)
Expand All @@ -649,11 +660,18 @@ function force_eqs!(s, system, psys, pset, eqs, defaults, guesses;
D(tether_vel[winch.idx]) ~ ifelse(brake[winch.idx]==true,
0, tether_acc[winch.idx])

tether_acc[winch.idx] ~ calc_moment_acc( # TODO: moment and speed control
winch.model, tether_vel[winch.idx],
winch_force[winch.idx],
set_values[winch.idx]
)
# Symbolic winch dynamics equations
ω_motor[winch.idx] ~ gear_ratio / drum_radius * tether_vel[winch.idx]
tau_friction[winch.idx] ~ smooth_sign(ω_motor[winch.idx]) *
f_coulomb * drum_radius / gear_ratio +
c_vf * ω_motor[winch.idx] * drum_radius^2 / gear_ratio^2
tau_motor[winch.idx] ~ set_values[winch.idx] # set_value is the motor torque
tau_total[winch.idx] ~ tau_motor[winch.idx] +
drum_radius / gear_ratio * winch_force[winch.idx] -
tau_friction[winch.idx]
α_motor[winch.idx] ~ tau_total[winch.idx] / inertia_total
tether_acc[winch.idx] ~ drum_radius / gear_ratio * α_motor[winch.idx]

winch_force_vec[:, winch.idx] ~ F
winch_force[winch.idx] ~ norm(winch_force_vec[:, winch.idx])
]
Expand Down
6 changes: 3 additions & 3 deletions src/model_management.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ function generate_prob_getters(sys_struct, sys)
if length(groups) > 0; get_group_state = getu(sys, c.([sys.twist_angle, sys.twist_ω, sys.group_tether_force, sys.group_tether_moment, sys.group_aero_moment])); end
if length(pulleys) > 0; get_pulley_state = getu(sys, c.([sys.pulley_len, sys.pulley_vel])); end
if length(winches) > 0
get_winch_state = getu(sys, c.([sys.tether_len, sys.tether_vel, sys.set_values, sys.winch_force_vec]))
get_winch_state = getu(sys, c.([sys.tether_len, sys.tether_vel, sys.set_values,
sys.winch_force_vec, sys.tau_friction]))
set_set_values = setp(sys, sys.set_values)
get_set_values = getp(sys, sys.set_values)
end
Expand Down Expand Up @@ -509,8 +510,7 @@ function get_sys_struct_hash(sys_struct::SystemStructure)
push!(data_parts, ("tether", tether.idx, tether.segment_idxs))
end
for winch in winches
model_type = winch.model isa TorqueControlledMachine
push!(data_parts, ("winch", winch.idx, model_type, winch.tether_idxs))
push!(data_parts, ("winch", winch.idx, winch.tether_idxs))
end
for wing in wings
push!(data_parts, ("wing", wing.idx, wing.group_idxs))
Expand Down
24 changes: 12 additions & 12 deletions src/predefined_structures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ function create_4_attach_ram_sys_struct(set::Settings)
STEERING_LINE, dynamics_type; z)

winches = [
Winch(1, TorqueControlledMachine(set), [left_power_idx, right_power_idx])
Winch(2, TorqueControlledMachine(set), [left_steering_idx])
Winch(3, TorqueControlledMachine(set), [right_steering_idx])
Winch(1, set, [left_power_idx, right_power_idx])
Winch(2, set, [left_steering_idx])
Winch(3, set, [right_steering_idx])
]

vsm_aero = BodyAerodynamics([vsm_wing])
Expand Down Expand Up @@ -299,9 +299,9 @@ function create_ram_sys_struct(set::Settings)
STEERING_LINE, dynamics_type; z)

winches = [
Winch(1, TorqueControlledMachine(set), [left_power_idx, right_power_idx])
Winch(2, TorqueControlledMachine(set), [left_steering_idx])
Winch(3, TorqueControlledMachine(set), [right_steering_idx])
Winch(1, set, [left_power_idx, right_power_idx])
Winch(2, set, [left_steering_idx])
Winch(3, set, [right_steering_idx])
]

vsm_aero = BodyAerodynamics([vsm_wing])
Expand Down Expand Up @@ -361,9 +361,9 @@ function create_tether_sys_struct(set::Settings;
axial_stiffness=axial_stiffness[4], axial_damping=axial_damping[4])

winches = [
Winch(1, TorqueControlledMachine(set), [left_power_idx, right_power_idx]; brake=true)
Winch(2, TorqueControlledMachine(set), [left_steering_idx]; brake=true)
Winch(3, TorqueControlledMachine(set), [right_steering_idx]; brake=true)
Winch(1, set, [left_power_idx, right_power_idx]; brake=true)
Winch(2, set, [left_steering_idx]; brake=true)
Winch(3, set, [right_steering_idx]; brake=true)
]

transforms = [Transform(1, deg2rad(set.elevation), deg2rad(set.azimuth), deg2rad(set.heading);
Expand Down Expand Up @@ -433,9 +433,9 @@ function create_simple_ram_sys_struct(set::Settings;
Tether(4, [4], 8)
]
winches = [
Winch(1, TorqueControlledMachine(set), [1,2])
Winch(2, TorqueControlledMachine(set), [3])
Winch(3, TorqueControlledMachine(set), [4])
Winch(1, set, [1,2])
Winch(2, set, [3])
Winch(3, set, [4])
]
vsm_aero = BodyAerodynamics([vsm_wing])
vsm_solver = Solver(vsm_aero; solver_type=NONLIN, atol=2e-8, rtol=2e-8)
Expand Down
9 changes: 6 additions & 3 deletions src/simulate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ function sim!(
total_time=10.0,
vsm_interval=3,
prn=true,
lin_model::Union{Nothing, <:NamedTuple, StateSpace}=nothing
lin_model::Union{Nothing, <:NamedTuple, StateSpace}=nothing,
torque_damp=0.9,
)
steps = Int(round(total_time / dt))
sys_struct = sam.sys_struct
Expand All @@ -55,12 +56,14 @@ function sim!(
set_torques = similar(set_values)
y_op = sam.simple_lin_model.get_y(sam.integrator)

steady_torque = calc_steady_torque(sam)

# --- Nonlinear Simulation Loop ---
elapsed = @elapsed for step in 1:steps
t = (step-1) * dt

set_torques[step, :] = -sam.set.drum_radius .* [norm(winch.force) for winch in sys_struct.winches]
set_torques[step, :] .+= set_values[step, :]
steady_torque = torque_damp * steady_torque + (1-torque_damp) * calc_steady_torque(sam)
set_torques[step, :] = steady_torque .+ set_values[step, :]

try
step_time += @elapsed next_step!(sam;
Expand Down
15 changes: 14 additions & 1 deletion src/symbolic_awe_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -481,12 +481,13 @@ function update_sys_struct!(prob::ProbWithAttributes,
end
end
if length(winches) > 0
tether_len, tether_vel, set_value, winch_force_vec = prob.get_winch_state(integ)
tether_len, tether_vel, set_value, winch_force_vec, friction = prob.get_winch_state(integ)
for winch in winches
winch.tether_len = tether_len[winch.idx]
winch.tether_vel = tether_vel[winch.idx]
winch.set_value = set_value[winch.idx]
winch.force .= winch_force_vec[:, winch.idx]
winch.friction = friction[winch.idx]
end
end
if length(tethers) > 0
Expand Down Expand Up @@ -545,6 +546,18 @@ function get_model_name(set::Settings; precompile=false)
return "model_$(ver)_$(set.physical_model)_$(dynamics_type)_$(set.segments)_seg.bin$suffix"
end

"""
calc_steady_torque(sam::SymbolicAWEModel)

Calculates the torque for each winch that results in zero acceleration (steady state).
"""
function calc_steady_torque(sam::SymbolicAWEModel)
sys_struct = sam.sys_struct
torques = -[winch.drum_radius / winch.gear_ratio * norm(winch.force) +
winch.friction for winch in sys_struct.winches]
return torques
end

"""
calc_aoa(s::SymbolicAWEModel)

Expand Down
Loading
Loading