Everyone likes a good lunar landing simulator, and [Dominic Doty] wrote a fun take on the idea: your goal is to write an autopilot controller to manage the landing. Try it out!
[Dominic] was inspired in part by this simple rocket landing game which is very much an exercise in reflex and intuition, not to mention being much faster-paced than the classic 1979 video game (which you can also play in your browser here.)
[Dominic]’s version has a similar classic look to the original, but embraces a more thoughtful approach. In it, one uses plain JavaScript to try to minimize the lander’s angle, velocity, and angular velocity in order to land safely on the generated terrain.
Want to see if you have the right stuff? Here’s a direct link to Lunar Pilot. Don’t get discouraged if you don’t succeed right away, though. Moon landings have had plenty of failures, and are actually very hard.
Pretty cool, but do not try to just write the word
print
in the editor LMAOHaha… that’s funny. I too searched for a way to log variables. console.log works but is not super comfortable…
Should have a variable log window, also single step possibilities…
2 minutes in and I’ve faced the same issue :D
You can bypass it by pressing and holding escape button and then you can remove print line
Angular velocity? Sure about that?
Why is it not angular velocity?
Because as the spacecraft rotates you’re only rarely going to be able to see the LZ? Perhaps I don’t understand what the term means.
Minimizing angular velocity is important. If you come down softly with low vertical and lateral velocity and perfectly upright but have significant angular velocity (rate of spin), the angular momentum (moment of inertia x angular velocity) will keep the ship spinning after the touchdown and it will crash.
That’s what I was firetruckin’ saying! Don’t let the spaceship spin because it’ll make landing superdifficult. So no need to control it. The ship also isn’t going up so no need to control the upward velocity.
If it’s a fancy way off saying “don’t let it wiggle” fine, but please no tarting it up in tech language. Parentheses (()) are your friend. Yes, recursive parentheses!
The only way to set the thrust vector is to rotate the craft which requires good angular velocity control. That means you must be able to control the angular pose by increasing the angular velocity towards the desired angular pose and then decreasing the angular velocity at the correct rate to stop at the desired angular pose. Since you can only command angular acceleration (thrust) and angular velocity doesn’t automagically zero or decrease with no angular acceleration, your control system does require some thought.
This would be much easier to play with on the phone if there was a text area to send debug to… I guess I’ll have to try again later on the laptop where there’s a console
It would be nice if there was some docs on how to output debug info… no real console on the thing from what I’ve seen.
console.log works in browser console
Is it just me or isn’t it working? I change the thrust or rotation but nothing seems to happen.
Nevermind, I’ve removed the :0 and it works.
I also had to try a few times, but the moon has several new craters!
Here’s my take, with a little PID like control loop stuff mixed in, works with ballistic and tumbling most of the time (quotes are replaced with non compatible 6 and 9 quotes so change them to normal double quotes):
// Arguments:
let {x_position, altitude, angle, userStore} = arguments[0];
// Example of how to initialize user storage
if (!(“store” in userStore)) {
userStore.store = 0;
userStore.initialAltitude = altitude;
userStore.prevAltitude = 0;
userStore.prevSpeed = 0;
userStore.prevAngle = 0;
userStore.prevX = 0;
}
speed = 0;
xspeed = userStore.prevX-x_position;
AngleSpeed = userStore.prevAngle – angle;
rotSpeed = 0;
if( angle > .1 || angle < .1 ) rotSpeed = ((AngleSpeed/6)-(((angle^2)/360)))*5 + (xspeed/6);
rotThrust = rotSpeed;
if( altitude < 260 ) speed = (userStore.prevAltitude – altitude)/1.5;
aftThrust = speed;
if( speed < 0 ) aftThrust = 0;
//rotThrust = 0;
userStore.prevAltitude = altitude;
userStore.prevAngle = angle;
userStore.prevX = x_position;
// Return:
return { rotThrust, aftThrust, userStore:userStore };
Oh and the – (minus) symbol gets replaced by an m dash, so you need to change that back as well.
// Arguments:
let {x_position, altitude, angle, userStore} = arguments[0];
// Example of how to initialize user storage
if (!(“store” in userStore)) {
userStore.store = {
lastAltitude: altitude,
};
}
// Calculate descent speed and decide on thrust
let descentSpeed = userStore.store.lastAltitude - altitude;
let requiredThrust = 0;
if(altitude<250 && descentSpeed>.5){
requiredThrust = 0.75;
}else if(altitude<50 && descentSpeed>0.25){
requiredThrust = .50;
}else if(altitude<10 && descentSpeed>0.05){
requiredThrust = .25;
}
// Update user store with the current state
userStore.store.lastAltitude = altitude;
// Return thrust commands
return {
rotThrust: 0, // Assume no rotational control needed for simplicity
aftThrust: requiredThrust, // Apply aft thrust to slow descent
userStore: userStore // Preserve user storage state
};
//Lovingly coded by Pauline Pounds
//Please enjoy my lazy implementation using PD control. As-is, works in most situations; not so great on tumbling ballistic. If I had the time I’d do a better (minimum fuel) implementation of velocity compensation prior to switching to PD. I figure as a professor in mechatronics specialising in control of UAVs I should be ok at this…
// Arguments:
let {x_position, altitude, angle, userStore} = arguments[0];
//User storage
if (!(“rot” in userStore)) {userStore.rot = 0;}
if (!(“thr” in userStore)) {userStore.thr = 0;}
if (!(“angle0” in userStore)) {userStore.angle0 = 0;}
if (!(“x0” in userStore)) {userStore.x0 = 0;}
if (!(“alt0” in userStore)) {userStore.alt0 = 0;}
//PD Control laws
userStore.rot = -1angle – 20(angle-userStore.angle0) + 20(userStore.x0 – x_position);
userStore.thr = 0.01(10-altitude) -1(altitude-userStore.alt0) – 5(userStore.x0 – x_position);
//Bounds checking
if(userStore.rot > 1){userStore.rot = 1;}
if(userStore.rot < -1){userStore.rot = -1;}
if(userStore.thr > 1){userStore.thr = 1;}
if(userStore.thr < 0){userStore.thr = 0;}
//Update history
userStore.x0 = x_position;
userStore.angle0 = angle;
userStore.alt0 = altitude;
// Return:
return { rotThrust:userStore.rot, aftThrust:userStore.thr, userStore:userStore };
Buh, it’s stripped out the asterisks for the multiplication in the control law. Just add them in yourself if you want to try it out.
Thanks for the concise code! If we disable the main thruster when upside down it sticks most ballistic landings.
userStore.thr = 0.01(10-altitude) -1(altitude-userStore.alt0) – 1*(userStore.x0 – x_position); // save some fuel here
// do not use main thruster when upside down
if (angle < -90) {userStore.thr = 0;}
if (angle > 90) {userStore.thr = 0;}
you know there are ksp mods for this.
This is going well! Oh no, I forgot about horizontal velocity! explodes
This seems to land almost every time for the vertical scenario:
aftThrust = 0;
if (altitude<170)
{
aftThrust = 1;
}
else if (altitude <10)
{
aftThrust = 0;
}
MVP:
// Arguments:
let {x_position, altitude, angle, userStore} = arguments[0];
// Return:
return { rotThrust:0, aftThrust:175-altitude, userStore:userStore };
I played with this for a while, if anyone wants a nice console.table print-out for live debugging, please enjoy.
gravity_accel = 1/60
mass_ship = 10
fuelLevel_x+1 = fuelLevel_x – (rotThrust + aftThrust) * 0.5
mass_fuel = fuelLevel / 10
So necessary thrust for hovering is gravity_accel * (mass_ship + mass_fuel)
1/3 for full fuel, 10/60 for empty fuel
Who will do the same work for the moment of inertia?
Attention: Altitude is absolute, not height above ground.
Overall very nice for e.g. a control engineering class, but too much bookkeeping up front…
I ended up deciding to open source this if anyone is interested:
https://github.com/dominicdoty/lunar_lander