Custom SystemStructure and SymbolicAWESystem

A custom SystemStructure can be used to create models of kite power systems of almost any configuration.

  • custom amount of tethers
  • custom bridle configurations
  • quasi-static or dynamic point masses
  • different amounts of stiffness, damping and diameter on different tether segments

Precondition

First, following the Home section up to the installation of the examples. To start Julia, either use julia --project=., or ./bin/run_julia. Make sure that at least SymbolicAWEModels version 0.2 is installed by typing ]status in the Julia REPL. If necessary, update SymbolicAWEModels by typing ]up.

Creating a simple tether

We start by loading the necessary packages and defining settings and parameters.

using SymbolicAWEModels, VortexStepMethod, ControlPlots

set = Settings("system.yaml")
set.segments = 20
set.l_tether = 50.0
dynamics_type = DYNAMIC

Then, we define vectors of the system structure types we are going to use. For this simple example we only need points and segments.

points = Point[]
segments = Segment[]

points = push!(points, Point(1, zeros(3), STATIC; wing_idx=0))

The first point we add is a static point. There are four different DynamicsTypes to choose from: STATIC, QUASI_STATIC, DYNAMIC and WING. STATIC just means that the point doesn't move. DYNAMIC is a point modeled with acceleration, while QUASI_STATIC constrains this acceleration to be zero at all times. A WING point is connected to a wing body.

Now we can add DYNAMIC points and connect them to each other with segments. BRIDLE segments don't need to have a tether, because they have a constant unstretched length.

segment_idxs = Int[]
for i in 1:set.segments
    global points, segments
    point_idx = i+1
    pos = [0.0, 0.0, i * set.l_tether / set.segments]
    if i < set.segments
        push!(points, Point(point_idx, pos, dynamics_type; wing_idx=0))
    else
        push!(points, Point(point_idx, pos, dynamics_type; mass=1.0, wing_idx=0))
    end
    segment_idx = i
    push!(segments, Segment(segment_idx, set, (point_idx-1, point_idx), BRIDLE))
    push!(segment_idxs, segment_idx)
end

In order to describe the initial orientation of the structure, we define a Transform(idx, elevation, azimuth, heading) with an elevation (-80 degrees), azimuth and heading, and a base position [0.0, 0.0, 50.0].

transforms = [Transform(1, deg2rad(-80), 0.0, 0.0; 
              base_pos = [0.0, 0.0, 50.0], base_point_idx=points[1].idx,
              rot_point_idx=points[end].idx)]

From the points, segments and transform we create a SystemStructure(name, set), which can be plotted in 2d to quickly investigate if the model is correct.

sys_struct = SystemStructure("tether", set; points, segments, transforms)
plot(sys_struct, 0.0)

SystemStructure visualization

If the system looks good, we can easily model it, by first creating a SymbolicAWEModel, initializing it and stepping through time.

sam = SymbolicAWEModel(set, sys_struct)

init!(sam; remake=false)
for i in 1:80
    plot(sam, i/set.sample_freq)
    next_step!(sam)
end

Tether during simulation

Using a winch and a tether

Let's try to adjust the length of the tether in the last example. To do this we first need to create a set of segments with a common changing l0, called a Tether.

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 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.

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

The 2d plot of the SystemStructure 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.

sys_struct = SystemStructure("winch", set; points, segments, tethers, winches, transforms)
@show set.v_wind
sam = SymbolicAWEModel(set, sys_struct)
init!(sam; remake=false)
ss = SysState(sam)

for i in 1:80
    plot(sam, (i-1)/set.sample_freq)
    next_step!(sam; set_values=[-20.0])
    update_sys_state!(ss, sam)
end
@show ss.l_tether[1]

Using a pulley

First, we need to update some settings. l_tether is specified such that the plot window is zoomed in correctly.

using SymbolicAWEModels, VortexStepMethod, ControlPlots

set = se("system.yaml")
set.v_wind = 10.0
set.l_tether = 5.0
set.abs_tol = 1e-4
set.rel_tol = 1e-4
dynamics_type = DYNAMIC

Now we create points and segments in a similar manner as in the last example. The mass keyword can be used to specify the mass of the point itself. When mass=0.0 the mass of the point just consists of the tether/segment mass.

points = Point[]
segments = Segment[]
pulleys = Pulley[]

push!(points, Point(1, [0.0, 0.0, 2.0], STATIC))
push!(points, Point(2, [2.0, 0.0, 2.0], STATIC))
push!(points, Point(3, [0.1, 0.0, 1.0], DYNAMIC))
push!(points, Point(4, [0.1, 0.0, 0.0], DYNAMIC; mass=0.1))

push!(segments, Segment(1, set, (3,1), BRIDLE))
push!(segments, Segment(2, set, (3,2), BRIDLE))
push!(segments, Segment(3, set, (3,4), BRIDLE))

Pulleys can be modeled when three or more Segments are connected to a common Point. When creating a pulley, only two segments are specified: these are the segments of the tether moving through the pulley.

push!(pulleys, Pulley(1, (1,2), DYNAMIC))

We can then use a Transform to describe the orientation of the initial system.

transforms = [Transform(1, -deg2rad(0.0), 0.0, 0.0; base_pos=[1.0, 0.0, 4.0], base_point_idx=1, rot_point_idx=2)]
sys_struct = SymbolicAWEModels.SystemStructure("pulley", set; points, segments, pulleys, transforms)
plot(sys_struct, 0.0; zoom=false, l_tether=set.l_tether)

If the plot of the SystemStructure looks good, we can continue by creating a SymbolicAWEModel and simulating through time.

sam = SymbolicAWEModel(set, sys_struct)
init!(sam; remake=false)
for i in 1:100
    plot(sam, i/set.sample_freq; zoom=false)
    next_step!(sam)
end