Launch to Orbit

Back to General discussions forum

gardengnome     2022-11-06 09:54:27
User avatar

To encourage fellow coders to try this task, below is a python-based implementation that allows you to execute a rocket flight. You still need to write and tune your own flight control program - the example used is the one provided in the text. Graphical output is provided via pyplot. This might make it easier to experiment with different approaches at home.

from math import pi, sin, cos, atan
import matplotlib.pyplot as plt

# constants
R_EARTH = 6_371_000  # radius earth (m)
G = -9.81  # gravity of earth (m/s^2)

M0 = 272_000  # default mass of rocket (kg)
FTH = 3_000_000  # default thrust force (N = kg*m/s^2)
VM = -800  # default fuel burning rate (kg/s)
DT = 1 / 4  # deafult time step (s)

H_TOLERANCE = 20  # height tolerenace (km)
V_TOLERANCE = 200  # velocity tolerance (m/s)
A_TOLERANCE = 1.0  # angle tolerance (degree)


class Rocket:
    ON_GROUND, IN_FLIGHT, CRASH, SHUTDOWN = "on_ground", "in_flight", "crash", "shutdown"

    def __init__(self, mass_init, force_thrust, fuel_burning_rate, time_step, h_target, v_target):
        self.h_target = h_target  # target height (m)
        self.v_target = v_target  # target velocity (m/s)
        self.m0, self.m = mass_init, mass_init  # initial and current mass (kg)
        self.fth = force_thrust  # thrust force (N = kg*m/s^2)
        self.vm = fuel_burning_rate  # fuel burning rate (kg/s)
        self.x, self.y = [0.0], [R_EARTH]  # coordinates (m)
        self.vx, self.vy = 0.0, 0.0  # velocity (m/s)
        self.att = 0.0  # attitude / angle (radiant)
        self.va = 0.0  # attitude velocity
        self.fa = 0.0  # attitude turning force
        self.t, self.dt = 0.0, time_step  # time (s)
        self.stage = 0
        self.off = 0
        self.flight_status = Rocket.ON_GROUND
        self.show_output = False

    def height(self):
        return (self.x[-1] ** 2 + self.y[-1] ** 2) ** 0.5 - R_EARTH

    def velocity(self):
        return (self.vx ** 2 + self.vy ** 2) ** 0.5

    def height_error(self):
        return self.height() - self.h_target

    def velocity_error(self):
        return self.velocity() - self.v_target

    def angle_error(self):
        at1 = atan(self.x[-1] / self.y[-1])
        at2 = atan(self.vx / self.vy) if self.vx < self.vy else atan(self.vy / - self.vx) + pi / 2
        return (at1 + pi / 2 - at2) * 180 / pi  # corrected

    def set_show_output(self, yn):
        self.show_output = yn

    def set_flight_status(self, status):
        self.flight_status = status
        if self.show_output:
            print(f"t={self.t}: {self.flight_status}")

    def fly(self, program=None):
        self.set_flight_status(Rocket.IN_FLIGHT)
        while True:
            self.exec_move()
            if self.flight_status in [Rocket.CRASH, Rocket.SHUTDOWN]:
                break
            self.pilot(program)
            if self.stage:
                self.exec_stage()
            if self.off:
                self.set_flight_status(Rocket.SHUTDOWN)
                break

    def exec_move(self):
        r = (self.x[-1] ** 2 + self.y[-1] ** 2) ** 0.5
        ag = G * (R_EARTH / r) ** 2
        ath = self.fth / self.m
        ax = ag * self.x[-1] / r + ath * sin(self.att)
        ay = ag * self.y[-1] / r + ath * cos(self.att)
        da = 0.001 * (self.m0 / self.m) * self.fa
        self.x += [self.x[-1] + self.vx * self.dt]
        self.y += [self.y[-1] + self.vy * self.dt]
        self.vx += ax * self.dt
        self.vy += ay * self.dt
        self.att += self.va * self.dt
        self.va += da * self.dt
        self.m += self.vm * self.dt
        self.t += self.dt
        if self.show_output and self.t == int(self.t):
            print(f"t={self.t:,.0f}  "
                  f"x={self.x[-1]:,.0f}  y={self.y[-1]:,.0f}  h={self.height():,.0f}  "
                  f"vx={self.vx:,.0f}  vy={self.vy:,.0f}  v={self.velocity():,.0f}  "
                  f"att={self.att:,.2f}  fa={self.fa:,.4f}  m={self.m:,.0f}  "
                  f"h_error={self.height_error():,.0f}  "
                  f"v_error={self.velocity_error():,.0f}  "
                  f"a_error={self.angle_error():,.1f}  ")
        if self.att > pi or self.att < - pi / 4 or self.height() < 0:
            self.set_flight_status(Rocket.CRASH)
        elif self.m <= 10_000:
            self.set_flight_status(Rocket.SHUTDOWN)

    def exec_stage(self):
        if self.vm <= -800:
            self.vm /= 4.0
            self.fth /= 4.0
        if self.show_output:
            print(f"t={self.t}: stage")

    def pilot(self, program=None):
        if program is not None:
            self.stage, self.off = 0, 0
            for i in range(0, len(program), 2):
                tc, cmd = program[i], program[i + 1]
                if tc == self.t:
                    if cmd == "stage":
                        self.stage = 1
                    elif cmd == "off":
                        self.off = 1
                    else:
                        self.fa = min(1.0, max(-1.0, cmd))
                    return
gardengnome     2022-11-06 09:54:57
User avatar

Continued:

    def display_flight(self):
        plt.plot([R_EARTH * cos(2 * pi / 1_000 * i) for i in range(1_000)],
                 [R_EARTH * sin(2 * pi / 1_000 * i) for i in range(1_000)],
                 "g-", label="earth")
        plt.plot([(R_EARTH + self.h_target) * cos(2 * pi / 1_000 * i) for i in range(1_000)],
                 [(R_EARTH + self.h_target) * sin(2 * pi / 1_000 * i) for i in range(1_000)],
                 "b--", label="orbit")
        plt.plot(self.x, self.y, "r-", label="rocket")
        plt.xlim([-500_000, 2_500_000])
        plt.ylim([R_EARTH - 300_000, R_EARTH + 1_000_000])
        plt.title("rocket flight")
        plt.legend()
        plt.show()


# inputs
h_target, v_target = 530, 8200

# ***CHANGE HERE*** manual flight program
program = [10, 1, 30, -0.98, 50, 0, 200, "stage", 210, 0.005, 700, "off"]

# simulate rocket path
rocket = Rocket(M0, FTH, VM, DT, h_target * 1_000, v_target)
rocket.set_show_output(True)
rocket.fly(program)

# results
print()
print(f"flight status: {rocket.flight_status}")
if rocket.flight_status == Rocket.SHUTDOWN:
    h_error = rocket.height_error() / 1_000
    v_error = rocket.velocity_error()
    a_error = rocket.angle_error()
    print(f"h_error: {h_error:.1f} km   {'(ok)' if abs(h_error) <= H_TOLERANCE else '(too large)'}")
    print(f"v_error: {v_error:.1f} m/s   {'(ok)' if abs(v_error) <= V_TOLERANCE else '(too large)'}")
    print(f"a_error: {a_error:.1f} degree   {'(ok)' if abs(a_error) <= A_TOLERANCE else '(too large)'}")
    print("flight program:", *program)

rocket.display_flight()
Rodion (admin)     2022-11-06 11:42:29
User avatar

Well, why shouldn't we have this directly linked from the problem statement? Let's include it!

As of pyplot usage, perhaps it would be interesting in future to add a "notebook" feature for storing arbitrary code snippets in Python (using some JS-based implementation so it is run in browser) with attached chart js library. Though perhaps such things already exist around, I'll check!

Please login and solve 5 problems to be able to post at forum