// Auto-pilot for docking with the International Space Station // // The program uses Artificial Intelligence and Decision Trees (i.e. basic kinematics and a bunch of if statements) // to perform docking with the ISS from any starting position. // // To use it: // - open the SpaceX simulation website: https://iss-sim.spacex.com/ // - open the Developer's console and paste the contents of this file // // Demo: https://youtu.be/jWQQH2_UGLw // function el(v) { return document.getElementById(v); } function blink(e) { e.style.backgroundColor='red'; setTimeout(()=>{ e.style.backgroundColor=''; }, 100); } // Sensor data function getPitch(i) { return parseFloat(el('pitch').children[i].innerText); } function getYaw(i) { return parseFloat(el('yaw').children[i].innerText); } function getRoll(i) { return parseFloat(el('roll').children[i].innerText); } function getRange() { return parseFloat(el('range').children[1].innerText); } function getRangeX() { return parseFloat(el('x-range').children[0].innerText); } function getRangeY() { return parseFloat(el('y-range').children[0].innerText); } function getRangeZ() { return parseFloat(el('z-range').children[0].innerText); } function getRate() { return parseFloat(el('rate').children[1].innerText); } // Controls function moveForward() { e = el('translate-forward-button'); blink(e); e.click(); } function moveBackward() { e = el('translate-backward-button'); blink(e); e.click(); } function moveUp() { e = el('translate-up-button'); blink(e); e.click(); } function moveDown() { e = el('translate-down-button'); blink(e); e.click(); } function moveLeft() { e = el('translate-left-button'); blink(e); e.click(); } function moveRight() { e = el('translate-right-button'); blink(e); e.click(); } function yawLeft() { e = el('yaw-left-button'); blink(e); e.click(); } function yawRight() { e = el('yaw-right-button'); blink(e); e.click(); } function pitchUp() { e = el('pitch-up-button'); blink(e); e.click(); } function pitchDown() { e = el('pitch-down-button'); blink(e); e.click(); } function rollLeft() { e = el('roll-left-button'); blink(e); e.click(); } function rollRight() { e = el('roll-right-button'); blink(e); e.click(); } // Artificial Intelligence var a = 0.1; var dt = 0.100; var cnt = 0; var hist = { yaw: [0, 0, 0], roll: [0, 0, 0], pitch: [0, 0, 0], y: [0, 0, 0], z: [0, 0, 0], r: [0, 0, 0] }; function AI(dt, x, v, a, finc, fdec) { T = dt*Math.abs(v/a); var target = x + v*T - 0.5*Math.sign(v)*a*T*T; setTimeout(target < 0.0 ? finc : target > 0.0 ? fdec : ()=>{}, 0.8*Math.random()*dt*1000); } function v(x) { var i = cnt - 3; if (i < 0) i += 3; var p0 = hist[x][i]; var i = cnt - 1; if (i < 0) i += 3; var p2 = hist[x][i]; return (p2 - p0)/(2*dt); } // Decision tree function update() { okYaw = Math.abs(getYaw(0)) < 2.0; okRoll = Math.abs(getRoll(0)) < 2.0; okPitch = Math.abs(getPitch(0)) < 2.0; hist['yaw'][cnt] = getYaw(0); hist['roll'][cnt] = getRoll(0); hist['pitch'][cnt] = getPitch(0); hist['y'][cnt] = getRangeY(0); hist['z'][cnt] = getRangeZ(0); hist['r'][cnt] = getRange() - 2.0; if (++cnt > 2) cnt = 0; if (okYaw && okRoll && okPitch) { AI(dt, getRangeY(), v('y'), a, moveRight, moveLeft); AI(dt, getRangeZ(), v('z'), a, moveUp, moveDown); if (Math.abs(getRangeY() < 10.0) && Math.abs(getRangeZ() < 10.0)) { if (getRange() > 2.0) { AI(dt, getRange() - 2.0, v('r'), 0.5*a, moveBackward, moveForward); } else { if (getRate() < -0.15) { moveBackward(); } else if (getRate() > -0.05) { moveForward(); } } } } if (okYaw && okRoll && okPitch) { AI(dt, getYaw(0), -getYaw(1), 0.2*a, yawLeft, yawRight); AI(dt, getRoll(0), -getRoll(1), 0.2*a, rollLeft, rollRight); AI(dt, getPitch(0), -getPitch(1), 0.2*a, pitchUp, pitchDown); } else { AI(dt, getYaw(0), v('yaw'), a, yawLeft, yawRight); AI(dt, getRoll(0), v('roll'), a, rollLeft, rollRight); AI(dt, getPitch(0), v('pitch'), a, pitchUp, pitchDown); } setTimeout(update, dt*1000); } update();