Jump to content

kOS Scriptable Autopilot System 0.9


KevinLaity

Recommended Posts

There is a bug with KOS where its expression system can't properly convert different types of number to each other and work seamlessly like they should. Most programming languages either:

A - Have no trouble automatically converting between integers, long integers, floats, and doubles so you don't need to explicitly convert them, or

B - Allow you to explicitly convert them manually.

But with KOS it's sort of the worst of both worlds. You are denied the ability to manually convert numeric types yourself, AND the system won't do it correctly for you. So when there's a type mismatch, you're out of luck and can't do anything about it. At the moment this is being handled by trying to make all the numbers returned everywhere in the system be the same type of number - a double-precision floating point number - so the conversions never have to happen. But there are a few places where that hasn't happened and one of those places is TARGET:DISTANCE.

This is what leads to that rather odd looking error message "Can't compare number to number", which sounds utterly ridiculous until you realize that what's going on is that it's actually complaining about "Can't compare float to double" or "Can't compare integer to double" or something like that, but the true names of the types are being masked when the message is printed and all numeric types are getting called just "number" in the error message.

Link to comment
Share on other sites

Method B is by using a PID controller type algorithm. This is a considerably more difficult and I haven't yet figured it out.

What type controller are you trying to write? Is it a P controller, PD, PI or PID? Also, I do not think a gain should have to be negative :)

So to summarize my position:

Fix the SOI problem first and restore negative apaopsis later. - I'm fine with this.

Fix the SOI problem and restore negative apoapsis in the same reliease - I'm fine with this too.

Fix the SOI problem later and restore negative apoapsis now - I'm not fine with this for the reasons stated above. The SOI problem *requires* getting information from KOS as there's no alternative way to calculate escape mathematically right now. There *is no* workaround right now. There are, however, workarounds to not being able to see a negative "apoapsis".

I think we are largely in agreement. Both should be possible. I feel that it would probably be done best by leaving what is now as is and adding a way of detecting the arbitrairy Squad SOI value.

What now ?

I don't think you can say until 0. It would have to be something like until var1 < 0.

Link to comment
Share on other sites

I think we are largely in agreement. Both should be possible. I feel that it would probably be done best by leaving what is now as is and adding a way of detecting the arbitrairy Squad SOI value.

Where we disagree is in the prority of the two problems. The inability to detect whether or not you are escaping by reading any of the KOS values is a far bigger problem than having to use a more complex expression to work out your eccentricity.

I don't think you can say until 0. It would have to be something like until var1 < 0.

It works. One of the few places where mismatching numeric types *does* seem to work seamlessly in KOS is in the ability to use numbers as booleans.

Link to comment
Share on other sites

Where we disagree is in the prority of the two problems. The inability to detect whether or not you are escaping by reading any of the KOS values is a far bigger problem than having to use a more complex expression to work out your eccentricity.

I do not think it is a matter of one problem being bigger than the other. I think that having an arbitrary SOI warrants having a proper way of detecting it anyway. But maybe this is getting all a bit academic :)

It works. One of the few places where mismatching numeric types *does* seem to work seamlessly in KOS is in the ability to use numbers as booleans.

Yes, I just tested it and came back to correct myself. I am not sure how the until condition could be fulfilled though.

Link to comment
Share on other sites

I do not think it is a matter of one problem being bigger than the other. I think that having an arbitrary SOI warrants having a proper way of detecting it anyway. But maybe this is getting all a bit academic :)

Yes, I just tested it and came back to correct myself. I am not sure how the until condition could be fulfilled though.

It's common practice in programming languages that have a loop escaper such as "break" (which KOS does have) to make the loop header claim the loop will loop forever, but then have the condition that ends the loop be checked for somewhere down in the loop with a thing like "if such-and-such then break."

Its a useful thing to do in scenarios where you want to end the loop somewhere other than at the bottom of it, so you don't want to have to wait for the bottom and then detect that it's time to quit the loop, or when it's a loop in which the exit condition is sufficiently complicated that it doesn't make a nice terse simple expression, so you don't want to put it up in the header, or when it's a loop for which it's *expected* to just keep on looping for most of the duration of the program and the escape from the loop is truly an exception case, such as when running a server that's expected to stay alive as long as the computer is running.

Link to comment
Share on other sites

It's common practice in programming languages that have a loop escaper such as "break" (which KOS does have) to make the loop header claim the loop will loop forever, but then have the condition that ends the loop be checked for somewhere down in the loop with a thing like "if such-and-such then break."

I figured that was a potential use for it, but I was not aware this was actually a common practice :) I used to implement such a loop by doing something like


until alt:radar < alt:radar {}.

but this is a cleaner way to do it. So it needs to be 0 specifically, as any other number breaks the loop right away?

Link to comment
Share on other sites

I figured that was a potential use for it, but I was not aware this was actually a common practice :) I used to implement such a loop by doing something like


until alt:radar < alt:radar {}.

I'm not sure that would have always worked, because I think you'd be sampling alt:radar at two different instants in time so they wouldn't necessarily be identical. (While deciphering your expression, KOS gets alt:radar once, then it encounters the second alt:radar and re-queries it. You may have moved a tiny amount during that interval.)

but this is a cleaner way to do it. So it needs to be 0 specifically, as any other number breaks the loop right away?

When using numbers as booleans, the logic is typically this:

If the number is zero: it's interpreted as boolean "false".

If the number is ANYTHING ELSE that is nonzero: It's interpreted as boolean "true".

The idea is that this makes "or" analogous to addition and "and" analogous to multiplication and in some old primitive computers they

actually were implemented that way:

false or false = false can be implemented as zero plus zero = zero.

true or true = true can be implemented as nonzero plus nonzero = nonzero. (as long as the nonzeros are not equal in size and opposite in sign, so usually the numbers are taken as unsigned to avoid that).

false or true = true can be implemented as zero plus nonzero = nonzero.

false and false = false can be implemented as zero times zero = zero.

true and true = true can be implemented as nonzero times nonzero = nonzero.

false and true = false can be implemented as nonzero times zero = zero.

And standard order of operations that makes multiplication come before addition is analogous to ANDs happening before ORs.

While this matches exactly with the boolean algebra math invented by Boole (from whom it gets its name), it's often not as useful as inverting the logic because boolean logic provides multiple values that are true, but only one value that is false, and what you often want is the other way around - a way to encode multiple types of error, but only one encoding for success.) So often operating system status codes are encoded the other way around, even though it's the opposite of the boolean standard. They have zero meaning "success" and then multiple types of non-success so you can read what type of failure it was. This leads to confusing looking code sometimes when programmers say things like "if not some_system_call() then ...." When what that really means the opposite of what it looks like it means.

Link to comment
Share on other sites

I'm not sure that would have always worked, because I think you'd be sampling alt:radar at two different instants in time so they wouldn't necessarily be identical. (While deciphering your expression, KOS gets alt:radar once, then it encounters the second alt:radar and re-queries it. You may have moved a tiny amount during that interval.)

Until now it has always worked, in both directions, so I guess the commands are either executed at the same time or in such a short timespan that it effectively means the same. Of course, the method would be just as viable comparing any other variable to itself.

When using numbers as booleans, the logic is typically this:

If the number is zero: it's interpreted as boolean "false".

If the number is ANYTHING ELSE that is nonzero: It's interpreted as boolean "true".

I meant that until false and until true should both be conditions that are never met, as there is nothing specified to compare the condition to. kOS seems to assume that comparing to nothing means comparing with some built in boolean that it not 0. Or am I confused here?

[explanation]

That is some great background info. It seems very much related to the basic operation of logic systems (and thus computers) where a series of simple comparisons is used to provide answers to more complex questions. I assume you are familiar with these things, but I would really suggest that anyone else who is interested in computers reads up on this. It is very interesting material and so very fundamental to modern life. There are some great books out there that cover this in a very comprehensive yet understandable way.

Edited by Camacha
Link to comment
Share on other sites

Apologies if this has been covered elsewhere, but I couldn't find a solution on the wiki/youtube/anywhere!

I've pretty much just downloaded and installed the mod, and I spent a few hours designing a program with a gradual gravity turn to take me into orbit. I set up the file by writing 'edit Launch.' into the console window. I saved it using 'F5' after I'd finished writing it. Of course, there were a few bugs that led to the rocket crash-landing, but I recovered the remains of the vehicle with the autopilot module. When I then tried to launch the same rocket on a different flight, "edit Launch." opened a blank, new program.

How do I open programs I've already written and saved?

Link to comment
Share on other sites

Apologies if this has been covered elsewhere, but I couldn't find a solution on the wiki/youtube/anywhere!

I've pretty much just downloaded and installed the mod, and I spent a few hours designing a program with a gradual gravity turn to take me into orbit. I set up the file by writing 'edit Launch.' into the console window. I saved it using 'F5' after I'd finished writing it. Of course, there were a few bugs that led to the rocket crash-landing, but I recovered the remains of the vehicle with the autopilot module. When I then tried to launch the same rocket on a different flight, "edit Launch." opened a blank, new program.

How do I open programs I've already written and saved?

You have to save programs to the archive. Once you've written a program and exit the editor you should use


copy launch to archive.

Then when you reload the rocket


copy launch from archive.

Then you can use a text editor of your choice to edit the program(s) outside of KSP/kOS. The archive folder can be found at Kerbal Space Program\Plugins\PluginData\Archive.

Link to comment
Share on other sites

What type controller are you trying to write? Is it a P controller, PD, PI or PID? Also, I do not think a gain should have to be negative :)

Hey Camacha, thanks for replying again.

I'm trying to write a full PID just to learn them. However I am doing it one step at a time, so first a P, then adding in the I and finally the D. I'm trying to get each stage to work properly before moving onto the next. That way I can make sure I fully understand what I'm doing and then I'll be able to write them for whatever I wish (in theory).

I've wrote, what I think is the p section of the PID. I have an if statement for above my setpoint and an if statement for below. The algorithm only works when I set Ki to a negative number. It seems to be slightly tempermental though. I can run the program and it'll work in one way, then run the exact same program again and it'll work slightly differently. I've made some little videos that I've posted below to show exactly what I mean.

That's the same program. I ran it. Broke into it, then just ran it again.

Link to comment
Share on other sites

How do I open programs I've already written and saved?

Well, the bad news is you have not saved it. Not the way you wanted it to be saved, at least. Pressing F5 saves it locally. That means that it is saved to your kOS unit. If that units gets destroyed or reverted to a state before saving your program, the program is gone as far as that unit is concerned and thus for as far KSP is concerned. If you would launch a vehicle with a second kOS unit, it would not have access to your program, as it would only be saved to the first unit. A way to avoid your program being lost upon revert to launch would be to quicksave just before launch. If you reload your quicksave instead of reverting to launch, your program will be there as KSP reverted to your vehicle state just before launch and with the program in that kOS unit.

If you want to permanently save a program you have to copy it to archive. That means writing it and issuing the command copy programname to 0. The program will then be saved as a .txt to /Plugins/PluginData/Archive and can be accessed through the archive in any career or sandbox you may play. Any kOS unit within antenna range will be able to access that program. If you build a new vehicle and put it on the launchpad, your old program will still be accessible to it.

I would advise to use the kOS IDE found here, as that makes editing a lot easier. It works through editing the .txt files in the archive, which you can then access in KSP. It also means that all work is saved to archive, permanently saving it.

More information can be found searching for the difference between saving to archive and saving locally.

Link to comment
Share on other sites

I would advise to use the kOS IDE found here, as that makes editing a lot easier. It works through editing the .txt files in the archive, which you can then access in KSP. It also means that all work is saved to archive, permanently saving it..

I second the using kOS IDE idea. It's a great tool and allows things like copy and paste as well as colouring different bits of your script making it much more readable.

I generally do all my programming in the archive in stead of the local volume using kOS IDE. As soon as you hit save in kOS IDE that modified script is available in game. So for example you change a parameter in kOS IDE and hit save. If you're already in the archive you simply type "run <progname> and kOS will run the new code.

Link to comment
Share on other sites

I'm trying to write a full PID just to learn them. However I am doing it one step at a time, so first a P, then adding in the I and finally the D. I'm trying to get each stage to work properly before moving onto the next. That way I can make sure I fully understand what I'm doing and then I'll be able to write them for whatever I wish (in theory).

Yes, that should be a good approach to fully understand what is going on and what you are doing :)

I've wrote, what I think is the p section of the PID. I have an if statement for above my setpoint and an if statement for below. The algorithm only works when I set Ki to a negative number. It seems to be slightly tempermental though. I can run the program and it'll work in one way, then run the exact same program again and it'll work slightly differently. I've made some little videos that I've posted below to show exactly what I mean.

Thanks for the movies, that makes things a lot clearer. Before I get into details: why do you have an I/integral gain (Ki) when you are trying to make only a P/proportional (Kp) controller?

Link to comment
Share on other sites

I second the using kOS IDE idea. It's a great tool and allows things like copy and paste as well as colouring different bits of your script making it much more readable.

And I third using kOS IDE. Though, if there were syntax highlighting for KerboScript in Notepad++ I would use that instead.

Link to comment
Share on other sites

Before I get into details: why do you have an I/integral gain (Ki) when you are trying to make only a P/proportional (Kp) controller?

Because I'm a doughnut. I simply wrote the wrong letter in there. :P

Edit: I've changed the "i" to a "p as it should be, thanks.

Edited by Kass
Link to comment
Share on other sites

Edit: I've changed the "i" to a "p as it should be, thanks.

Your post still says i :) Not that it should really matter for operation, as long as you do not have the terms confused in your script.

Commenting on your previous post: I am quite sure you can write a PID controller without if statements, as I made a full PID without one. That, however, does not mean that one with if statements is wrong. It might work differently. Without looking at your code it is hard to say, but I can imagine that the different if statements cause the script to have two (or more) states. That might explain behaviour that changes from time to time, as it changes behaviour when it switches between the states.

Another possibility is that your tuning is off. I have seen haviour like in the first movie clip in a PID-controller that was not tuned properly.

Also be aware that variables persist longer and in different situations than you might think, so variables might be 'left over' from last time you ran the script. To be sure, you can set the variables at the start of your script to a default or placeholder value, so no erroneous values are used for calculations.

I am temporarily unable to watch Youtube clips in full resolution, so I am unable to comment on the values in your movie clips.

Edited by Camacha
Link to comment
Share on other sites

Ahh, that explains why it only happens the very first time I run the script. Not sure how to correct that though.

Edit: no they aren't confused in the script. I've only written the P part so far.

I only uploaded half res videos, just to show what was happening. The numbers I'm generating are probably redundant to someone who knows what they are doing. Lots are just debugging points to indicate what part of the script is being used at any time.

Am I right in thinking that a properly tuned P controller functions as in my second video? Probably presumptuous of me but I'll pm you my script unless it'd be better to post it here?

Thanks for all your help so far Camacha

Link to comment
Share on other sites

Ahh, that explains why it only happens the very first time I run the script. Not sure how to correct that though.

Until now I have worked with a deliver and execute method: one script delivers the vehicle to the height necessary at zero speed, then the hover script takes over for hovering. That way the error is not huge right when the hoverscript gets started. One thing to remember about PID-controllers or variants is that they have no garantee for stability, so when you jolt the system too much, unrecoverable instability might arise. I have been looking into foregoing the delivering script and using strictly the hover script for the initial change in altitude, but I have not had the time to experiment with that enough.

Possible solutions to your problem might be injecting the proper values yourself - so getting the internal variables when the script is doing what you want, and making sure that those variables are used the next time you start your script. Another solution might be to implement some kind of safe state, in which the script does not have control yet but has some time to find its bearings, before releasing control to the script.

I must say I have not experimented enough with this yet to give conclusive answers.

Am I right in thinking that a properly tuned P controller functions as in my second video?

Yes, that looks good and is what a properly set up controller should do.

Edited by Camacha
Link to comment
Share on other sites

A simplistic way that works is:


Set PM to 5.2915793*(10^22). // Planet Mass (Stock Kerbin)
Set PR to 600000. // Planet Radius (Stock Kerbin)

set e to 2.71828. // constant 'e'
set G to 6.67384*(10^-11). // Gravitational Constant

until altitude > 35000 {
Set m to mass.
Set r to PR + altitude.
set FG to m*G*PM*(r^-2). // Gravity Force

set p to (e^((0-altitude)/5000)).
set rho to 1.2230948554874*p.
set v to velocity:surface:mag.
set FD to rho*v*v*0.0008*m. //Drag Force (without FAR!!)

if FD > FG and t>0 {set t to t-0.05.}.
if FD < FG and t<1 {set t to t+0.05.}.
}.

Thank you very much!

Is there a way to know the prograde/retrograde vector(relative to the surface) of the ship?

Link to comment
Share on other sites

Is there a way to know the prograde/retrograde vector(relative to the surface) of the ship?

You can actually use prograde.

lock steering to prograde.

If you need the relative position to the surface you will need to apply some math using prograde and up.

Link to comment
Share on other sites

Is there a way to fire specific RCS thruster?

I need that to let my "grasshopper" like craft(it just go up for a while, hover and land). Currently, my code doesn't allow my craft to travel sideway while hovering(how can I land without that? simple: I just don't hover long enough to get out of the launchpad).

Link to comment
Share on other sites

I know it's impolite to repost by my post was burried by 3 pages of a fast paced discussion about variable conversion...

Which by the way I have not seen anyone pointing out that setting things to a variable seems to be what does the number conversion so:

lock td to target:distance.

wait until td < 150.

Works but:

wait until target:distance < 150.

Gives the notorious "can't compare number to number" error.

Also note that you must use lock instead of set in a wait until loop as set will only get the value once.

Anyway here is my old post, if someone could help or at least confirm this behavior that would be great:

So I have a rover that follows another one, but whenever the rovers are not on flat land they veer wildly off course... (seems the angle of the rover running the script is the determining factor)

My steering code looks like this:

lock ts to vessel("battery").

lock wheelsteering to ts.

Battery is the name of the rover I'm following. (ultimately I would to get them to dock at some point but I can't even try if they go in random directions when on a hill because wheelsteering told them too...)

Also how do you get the position of an object? I was going to try to specify the of the location of the other rover directly and hope that fixes it.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...