Jump to content

[1.3] kOS Scriptable Autopilot System v1.1.3.0


erendrake

Recommended Posts

15 hours ago, kcs123 said:

I don't have github account, so it will take some time to create one. When I create account I would probably use it for other stuff than KSP, so I need to get more familiar with github first.

It doesn't sound like it's an issue from the thrust suffix anyways, so no need to make it on our account.  I should have also added the qualification that I could create the issue as well, my first choice is just to have the person who found the issue be attached to it so that you can get updates and we can ask you additional questions if needed.

15 hours ago, kcs123 said:

Regardless, that IsNull feature would be usefull for idiotproof scripts.

So far it has been a conscious decision to not include a form of "null", the rationale being that "null" can often complicate scripts for people just learning how to program.  Null checks wouldn't really help here anyways.  

What I do want to include is a way to determine if a scalar is "Valid".  Right now, kOS is set to a safety mode by default where it will not allow "NaN", or +/-"Infinity".  This is because KSP has had issues with crashing if you do things like setting the throttle to Infinity or NaN.  Since there currently isn't a way to test the validity yourself, you can't really turn off that config setting without risking the crash.  If you could check `number:isvalid` before using it, that would allow you to use these values for something useful.  The same issue from above applies though, this is a substantially more complicated issue for beginning programmers.

The line you showed looks to me like it's essentially a quadratic solver.  In my own landing script I found that some component of the equation can force the square root calculation negative.  I'm not sure if it's an issue with floating point precision, or an issue with some components being out of sequence with the physics frames.  But I simply wrapped it in a `max` function

return (-b + sqrt(max(b ^ 2 - 4 * a * c, 0))) / (2 * a).
[...]
return (-b - sqrt(max(b ^ 2 - 4 * a * c, 0))) / (2 * a).

 

19 hours ago, MAFman said:

I've corrected as many errors as I can, but it's still throwing the same exception.

See the instructions for finding your log file.  From a quick glance at your script, it can't be the same exact error message since it appears you now have defined "arrived".  Whatever variable it is that kOS says it doesn't recognize, search for that name in your script and make sure it is being set before you try to access it.

22 hours ago, Steven Mading said:

Here's where to find it depending on your OS:

  • Windows: (where-ever-your-KSP-is-Installed)\KSP_x64\KSP_x64_DATA\output_log.txt , if running in 64-bit mode.  (remove the "_x64" parts if you run in 32-bit mode).
  • Mac OSX: ~/Library/Logs/Unity/Player.log
  • Linux: ~/.config/unity3d/Squad/Kerbal Space Program/Player.log

 

22 hours ago, Lookerksy said:

When to release new kOS version, please?

We will release as soon as possible, but we are in the middle of wrapping up a rather large update already and are planning on releasing the fix for KSP 1.1.3 at the same time.

Edited by hvacengi
Link to comment
Share on other sites

1 hour ago, hvacengi said:

The line you showed looks to me like it's essentially a quadratic solver.  In my own landing script I found that some component of the equation can force the square root calculation negative.  I'm not sure if it's an issue with floating point precision, or an issue with some components being out of sequence with the physics frames.  But I simply wrapped it in a `max` function


return (-b + sqrt(max(b ^ 2 - 4 * a * c, 0))) / (2 * a).
[...]
return (-b - sqrt(max(b ^ 2 - 4 * a * c, 0))) / (2 * a).

 

Exactly, one part of solution would always give negative number and it is excluded from script. Other part should always return positive number, unless, as I found in a hard way, craft runs out of fuel and have zero available thrust. Who would think of that craft without fuel can't produce thrust ? :D Same thing would be if script is used on craft with TWR lower than 1.

Solved that issue by additional checking if part of equation under square root is positive number or not. Never thinked about soulution by using max function for that purpose. looks more elegant.

1 hour ago, hvacengi said:

What I do want to include is a way to determine if a scalar is "Valid".  Right now, kOS is set to a safety mode by default where it will not allow "NaN", or +/-"Infinity".  This is because KSP has had issues with crashing if you do things like setting the throttle to Infinity or NaN.

I quoted IsNull function from already raised issue/feature request on github. "Valid" might be a better term in this case, but, yes you got idea, purpose for this is to know in front if something is "NaN" and prevent to use that in inapropriate way that will cause CTD.

kOS already properly breaks if sense a NaN, to prevent issues in core of the game and gives exception error on screen. Can that part of code be used for new feature ?
Instead of writting error on terminal, it can give true/false value if something is NaN. Not necessary to give true null result. That could be sufficient

I know that is not something of biggest issues that you are working on and that might be a more complicated problems with this on the first sight, I just throwing ideas as I encounter some difficulties trough kOS usage.

Link to comment
Share on other sites

I'd love some way to prevent errors where I know they might crop up outside of my control, isNull or isValid etc. style checks would be a godsend. It just bothers me on a spiritual level to see a script crash due to something I could easily write a check for, but can't, and no way to refactor (alternately, "why do I have to refactor? This is inherent in every programming language..."). Then you just run the script again and hope that one possible issue doesn't occur and crash the script, or give up entirely on solving the problem.

Link to comment
Share on other sites

14 minutes ago, kcs123 said:

Instead of writting error on terminal, it can give true/false value if something is NaN. Not necessary to give true null result. That could be sufficient

The way I envision it is that config:safe would still control the way kOS responds to these values, but that the scalar would have additional suffixes:

set config:safe to false.
set x to 1 / 0. // does not throw an error
print x:isvalid. // prints false
print x:isinfinity. // prints true
print x:isnan. // prints false

That way existing functionality isn't really changed, but disabling safe mode is actually useful.  Right now, if you disable safe mode you just kind of have to hope you don't set a KSP variable to an bad value, which isn't really a good alternative.

Link to comment
Share on other sites

5 minutes ago, troyfawkes said:

I'd love some way to prevent errors where I know they might crop up outside of my control, isNull or isValid etc. style checks would be a godsend. It just bothers me on a spiritual level to see a script crash due to something I could easily write a check for, but can't, and no way to refactor (alternately, "why do I have to refactor? This is inherent in every programming language..."). Then you just run the script again and hope that one possible issue doesn't occur and crash the script, or give up entirely on solving the problem.

While we haven't been able to do it for everything, we try very hard to offer a way to predict an error condition where it's within our control.  That's why we have added things like `hassuffix`, `hasevent`, `hasnode`, and `hastarget`.  When you find one that we don't currently make available, take a look through the github issues and create a new one if it's not already on our radar.  The goal is to make it so a user never is forced to encounter an error that they can't predict.  There are a lot of things that are limited to the active vessel, so I have a number of functions that simply open with `if ship = kuniverse:activevessel`.

Link to comment
Share on other sites

1 minute ago, hvacengi said:

While we haven't been able to do it for everything, we try very hard to offer a way to predict an error condition where it's within our control.  That's why we have added things like `hassuffix`, `hasevent`, `hasnode`, and `hastarget`.  When you find one that we don't currently make available, take a look through the github issues and create a new one if it's not already on our radar.  The goal is to make it so a user never is forced to encounter an error that they can't predict.  There are a lot of things that are limited to the active vessel, so I have a number of functions that simply open with `if ship = kuniverse:activevessel`.

Ah ok, I'll submit those on a case-by-case basis. The issue I was having with a couple scripts before I ripped out half my hair was a null result, but "has:node" etc. might resolve those. Hopefully those are new and I didn't just fail at going through the documentation :/

Link to comment
Share on other sites

4 minutes ago, troyfawkes said:

Hopefully those are new and I didn't just fail at going through the documentation :/

Well... they were new in v0.19.0 but I'm revising where exactly in the documentation `hasnode` is because it wasn't shown on the maneuver node page itself (just the bound variables page).

Link to comment
Share on other sites

Exactly that. kOS should prevent pushing NaN or infinity into the stack, to prevent issues in core game engine.
It should still throw error if tries to calculate something that is not valid.For example:

set x to 1 / 0. // does not throw an error
print x:isvalid. // prints false
print x:isinfinity. // prints true
print x:isnan. // prints false

set y to x / 5. // This should throw error

Probably not the best solution, due to game engine limits and probably bunch of other stuff, but it is better than having nothing.
Also, you can use infinity value for different choices in script instead of using extremly large number for that purpose, for example.

Could be used in bunch of different ways, not only to prevent errors.

Link to comment
Share on other sites

5 hours ago, hvacengi said:

What I do want to include is a way to determine if a scalar is "Valid".  Right now, kOS is set to a safety mode by default where it will not allow "NaN", or +/-"Infinity".  This is because KSP has had issues with crashing if you do things like setting the throttle to Infinity or NaN.  Since there currently isn't a way to test the validity yourself, you can't really turn off that config setting without risking the crash.  If you could check `number:isvalid` before using it, that would allow you to use these values for something useful.  

What I'd like to see in a perfect world is some form of exception support (even if it's a single exception structure and is caught unconditionally, with members to examine to see what happened) to catch these cases and more (like my maneuver planning functions attempting to do the impossible).

Then config:safe would raise immediately when on and -- ideally -- whenever NaN or similar is sent to anything outside kOS if off.  I.e. don't let us crash KSP by setting throttle to infinity, crash our script/raise an exception instead.  Thus, config:safe would determine whether NaN triggers a crash when first encountered or is deferred until we try to do anything with it in KSP.

Link to comment
Share on other sites

7 hours ago, dewin said:

Then config:safe would raise immediately when on and -- ideally -- whenever NaN or similar is sent to anything outside kOS if off.  I.e. don't let us crash KSP by setting throttle to infinity, crash our script/raise an exception instead.  Thus, config:safe would determine whether NaN triggers a crash when first encountered or is deferred until we try to do anything with it in KSP.

If it was easy to be sure we had all the places where kOS values were sent to KSP and be 100% sure we had them all, we wouldn't have needed the "no Infinitys, no NaNs" rule in the first place.

Many, many places in kOS send values to KSP, and we don't have the resources to go and find them all, then test each and every single one of them to find out which ones are not protected against in KSP's own code.  Especially since it can't be done with automated unit testing either since many of these cases depend on the game being in a certain state (i.e. for this test you have to be in orbit, for that one you have to be landed, for that one you have to be under accelleration, for that one you have to be coasting, etc etc etc).

And before you say "why not just try/catch them?" the problem is that if we don't wrap small try/catches around everything, and instead try to catch them all in one universal try/catch, then it also interrupts the behavior of our VM and we don't get to control the state of our own data and make sure it's right.  (i.e. we stop right in the middle of trying to do the work of an opcode, and didn't finish updating all the VM's state.)  This is why exceptions in kerboscript aren't currently a thing.  When we throw exceptions and catch them in one universal location, the easiest way to get our own VM data back to a known working state despite work having been left half-done is to just kill the kOS script program entirely.  To implement kos exception handing, we'd have to keep a checkpoint of the entire state of the VM as it was just before embarking on starting work on the current Opcode, and then return to that state again if a script traps an exception that happened in the midst of trying to execute the Opcode.)

 

Link to comment
Share on other sites

// Until the goal has been reached, go through this process.

// The loop... Functions are defined below
UNTIL THERE_YET(goal)
{
	POPULATE().
	DECIDE_NEXT(options, goal).
	DRIVE_TO(nextPoint).
}

LOCK WHEELTHROTTLE TO -1. WAIT UNTIL GROUNDSPEED < 0.5. UNLOCK ALL. BRAKES ON.

FUNCTION THERE_YET
{
	PARAMETER point.
	
	SET here TO LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG).
	SET there TO point.
	
	// Crazy formula ahead!!!
	SET delta TO ARCTAN2(SQRT((COS(there:LAT * SIN(there:LNG - here:LNG))^2) + (COS(here:LAT) * SIN(there:LAT) - SIN(here:LAT) * COS(there:LAT) * COS(there:LNG - here:LNG) ^ 2)),
							(SIN(here:LAT) * SIN(there:LAT) + COS(here:LNG) * COS(there:LNG) * COS(there:LNG - here:LNG))).
	
	// Whew!
	SET dist TO BODY:RADIUS * delta.
	
	IF dist < 10
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION POPULATE
{
	SET offset_90 TO (360 * lookAhead) / (2 * BODY:RADIUS * CONSTANT:PI). //100m north, east, south, or west.
	SET offset_45 TO offset_90 * SIN(45).
	
	SET options TO LIST
	(
		LATLNG(SHIP:GEOPOSITION:LAT + offset_90, SHIP:GEOPOSITION:LNG), // N
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG + offset_45), // NE
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG + offset_90), // E
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG + offset_45), // SE
		LATLNG(SHIP:GEOPOSITION:LAT - offset_90, SHIP:GEOPOSITION:LNG), // S
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG - offset_45), // SW
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG - offset_90), // W
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG - offset_45) // W
	).
	RETURN options.
}

FUNCTION SLOPE_IS_SAFE
{
	PARAMETER option.
	SET here TO SHIP:GEOPOSITION.
	SET there TO option:GEOPOSITION.
	SET slope TO ABS(ARCTAN2(there:TERRAINHEIGHT - here:TERRAINHEIGHT, lookAhead)).
	IF slope < acceptableSlope
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION BEARING_IS_OK
{
	PARAMETER option, goal.
	SET brg TO ABS(goal:BEARING - option:BEARING).
	
	// Make sure the result makes sense...
	IF brg > 180
	{
		SET brg TO brg - 360.
	}
	
	IF brg < 60
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION DECIDE_NEXT
{
	PARAMETER goal, options.
	SET n TO 0.
	UNTIL FALSE
	{
		IF SLOPE_IS_SAFE(options[n]) AND BEARING_IS_OK(options[n], goal)
		{
			SET nextPoint TO options[n].
			BREAK.
		}
		ELSE IF SLOPE_IS_SAFE(options[n])
		{
			SET nextPoint TO options[n].
			BREAK.
		}
		ELSE
		{
			SET n TO n + 1.
		}
	}
	RETURN nextPoint.
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	SET spd TO 1. // Really stinkin' slow, but safe.

	// Throttle control variables
	SET kPT TO 0.05. SET kIT TO 0.25. SET kDT TO 0.75.
	SET throttlePID TO PIDLOOP(kPT, kIT, kDT, -1, 1).
	SET throttlePID:SETPOINT TO spd.

	// Steering control variables
	SET kPS TO (1/180). SET kIS TO 0.01. SET kDS TO 0.125.
	SET steerPID TO PIDLOOP(kPS, kIS, kDS, -1, 1).
	SET steerPID:SETPOINT TO point:HEADING.
	
	UNTIL THERE_YET(point)
	{
		SET WHEELTHROTTLE TO throttlePID:OUTPUT.
		SET WHEELSTEER TO steerPID:OUTPUT.
	}
}

Ok... Revamped the autonav script, but now I'm having trouble with iteration... What's going on?

Link to comment
Share on other sites

11 hours ago, MAFman said:

// Until the goal has been reached, go through this process.

// The loop... Functions are defined below
UNTIL THERE_YET(goal)
{
	POPULATE().
	DECIDE_NEXT(options, goal).
	DRIVE_TO(nextPoint).
}


FUNCTION BEARING_IS_OK
{
	PARAMETER option, goal.
	SET brg TO ABS(goal:BEARING - option:BEARING).
	
	// Make sure the result makes sense...
	IF brg > 180
	{
		SET brg TO brg - 360.
	}
	
	IF brg < 60
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION DECIDE_NEXT
{
	PARAMETER goal, options.
	SET n TO 0.
	UNTIL FALSE
	{
		IF SLOPE_IS_SAFE(options[n]) AND BEARING_IS_OK(options[n], goal)
		{
			SET nextPoint TO options[n].
			BREAK.
		}
		ELSE IF SLOPE_IS_SAFE(options[n])
		{
			SET nextPoint TO options[n].
			BREAK.
		}
		ELSE
		{
			SET n TO n + 1.
		}
	}
	RETURN nextPoint.
}

Ok... Revamped the autonav script, but now I'm having trouble with iteration... What's going on?

Just quick peek at the code, but it looks like you didn't set properly "goal" value in DECEIDE_NEXT function.
From main loop, you call deceide_next and from within deceide_next you check for goal value only trough BEARING_IS_OK.

It is not clear for me in what part of code you set goal as you have reached destination or not. Hope that will help you to get back on proper track.

Link to comment
Share on other sites

12 hours ago, MAFman said:


UNTIL THERE_YET(goal)
{
	POPULATE().
	DECIDE_NEXT(options, goal).
	DRIVE_TO(nextPoint).
}

...

FUNCTION DECIDE_NEXT
{
	PARAMETER goal, options.

    ...
}

...

Ok... Revamped the autonav script, but now I'm having trouble with iteration... What's going on?

Notice that order of parameters for DECIDE_NEXT is changed between call and definition.

Link to comment
Share on other sites

36 minutes ago, MAFman said:

It's still not letting me iterate over the options, though... Attached is a screenshot of the error.

What is initially assigned to startPos? Error sounds like it's not an integer.

Link to comment
Share on other sites

Are you sure you don't have some set of coordinates accidentally assigned to startPos instead of another variable somewhere? Also it seems you are missing RETURN when recursively calling the function  in ELSE branch

Link to comment
Share on other sites

47 minutes ago, MAFman said:

 

https://drive.google.com/file/d/0B1BXLWa5LPIMWmw4M0xPTk1JNE0/view?usp=sharing

Ok, corrected the order of parameters bug / typo. It's still not letting me iterate over the options, though... Attached is a screenshot of the error.

 

In the image the error message says "called from ... DECIDE_NEXT(options, goal, 0)" but the function def. has options and goal in the other order.

Link to comment
Share on other sites

// Until the goal has been reached, go through this process.
PARAMETER goal.
// The loop... Functions are defined below
UNTIL THERE_YET(goal)
{
	POPULATE().
	DECIDE_NEXT(goal, options, 0).
	DRIVE_TO(nextPoint).
}

LOCK WHEELTHROTTLE TO -1. WAIT UNTIL GROUNDSPEED < 0.5. UNLOCK ALL. BRAKES ON.

FUNCTION THERE_YET
{
	PARAMETER point.
	
	SET here TO LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG).
	SET there TO point.
	
	// Crazy formula ahead!!!
	SET delta TO ARCTAN2(SQRT((COS(there:LAT * SIN(there:LNG - here:LNG))^2) + (COS(here:LAT) * SIN(there:LAT) - SIN(here:LAT) * COS(there:LAT) * COS(there:LNG - here:LNG) ^ 2)),
							(SIN(here:LAT) * SIN(there:LAT) + COS(here:LNG) * COS(there:LNG) * COS(there:LNG - here:LNG))).
	
	// Whew!
	SET dist TO BODY:RADIUS * delta.
	
	IF dist < 10
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION POPULATE
{
	SET offset_90 TO (360 * lookAhead) / (2 * BODY:RADIUS * CONSTANT:PI). //100m north, east, south, or west.
	SET offset_45 TO offset_90 * SIN(45).
	
	SET options TO LIST
	(
		LATLNG(SHIP:GEOPOSITION:LAT + offset_90, SHIP:GEOPOSITION:LNG), // N
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG + offset_45), // NE
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG + offset_90), // E
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG + offset_45), // SE
		LATLNG(SHIP:GEOPOSITION:LAT - offset_90, SHIP:GEOPOSITION:LNG), // S
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG - offset_45), // SW
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG - offset_90), // W
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG - offset_45) // W
	).
	RETURN options.
}

FUNCTION SLOPE_IS_SAFE
{
	PARAMETER option.
	SET here TO SHIP:GEOPOSITION.
	SET there TO option.
	SET slope TO ABS(ARCTAN2(there:TERRAINHEIGHT - here:TERRAINHEIGHT, lookAhead)).
	IF slope < acceptableSlope
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION BEARING_IS_OK
{
	PARAMETER option, goal.
	SET brg TO ABS(goal:BEARING - option:BEARING).
	
	// Make sure the result makes sense...
	IF brg > 180
	{
		SET brg TO brg - 360.
	}
	
	IF brg < 60
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION DECIDE_NEXT
{
	PARAMETER goal, options, startPos.
	SET o TO options[startPos].
	IF SLOPE_IS_SAFE(o) AND BEARING_IS_OK(o, goal)
	{
		SET nextPoint TO o. RETURN nextPoint.
	}
	ELSE
	{
		SET startPos TO startPos + 1.
		DECIDE_NEXT(goal, options, startPos).
	}
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	SET spd TO 1. // Really stinkin' slow, but safe.

	// Throttle control variables
	SET kPT TO 0.05. SET kIT TO 0.25. SET kDT TO 0.75.
	SET throttlePID TO PIDLOOP(kPT, kIT, kDT, -1, 1).
	SET throttlePID:SETPOINT TO spd.

	// Steering control variables
	SET kPS TO (1/180). SET kIS TO 0.01. SET kDS TO 0.125.
	SET steerPID TO PIDLOOP(kPS, kIS, kDS, -1, 1).
	SET steerPID:SETPOINT TO point:HEADING.
	
	UNTIL THERE_YET(point)
	{
		SET WHEELTHROTTLE TO throttlePID:OUTPUT.
		SET WHEELSTEER TO steerPID:OUTPUT.
	}
}

Fixed it?

Link to comment
Share on other sites

9 minutes ago, MAFman said:

// Until the goal has been reached, go through this process.
PARAMETER goal.
// The loop... Functions are defined below
UNTIL THERE_YET(goal)
{
	POPULATE().
	DECIDE_NEXT(goal, options, 0).
	DRIVE_TO(nextPoint).
}

LOCK WHEELTHROTTLE TO -1. WAIT UNTIL GROUNDSPEED < 0.5. UNLOCK ALL. BRAKES ON.

FUNCTION THERE_YET
{
	PARAMETER point.
	
	SET here TO LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG).
	SET there TO point.
	
	// Crazy formula ahead!!!
	SET delta TO ARCTAN2(SQRT((COS(there:LAT * SIN(there:LNG - here:LNG))^2) + (COS(here:LAT) * SIN(there:LAT) - SIN(here:LAT) * COS(there:LAT) * COS(there:LNG - here:LNG) ^ 2)),
							(SIN(here:LAT) * SIN(there:LAT) + COS(here:LNG) * COS(there:LNG) * COS(there:LNG - here:LNG))).
	
	// Whew!
	SET dist TO BODY:RADIUS * delta.
	
	IF dist < 10
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION POPULATE
{
	SET offset_90 TO (360 * lookAhead) / (2 * BODY:RADIUS * CONSTANT:PI). //100m north, east, south, or west.
	SET offset_45 TO offset_90 * SIN(45).
	
	SET options TO LIST
	(
		LATLNG(SHIP:GEOPOSITION:LAT + offset_90, SHIP:GEOPOSITION:LNG), // N
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG + offset_45), // NE
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG + offset_90), // E
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG + offset_45), // SE
		LATLNG(SHIP:GEOPOSITION:LAT - offset_90, SHIP:GEOPOSITION:LNG), // S
		LATLNG(SHIP:GEOPOSITION:LAT - offset_45, SHIP:GEOPOSITION:LNG - offset_45), // SW
		LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG - offset_90), // W
		LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG - offset_45) // W
	).
	RETURN options.
}

FUNCTION SLOPE_IS_SAFE
{
	PARAMETER option.
	SET here TO SHIP:GEOPOSITION.
	SET there TO option.
	SET slope TO ABS(ARCTAN2(there:TERRAINHEIGHT - here:TERRAINHEIGHT, lookAhead)).
	IF slope < acceptableSlope
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION BEARING_IS_OK
{
	PARAMETER option, goal.
	SET brg TO ABS(goal:BEARING - option:BEARING).
	
	// Make sure the result makes sense...
	IF brg > 180
	{
		SET brg TO brg - 360.
	}
	
	IF brg < 60
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION DECIDE_NEXT
{
	PARAMETER goal, options, startPos.
	SET o TO options[startPos].
	IF SLOPE_IS_SAFE(o) AND BEARING_IS_OK(o, goal)
	{
		SET nextPoint TO o. RETURN nextPoint.
	}
	ELSE
	{
		SET startPos TO startPos + 1.
		DECIDE_NEXT(goal, options, startPos).
	}
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	SET spd TO 1. // Really stinkin' slow, but safe.

	// Throttle control variables
	SET kPT TO 0.05. SET kIT TO 0.25. SET kDT TO 0.75.
	SET throttlePID TO PIDLOOP(kPT, kIT, kDT, -1, 1).
	SET throttlePID:SETPOINT TO spd.

	// Steering control variables
	SET kPS TO (1/180). SET kIS TO 0.01. SET kDS TO 0.125.
	SET steerPID TO PIDLOOP(kPS, kIS, kDS, -1, 1).
	SET steerPID:SETPOINT TO point:HEADING.
	
	UNTIL THERE_YET(point)
	{
		SET WHEELTHROTTLE TO throttlePID:OUTPUT.
		SET WHEELSTEER TO steerPID:OUTPUT.
	}
}

Fixed it?

It no longer complains when I try the command RUN autonav(1000,270), but now it just does nothing at all.

PARAMETER goalDistance, goalHeading.

// Navigation variables
SET lookAhead TO 100. // Waypoint options will be plotted this far out in all directions
SET acceptableSlope TO 15. // The new waypoint can have this much relative slope and still be good
SET acceptableBearingDiff TO 60. // The new waypoint can be this far off the goal and still be good

// Initialize the navigation by setting a geo-point goal
SET startPosition TO LATLNG(SHIP:GEOPOSITION:LAT, SHIP:GEOPOSITION:LNG).

SET goalOffset TO (360 * goalDistance) / (BODY:RADIUS * 2 * CONSTANT:PI).
SET goalLatOff TO goalOffset * SIN(goalHeading).
SET goalLonOff TO goalOffset * COS(goalHeading).

SET goal TO LATLNG(startPosition:LAT + goalLatOff, startPosition:LNG + goalLonOff).

RUN autoNavIterate(goal).

 

Link to comment
Share on other sites

2 hours ago, MAFman said:

It no longer complains when I try the command RUN autonav(1000,270), but now it just does nothing at all.

You're not updating the pid in your DRIVE_TO loop. Try the following - I added print statements to see what was going on and pass current speed and heading in as inputs to the pids to get updated outputs:

Spoiler

FUNCTION DRIVE_TO
{
	PARAMETER point.
	
	BRAKES off.
	SET spd TO 1. // Really stinkin' slow, but safe.

	// Throttle control variables
	SET kPT TO 0.05. SET kIT TO 0.25. SET kDT TO 0.75.
	SET throttlePID TO PIDLOOP(kPT, kIT, kDT, -1, 1).
	SET throttlePID:SETPOINT TO spd.

	// Steering control variables
	SET kPS TO (1/180). SET kIS TO 0.01. SET kDS TO 0.125.
	SET steerPID TO PIDLOOP(kPS, kIS, kDS, -1, 1).
	SET steerPID:SETPOINT TO point:HEADING.
	
	clearscreen.
	
	set thrott to 0.
	lock wheelthrottle to thrott.
	
	set steerVal to 0.
	lock wheelsteer to steerVal.
	
	UNTIL THERE_YET(point)
	{
		set thrott to throttlePID:UPDATE(TIME:SECONDS, SHIP:GROUNDSPEED).
		SET steerVal TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING).
		
		print "Here: " + SHIP:GEOPOSITION at (0,3).
		Print "Dest: " + point at (0,4).
		Print "ThrottlePID " + thrott at (0,9).
		Print "SteerPID    " + steerVal at (0,10).
		
		wait 0.
	}
}

 

Edit: it now drives forward rather jerkily. The pid values will need to be tuned to get it to control speed and direction better.

Edited by Aelfhe1m
additional note
Link to comment
Share on other sites

Try with this settings for more gentle PID behaviour, it worked better for me in my scripts than defaults that were recommended elsewhere:

set MyPID to PIDLOOP( 0.15, 0.05, 0.1, -1.0, 1.0 ).

It might be also good idea to limit min/max values for steering based on rover ground speed. -1,1 for low speeds, -0.5,0.5 for higher speeds, to prevent jerkily behaviour. Please, note that I used above PID settings for very gently behaviour, if it is too gentle for you, then increase first parameter a bit.

Link to comment
Share on other sites

Hmmm... Ok, now the driving works, but the navigation is behaving badly. It appears to want to drive straight south...

How do I say, "Ok, go to the local radio horizon at this heading."?

Link to comment
Share on other sites

I totally redid the algorithm. However, I'm still getting that "not in dictionary" error. What am I doing wrong?

AutoNavInit.ks:

PARAMETER goalHeading.

// Navigation variables
SET lookAhead TO 10. // Waypoint options will be plotted this far out in all directions
SET acceptableSlope TO 10. // The new waypoint can have this much relative slope and still be good

FUNCTION NOTIFY
{
	PARAMETER message, priority.
	IF priority = "NOMINAL"
	{
		SET col TO GREEN.
	}
	ELSE IF priority = "ALERT"
	{
		SET col TO RGB(1,0.31,0). // international orange
	}
	ELSE IF priority = "WARNING"
	{
		SET col TO RED.
	}
	HUDTEXT("kOS: " + message, 5, 2, 30, col, FALSE).
}

NOTIFY("Starting navigation script...Done.", "NOMINAL").
WAIT 5.

NOTIFY("Downloading navigation instructions...Done.", "NOMINAL").
WAIT 5.

NOTIFY("Running iterative navigator...", "NOMINAL").
RUN autoNavIterate(goalHeading).

 

AutoNavIterate.ks:

// Until the goal has been reached, go through this process.

PARAMETER direct.

// The loop... Functions are defined below
IF ADDONS:RT:HASCONNECTION(SHIP)
{
	UNTIL FALSE
	{
		POPULATE(direct).
		DECIDE_NEXT(options).
		NOTIFY("Selecting next waypoint.", "NOMINAL").
		WAIT 0.5.
		DRIVE_TO(nextPoint).
		STOP(). WAIT 5.
		
		IF NOT ADDONS:RT:HASCONNECTION(SHIP)
		{
			BREAK.
		}
	}
}
ELSE
{
	UNTIL FALSE
	{
		POPULATE(360 - direct).
		DECIDE_NEXT(options).
		NOTIFY("Selecting next waypoint", "NOMINAL").
		WAIT 0.5.
		DRIVE_TO(nextPoint).
		STOP(). WAIT 5.
		
		IF ADDONS:RT:HASCONNECTION(SHIP)
		{
			BREAK.
		}
	}
}
PARK().

FUNCTION THERE_YET
{
	PARAMETER point.
	
	IF point:DISTANCE < 10
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION POPULATE
{
	PARAMETER hdg.
	
	SET offset_90 TO SIN(hdg) * ((360 * lookAhead) / (BODY:RADIUS * 2 * CONSTANT:PI)). // duh
	SET offset_675 TO offset_90 * SIN(67.5). // 67.5 deg.
	SET offset_45 TO offset_90 * SIN(45). // 45 deg.
	SET offset_225 TO offset_90 * SIN(22.5). // 22.5 deg.
	
	SET op_A TO LATLNG(SHIP:GEOPOSITION:LAT + offset_225, SHIP:GEOPOSITION:LNG - offset_675).
	SET op_B TO LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG - offset_45).
	SET op_C TO LATLNG(SHIP:GEOPOSITION:LAT + offset_90, SHIP:GEOPOSITION:LNG).
	SET op_D TO LATLNG(SHIP:GEOPOSITION:LAT + offset_45, SHIP:GEOPOSITION:LNG + offset_45).
	SET op_E TO LATLNG(SHIP:GEOPOSITION:LAT + offset_225, SHIP:GEOPOSITION:LNG + offset_675).
	
	SET options TO LIST
	(
		op_A, op_B, op_C, op_D, op_E
	).
	
	RETURN options.
}

FUNCTION SLOPE_IS_SAFE
{
	PARAMETER option.
	SET here TO SHIP:GEOPOSITION.
	SET there TO option.
	SET slope TO ABS(ARCTAN2(there:TERRAINHEIGHT - here:TERRAINHEIGHT, lookAhead)).
	IF slope < acceptableSlope
	{
		RETURN TRUE.
	}
	ELSE
	{
		RETURN FALSE.
	}
}

FUNCTION DECIDE_NEXT
{
	PARAMETER options.
	SET startPos TO 0.
	UNTIL startPos > options:LENGTH - 1
	{
		SET o TO options[startPos].
		IF SLOPE_IS_SAFE(o)
		{
			SET nextPoint TO o.
			RETURN nextPoint.
			BREAK.
		}
		ELSE
		{
			SET startPos TO startPos + 1.
		}
	}
}

FUNCTION DRIVE_TO
{
	PARAMETER point.
	
	BRAKES OFF.
	SET spd TO 1. // Really stinkin' slow, but safe.

	// Throttle control variables
	SET throttlePID TO PIDLOOP(0.15, 0.05, 0.1, -1.0, 1.0).
	SET throttlePID:SETPOINT TO spd.

	// Steering control variables
	SET steerPID TO PIDLOOP(0.15, 0.05, 0.1, -1.0, 1.0).
	SET steerPID:SETPOINT TO point:HEADING.
	
	CLEARSCREEN.
	
	SET thrott TO 0.
	SET steerVal TO 0.
	
	UNTIL THERE_YET(point)
	{
		SET thrott TO throttlePID:UPDATE(TIME:SECONDS, SHIP:GROUNDSPEED).
		SET steerVal TO steerPID:UPDATE(TIME:SECONDS, SHIP:HEADING).
		
		LOCK WHEELTHROTTLE TO thrott.
		LOCK WHEELSTEER TO steerVal.
		
		PRINT "Current Location: " + ROUND(SHIP:GEOPOSITION:LAT,3) + ", " + ROUND(SHIP:GEOPOSITION:LNG,3) at (0,3).
		PRINT "Next waypoint: " + ROUND(point:LAT,3) + ", " + ROUND(point:LNG, 3) AT (0,4).
		PRINT "Throttle: " + ROUND(thrott, 3) AT (0,9).
		PRINT "Steering:    " + ROUND(steerVal,3) AT (0,10).
		
		WAIT 0.01.
	}
	STOP().
}

FUNCTION STOP
{
	IF GROUNDSPEED > 0.5
	{
		LOCK WHEELTHROTTLE TO -0.5.
	}
	ELSE
	{
		UNLOCK WHEELTHROTTLE.
		IF NOT BRAKES
		{
			BRAKES ON.
		}
	}
	UNLOCK ALL. NOTIFY("Arrived!", "NOMINAL").
}

FUNCTION PARK
{
	STOP().
	LIST PARTS.
	FOR p IN PARTS
	{
		p:GETMODULE("ModuleAnimateGeneric"):DOEVENT("Extend").
		p:GETMODULE("ModuleLight"):DOEVENT("Lights on").
	}
}

 

Link to comment
Share on other sites

Ok, here's the log file... I finally found it... (at least the part with the kOS error.)

[LOG 19:10:59.261] kOS: FlightCtrlParam: Enabled: throttle False => False
[LOG 19:10:59.261] kOS: FlightCtrlParam: Enabled: steering False => False
[LOG 19:10:59.262] kOS: FlightCtrlParam: Enabled: wheelthrottle False => False
[LOG 19:10:59.263] kOS: FlightCtrlParam: Enabled: wheelsteering False => False
[LOG 19:10:59.263] kOS: FlightControl Unbinding
[LOG 19:10:59.264] kOS: FlightControl Unbound
[LOG 19:10:59.265] kOS: Pushing context staring with: File                 Line:Col IP   label   opcode operand
[LOG 19:10:59.266] kOS: Saving and removing 9 pointers
[LOG 19:10:59.271] kOS: FlightControlManager.AddTo 45e59b2a-16bb-4de8-9909-bd98dd240abb
[LOG 19:10:59.271] kOS: FlightCtrlParam: Enabled: throttle False => False
[LOG 19:10:59.272] kOS: FlightCtrlParam: Enabled: steering False => False
[LOG 19:10:59.273] kOS: FlightCtrlParam: Enabled: wheelthrottle False => False
[LOG 19:10:59.274] kOS: FlightCtrlParam: Enabled: wheelsteering False => False
[LOG 19:10:59.276] kOS: Pushing context staring with: File                 Line:Col IP   label   opcode operand
[LOG 19:10:59.276] kOS: Saving and removing 0 pointers
[LOG 19:10:59.545] kOS: FlightControl Binding
[LOG 19:10:59.546] kOS: FlightControl Bound
[LOG 19:10:59.579] kOS: FunctionCopy: Volume: 0 Direction: from Filename: startup.ks
[LOG 19:10:59.591] kOS: FunctionCopy: Volume: 0 Direction: from Filename: autonavinit.ks
[LOG 19:10:59.602] kOS: FunctionCopy: Volume: 0 Direction: from Filename: autonaviterate.ks
[LOG 19:10:59.953] Updating vessel voxel for Relay
[LOG 19:11:00.182] Std dev for smoothing: 3 voxel total vol: 40.614487677163 filled vol: 3.44612579691208

[LOG 19:11:01.574] Updating vessel voxel for Relay
[LOG 19:11:01.797] Std dev for smoothing: 3 voxel total vol: 40.614487677163 filled vol: 3.44612579691208

[LOG 19:11:03.243] Updating vessel voxel for Relay
[LOG 19:11:03.418] Std dev for smoothing: 3 voxel total vol: 40.614487677163 filled vol: 3.44612579691208

[LOG 19:11:11.258] kOS: At autonavinit.ks on 1, line 10
RUN autoNavIterate(goalHeading).
^
Called from startup.ks on 1, line 3
RUN autoNavInit(270).
^
Called from boot.ks on archive, line 124
	RUN startup.ks.
 ^
Called from sys:boot, line 1
(Can't show source line)
^

[LOG 19:11:11.259] System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
  at System.Collections.Generic.Dictionary`2[System.String,System.Int32].get_Item (System.String key) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Compilation.ProgramBuilder.ReplaceLabels (System.Collections.Generic.List`1 program) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Compilation.ProgramBuilder.BuildProgram () [0x00000] in <filename unknown>:0 
  at kOS.Safe.Execution.ProgramContext.AddObjectParts (IEnumerable`1 parts) [0x00000] in <filename unknown>:0 
  at kOS.Function.FunctionLoad.Execute (kOS.SharedObjects shared) [0x00000] in <filename unknown>:0 
  at kOS.Function.FunctionManager.CallFunction (System.String functionName) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Execution.CPU.CallBuiltinFunction (System.String functionName) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Compilation.OpcodeCall.StaticExecute (ICpu cpu, Boolean direct, System.Object destination, Boolean calledFromKOSDelegateCall) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Compilation.OpcodeCall.Execute (ICpu cpu) [0x00000] in <filename unknown>:0 
  at kOS.Safe.Execution.CPU.ExecuteInstruction (IProgramContext context, Boolean doProfiling) [0x00000] in <filename unknown>:0 
[LOG 19:11:11.265] Code Fragment
File                 Line:Col IP   label   opcode operand
====                 ====:=== ==== ================================  
1/startup.ks            3:17  0556 @0552   push 270 
1/startup.ks            3:17  0557 @0553   call 526 
1/startup.ks            3:17  0558 @0554   pop 
                        0:0   0559         push 0 
                        0:0   0560         return 0 deep 
1/autonavinit.ks       10:1   0561 @0555   push $program-autonaviterate* 
1/autonavinit.ks       10:1   0562 @0556   exists 
1/autonavinit.ks       10:1   0563 @0557   br.false +4 
1/autonavinit.ks       10:1   0564 @0558   br.false +11 
1/autonavinit.ks       10:1   0565 @0559   push 0 
1/autonavinit.ks       10:1   0566 @0560   return 0 deep 
1/autonavinit.ks       10:1   0567 @0561   pop 
1/autonavinit.ks       10:1   0568 @0562   push $program-autonaviterate* 
1/autonavinit.ks       10:1   0569 @0563   push _KOSArgMarker_ 
1/autonavinit.ks       10:1   0570 @0564   push autonaviterate 
1/autonavinit.ks       10:1   0571 @0565   push null 
1/autonavinit.ks       10:1   0572 @0566   call load() <<--INSTRUCTION POINTER--
1/autonavinit.ks       10:1   0573 @0567   pop 
1/autonavinit.ks       10:1   0574 @0568   store 
1/autonavinit.ks       10:1   0575 @0569   call $program-autonaviterate* 
1/autonavinit.ks       10:1   0576 @0570   pop 
1/autonavinit.ks       10:1   0577 @0571   push 0 
1/autonavinit.ks       10:1   0578 @0572   return 0 deep 
1/autonavinit.ks        1:11  0579 @0573   push goalheading 
1/autonavinit.ks        1:11  0580 @0574   swap 
1/autonavinit.ks        1:11  0581 @0575   storelocal 
1/autonavinit.ks        1:11  0582 @0576   argbottom 
1/autonavinit.ks        3:7   0583 @0577   push _KOSArgMarker_ 
1/autonavinit.ks        3:8   0584 @0578   push Starting navigation script...Done. 
1/autonavinit.ks        3:46  0585 @0579   push NOMINAL 
1/autonavinit.ks        3:46  0586 @0580   call $notify* 
1/autonavinit.ks        3:46  0587 @0581   pop 
1/autonavinit.ks        4:6   0588 @0582   push 5 

[LOG 19:11:11.273] kOS: Stack dump: stackPointer = 10
018      SubroutineContext: {CameFromInstPtr 23, TriggerPointer -1} (type: SubroutineContext)
017      SubroutineContext: {CameFromInstPtr 16, TriggerPointer -1} (type: SubroutineContext)
016      kOS.Safe.Execution.VariableScope (type: VariableScope)
          ScopeId=22, ParentScopeId=0, ParentSkipLevels=1 IsClosure=False
015      SubroutineContext: {CameFromInstPtr 502, TriggerPointer -1} (type: SubroutineContext)
014      SubroutineContext: {CameFromInstPtr 375, TriggerPointer -1} (type: SubroutineContext)
013      SubroutineContext: {CameFromInstPtr 558, TriggerPointer -1} (type: SubroutineContext)
012      SubroutineContext: {CameFromInstPtr 541, TriggerPointer -1} (type: SubroutineContext)
011      SubroutineContext: {CameFromInstPtr 607, TriggerPointer -1} (type: SubroutineContext)
010 SP-> $program-autonaviterate* (type: String)
009      270 (type: Scalar)
008      _KOSArgMarker_ (type: KOSArgMarkerType)
007      _KOSArgMarker_ (type: KOSArgMarkerType)
006      _KOSArgMarker_ (type: KOSArgMarkerType)
005      _KOSArgMarker_ (type: KOSArgMarkerType)
004      _KOSArgMarker_ (type: KOSArgMarkerType)
003      _KOSArgMarker_ (type: KOSArgMarkerType)
002      _KOSArgMarker_ (type: KOSArgMarkerType)
001      _KOSArgMarker_ (type: KOSArgMarkerType)
000      0 (type: Scalar)

[LOG 19:11:11.277] kOS: Popping context 2
[LOG 19:11:11.278] kOS: Removed Context File                 Line:Col IP   label   opcode operand

 

Link to comment
Share on other sites

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