Closed Dunbaratu closed 8 years ago
I think that I'd lean towards making sure the PIDLoop structure and the tutorial are similar. I'm a firm believer in the idea of "you need to know what the little black box is doing." In that vein, an explaination of how to create a PID loop will help users know what to expect. Then maybe a short explaination at the end showing how the built in loop can replace the kerboscript would help. That way they have the knowledge to extend the system if they have a use case that the built in feature doesn't support.
I honestly have read so many PID tutorials in the last 4 months, that I'm not sure that I'm completely sure of the difference between our guide and my implementation. I can take a look at the document with fresh eyes (now that I've been out of that code for a month) and see what we can do to bring the two together.
I'm adding the link to the new pidloop to the top of this tutorial in my upcoming docs PR, but otherwise not editing it any further yet. Thus I'm not entirely going to close this issue, instead assigning it to @hvacengi to have a look at further after I finish, due to his comment above about having a look at it himself.
@hvacengi : I hope you don't mind that I assigned this to you - it was just to remind you of your earlier comment that you wanted to take a look at it. If you don't feel it needs any work, just go ahead and unassign yourself from it.
You dont need to know what the black box is doing. Here is something I just wrote, that could (after the pid values have been improved by someone more knowledgable than me) be incorporated into the tutorials.
After faaaar too long researching pid controllers, it appears that the values dont really matter much anyway. I noticed a far bigger difference in the max, min cutoff values, the 'P' value and their ratio to the divisor that i used to set the ship control to - something easily accomplished otherwise with the sin() function. Supposedly the pid loop autocorrects errors, but as you can see, it did not, noticably.
Anyway, if its helpful, split it apart (it already is really - roll, pitch, yaw) and tutorial the hell out of it! But note that with no previous documentation, this is probably not the best way. It is a use case for the pidloop function that mostly works, though, and would be interesting to newcomers.
(also, im currently looking through the buglist to see if the bug that there is no way to detect if TARGET is set has been reported yet. As you can see, it's a pretty big problem here. I will sift through the rest and make a report if necessary - I saw someone being asked to in a forum, but I'm not sure they did).
//
// Aircraft (Space Plane) Auto Pilot
//
// this is also a demonstration of how the new PIDLOOPs work
// athough not a very good one.
//
// Use notes:
//
// Doesnt work well with super highly maneuvable craft at high mach
// numbers, although those are usually fighter craft or rockets
// (or things that shouldnt fly at all). However, if it does start
// to wobble all over the place, just slow down! It'll get you
// there eventually.
// I guess its technically not a *space*plane autopilot, its just
// an aeroplane autopilot!
//
// vs hold is waaay off. the pidloops seem to not really care about
// how accurate they are (or maybe i just havent tuned them right),
// so both modes will be off, but alt hold mode will eventually
// stabilze +/- ~500 near the target. Real ap MUST stabilize
// within ~35m. For vs hold, just set it about 5-10 degrees above
// where you want it.
//
// It might be possible to make this work for rockets, but will
// require MUCH more precise loops. Let me know if you tweak them,
// I dont care how pid controllers work and dont want to know.
// I have a suspicion that doing so will destroy the large range
// of aircraft this works for though - at best you'll end up with
// one set of values that work for rockets, and another for planes.
//
// Its possible to put the control loops in when () loops, so you
// can also run some other script, but so far its not optimized
// much, and ive found that activating all 3 controllers causes
// instruction limit errors. The vnav one is particularly code
// heavy.
//
// missing autothrottle. I left the easiest for last :)
//
// might be possible to modify wing leveller to help rnav (bank into
// turns), but not currently a goal.
//
// default altitude hold setting.
set alt_setting to 5000.
clearscreen.
print "---Spaceplane Autopilot---".
print " ".
print "Press 1 to reduce vs hold, 2 to increase.".
print "Select a target, and press 8 to engage rnav.".
print "Press 9 to toggle vnav mode".
print " (off/vs hold/alt hold).".
print "Press 0 to engage wing leveler.".
SET rollpid TO PIDLOOP(0.9, 0.16, 0.006, -90, 90).
set rollpid:setpoint to 0.
lock roll_error to
// arccos(vdot(ship:up:vector:normalized,
// ship:facing:starvector:normalized)) -90.
// dang it, i always forget about vang and it may be faster
vang(ship:up:vector, ship:facing:starvector) -90.
set VNAVOFF to 0.
set VSHOLD to 2.
set ALTHOLD to 1.
set vnavmode to VNAVOFF.
set vs_target to 0.
SET vsholdpid TO PIDLOOP(0.9, 0.02, 0.02, -90, 100).
// something pretty messed up with these pid controllers.
// this one gets randomly between -10..10 of 0 (depending on
// speed/altitude), but no better.
set vsholdpid:setpoint to 0.
SET altholdpid TO PIDLOOP(0.9, 0.0, 0.0, -500, 500).
set altholdpid:setpoint to 0.
// fix up broken SHIP:HEADING.
set northpole to latlng(90, 0).
lock SHIPHDG to mod(360 -northpole:bearing, 360).
// great. no way to tell if a target is set.
// so we have to just clobber it and expect user to (re)set it.
set TARGET to "Sun".
print " ".
print "TARGET deselected, select target for rnav function.".
SET yawpid TO PIDLOOP(0.9, 0.2, 0.01, -25, 25).
set yawpid:setpoint to 0.
// possibly unnecessary now:
lock aoa to vang(ship:velocity:surface, ship:facing:upvector) -90.
lock pitch_ang to 90 -vang(ship:up:forevector,
ship:facing:forevector).
function simple_eta {
parameter dest.
set Vn to vdot(ship:velocity:surface,
heading(dest:heading, 0):vector:normalized).
if (Vn <= 0) return -1.
// FIXME: geoposition:distance is not great circle.
return dest:distance /Vn.
}
function print_wl {
if (AG10) {
print "wing level enabled (e:" +round(roll_error, 4)
+", c:" +round(SHIP:CONTROL:ROLL, 4)
+") " at (0,10).
} else {
print "wing level disabled"
+" " at (0,10).
}
}
function print_vnav {
if (vnavmode = VNAVOFF) {
print "vnav disabled"
+" " at (0,12).
} else if (vnavmode = ALTHOLD) {
print "altitude hold (" +round(alt_setting)
+"m) " at (0,12).
} else if (vnavmode = VSHOLD) {
print "vs hold (" +round(vs_target)
+"ms-1) " at (0,12).
}
}
function print_rnav {
if ((not AG8) or TARGET = BODY("Sun")) {
print "rnav disabled " at(0,14).
return.
}
print "rnav enabled, c: "
+round(SHIPHDG, 1) +", e: "
+round(TARGET:GEOPOSITION:BEARING, 1)
+" " at(0,14).
}
on AG10 {
if not AG10 {
set SHIP:CONTROL:ROLL to 0.
print "wing level disabled " at (0,10).
}
preserve.
}
on AG9 {
if (vnavmode = VNAVOFF) {
set vnavmode to VSHOLD.
// vsholdpid:reset().
} else if (vnavmode = VSHOLD) {
set vnavmode to ALTHOLD.
if (SHIP:ALTITUDE > 2000)
set alt_setting to SHIP:ALTITUDE.
else set alt_setting to 2000.
// altholdpid:reset().
// vsholdpid:reset().
} else if (vnavmode = ALTHOLD) {
set vnavmode to VNAVOFF.
set SHIP:CONTROL:PITCH to 0.
}
print_vnav().
preserve.
}
on AG1 {
set vs_target to vs_target -1.
preserve.
}
on AG2 {
set vs_target to vs_target +1.
preserve.
}
on AG3 {
if (vnavmode = VSHOLD) set vnavmode to ALTHOLD.
else set vnavmode to VSHOLD.
preserve.
}
//when (round(time:seconds) > last_time) and AG10 then {
function roll_loop {
if (not AG10) {
print_wl().
return.
}
local rc to rollpid:update(TIME:SECONDS*10, roll_error).
SET SHIP:CONTROL:ROLL to rc/90.0.
print_wl().
}
function vshold_loop {
local vs_t to vs_target.
if (vnavmode = VNAVOFF) {
print_vnav().
return.
}
if (vnavmode = ALTHOLD) {
set vs_t to altholdpid:update(TIME:SECONDS*10,
(SHIP:ALTITUDE -alt_setting)/5).
}
local vs_e to SHIP:VERTICALSPEED -vs_t.
local pc to vsholdpid:update(TIME:SECONDS*10, vs_e).
// divide by some sort of random number probably related
// to how much elevator pitch change causes the vs.
SET SHIP:CONTROL:PITCH to pc/60.
print_vnav().
// print "vs_target: " +round(vs_t, 4)
// +", vs_error: " +round(vs_e, 4)
// +" " at(0,13).
}
function rnav_loop {
if ((not AG8) or TARGET = BODY("Sun")) {
SET SHIP:CONTROL:YAW to 0.
print_rnav().
return.
}
local yaw_c to yawpid:update(TIME:SECONDS*10,
-TARGET:GEOPOSITION:BEARING).
SET SHIP:CONTROL:YAW to yaw_c /20.
print_rnav().
local eta to simple_eta(TARGET:GEOPOSITION).
local secs to eta -round(eta/60.0).
print " eta: " +round(eta/60) +":"
+round(secs, 1) +"s " at(0,15).
}
until 0 {
roll_loop().
vshold_loop().
rnav_loop().
// if (defined tgtgeo) print "ETA: " +round(simple_eta(tgtgeo))
// +"s " at(0, 26).
wait 0.1.
}
// -surge
You can tell github comments to make a literal code section with three backticks "```" on a line by itself, as the marker to start and stop the section. That would make the post a lot easier to read.
done, and now it doesnt resize horizontally in my browser. great.
Also, forgot to mention, but the lack of ability to tell if TARGET is set is a big problem here - im combing through the bug reports to see if it's been reported before, when I saw this and thought: ah-ha! I have this.
I submitted a pull request that might (at least partially) address this issue. I’m not 100% sure I got the .rst
syntax right, since I’ve never written RST before and the intra-doc linking syntax is fairly opaque, but hopefully it’s either okay or can be easily corrected.
All my change does is add a section right after the PID-loop section where the PID-loop script is redone with pidloop
and mention is made of the reduction in instructions, and therefore electrical drain, being a reason to consider using pidloop
. I left everything else in place, as I completely agree that showing the internals of a PID loop is useful.
I'll take a look at your adjustments when I have a little time. I've been reluctant to provide a one to one tutorial simply because the translation between one system and another is part of the learning process. But at the same time, I don't the transition to be complicated.
Thanks for sharing this!
There is a PIDLoop tutorial that runs people through making a PID loop themselves. It definitely should be edited to mention the new PIDLoop structure if nothing else. I'm still waffling on whether or not it should actually use it up front. It may be useful to let people see the inner thinking of how a PID loop behaves and have them derive one themselves before then using the built-in one.
Maybe it needs to be split into two paths of teaching. One for if you don't want to know the innards and just want to only know how to tweak the gains, and the other for if you want to actually know the innards under the hood. It might be cool if the end result of the detailed tutorial ends up being exactly the kerboscript code shown at the bottom of the PIDLoop page.