|
1 | 1 | module SimpleKiteControllers |
2 | 2 |
|
3 | | -# Write your package code here. |
| 3 | +using DiscretePIDs, Parameters |
4 | 4 |
|
| 5 | +@with_kw mutable struct ParkingControllerSettings @deftype Float64 |
| 6 | + dt |
| 7 | + # turn rate controller settings |
| 8 | + kp_tr=0.06 |
| 9 | + ki_tr=0.0012 |
| 10 | + # outer controller (heading/ course) settings |
| 11 | + kp=15 |
| 12 | + ki=0.5 |
| 13 | + # NDI block settings |
| 14 | + va_min = 5.0 # minimum apparent wind speed |
| 15 | + va_max = 100.0 # maximum apparent wind speed |
| 16 | + k_ds = 2.0 # influence of the depower settings on the steering sensitivity |
| 17 | + c1 = 0.048 # v9 kite model |
| 18 | + c2 = 0 # a value other than zero creates more problems than it solves |
5 | 19 | end |
| 20 | + |
| 21 | +mutable struct ParkingController |
| 22 | + pcs::ParkingControllerSettings |
| 23 | + pid_tr::DiscretePID |
| 24 | + pid_outer::DiscretePID |
| 25 | + last_heading::Float64 |
| 26 | + chi_set::Float64 |
| 27 | + last_ndi_gain::Float64 |
| 28 | +end |
| 29 | + |
| 30 | +function ParkingController(pcs::ParkingControllerSettings; last_heading = 0.0) |
| 31 | + Ts = pcs.dt |
| 32 | + pid_tr = DiscretePID(;K=pcs.kp_tr, Ti=pcs.kp_tr/ pcs.ki_tr, Ts) |
| 33 | + pid_outer = DiscretePID(;K=pcs.kp, Ti=pcs.kp/ pcs.ki, Ts) |
| 34 | + return ParkingController(pcs, pid_tr, pid_outer, last_heading, 0, 0) |
| 35 | +end |
| 36 | + |
| 37 | +""" |
| 38 | + linearize(pcs::ParkingControllerSettings, psi_dot, psi, elevation, v_app; ud_prime=0) |
| 39 | +
|
| 40 | +Nonlinear, dynamic inversion block (NDI) according to Eq. 6.4 and Eq. 6.12. |
| 41 | +
|
| 42 | +Parameters: |
| 43 | +- psi_dot: desired turn rate in radians per second |
| 44 | +- psi: heading in radians |
| 45 | +- elevation: elevation angle in radians |
| 46 | +- v_app: apparent wind speed in m/s |
| 47 | +- ud_prime: depower setting in the range of 0 to 1, 0 means fully powered, 1 means fully depowered |
| 48 | +""" |
| 49 | +function linearize(pc::ParkingController, psi_dot, psi, elevation, v_app; ud_prime=0) |
| 50 | + pcs = pc.pcs |
| 51 | + # Eq. 6.13: calculate va_hat |
| 52 | + va_hat = clamp(v_app, pcs.va_min, pcs.va_max) |
| 53 | + # Eq. 6.12: calculate the steering from the desired turn rate |
| 54 | + u_s = (1.0 + pcs.k_ds * ud_prime) / (pcs.c1 * va_hat) * (psi_dot - pcs.c2 / va_hat * sin(psi) * cos(elevation)) |
| 55 | + if abs(psi_dot) < 1e-6 |
| 56 | + ndi_gain = pc.last_ndi_gain |
| 57 | + else |
| 58 | + ndi_gain = clamp(u_s / psi_dot, -20, 20.0) |
| 59 | + end |
| 60 | + pc.last_ndi_gain = ndi_gain |
| 61 | + return u_s, ndi_gain |
| 62 | +end |
| 63 | + |
| 64 | +""" |
| 65 | + navigate(fpc, limit=50.0) |
| 66 | +
|
| 67 | +Calculate the desired flight direction chi_set using great circle navigation. |
| 68 | +Limit delta_beta to the value of the parameter limit (in degrees). |
| 69 | +""" |
| 70 | +function navigate(pc::ParkingController, azimuth, elevation; limit=50.0) |
| 71 | + phi_set = 0.0 # azimuth |
| 72 | + beta_set = deg2rad(77) # zenith |
| 73 | + beta = elevation |
| 74 | + phi = azimuth |
| 75 | + # println("beta: $(rad2deg(beta)), phi: $(rad2deg(phi))") |
| 76 | + r_limit = deg2rad(limit) |
| 77 | + if beta_set - beta > r_limit |
| 78 | + beta_set = beta + r_limit |
| 79 | + elseif beta_set - beta < -r_limit |
| 80 | + beta_set = beta - r_limit |
| 81 | + end |
| 82 | + y = sin(phi_set - phi) * cos(beta_set) |
| 83 | + x = cos(beta) * sin(beta_set) - sin(beta) * cos(beta_set) * cos(phi_set - phi) |
| 84 | + pc.chi_set = atan(y, x) |
| 85 | +end |
| 86 | + |
| 87 | +""" |
| 88 | + calc_steering(pc::ParkingController, heading; elevation=0.0, v_app=10.0, ud_prime=0.0) |
| 89 | +
|
| 90 | +Calculate rel_steering and ndi_gain from the actual heading, elevation, and apparent wind speed. |
| 91 | +
|
| 92 | +Parameters: |
| 93 | +- pc: parking controller |
| 94 | +- heading: actual heading in radians |
| 95 | +- elevation: elevation angle in radians |
| 96 | +- v_app: apparent wind speed in m/s |
| 97 | +- ud_prime: depower setting in the range of 0 to 1, 0 means fully powered, 1 means fully depowered |
| 98 | +
|
| 99 | +""" |
| 100 | +function calc_steering(pc::ParkingController, heading, chi_set; elevation=0.0, v_app=10.0, ud_prime=0.0) |
| 101 | + # calculate the desired turn rate |
| 102 | + heading = wrap2pi(heading) # a different wrap2pi function is needed that avoids any jumps |
| 103 | + psi_dot_set = pc.pid_outer(wrap2pi(chi_set), heading) |
| 104 | + psi_dot = (wrap2pi(heading - pc.last_heading)) / pc.pcs.dt |
| 105 | + pc.last_heading = heading |
| 106 | + psi_dot_in = pc.pid_tr(psi_dot_set, psi_dot) |
| 107 | + # linearize the NDI block |
| 108 | + u_s, ndi_gain = linearize(pc, psi_dot_in, heading, elevation, v_app; ud_prime) |
| 109 | + u_s, ndi_gain, psi_dot, psi_dot_set |
| 110 | +end |
| 111 | + |
| 112 | +end |
| 113 | + |
0 commit comments