Back when SpaceX was testing their grasshopper (a test vehicle for relanding the first stage), I thought it'd be fun to do a simple 2D simulation to get an understanding of how Thrust Vector Controlled (TVC) systems work. I chose python and
pygame for ease of programming and visualization .
This is a very nuts and bolts simplistic view of the conversion of GNC (Guidance, Navigation and Controls) abstract concepts of turning the goal of putting a rocket into a certain state into a sequence of controls that cause the rocket to go there.
At the heart of it, the system is controlled by three PD controllers (Proportional and Derivative, a subset of
PID controllers). The first controls throttle based on altitude. The second controls TVC angle based on orientation. The third modifies TVC angle based on lateral drift.
|
By slightly altering the vector of thrust we can control the vehicle's rotation in addition to it's thrust along the axis. |
The rocket is modelled as a simple 5.5x33m box massing 10 tons, with a maximum thrust of 500 kN. A blue line shows the force in the normal direction. Thrust is shown as a red line, which is in the direction dictated by the TVC controllers. In our Python code, we can build a simple PID controller class that contains three values for
Proportional,
Integral, and
Derivative gain.
|
| class PID: |
| def __init__(self, p, i, d): |
| self.p = p |
| self.i = i |
| self.d = d |
| def __str__(self): |
| return "PID(%g,%g,%g)" % (self.p, self.i, self.d) |
|
|
Then, we can set up our three PID controllers we described earlier.
| # PID values [P, I, D] for TVC |
| pid_values = {'altitude': PID(0.3, 0.02, 0.67), |
| 'lateral drift': PID(1.5, 0, 3.55), |
| 'attitude': PID(0.4, 0, 0.2)} |
Pygame provides a nice framework for changing configurations in real-time, sliders etc., I used this to tweak the gains till we found a comfortable response.
In the main loop, we
run our PIDs and use it to update our thrust magnitude and vector based on our current state.
| # PD controller for altitude |
| pos_error = goalPos.y - pos.y |
| vel_error = 0 - self.ship.linearVelocity.y
|
| force_mag = pid_values['altitude'].p*pos_error + \ |
| pid_values['altitude'].d*vel_error + \ |
| self.ship.mass*10 / self.max_tvc_thrust_mult # gravity bias |
|
|
| ######################################## |
| # PD for lateral position to goal orientation |
| lat_error = goalPos.x - pos.x |
| lat_vel_error = 0 - self.ship.linearVelocity.x |
|
|
| tvc_offset = (lat_error*pid_values['lateral drift'].p + \ |
| lat_vel_error*pid_values['lateral drift'].d) * -pi/180.0 |
|
|
| ######################################## |
| # PD controller for TVC |
| ang_error = goal_orientation - (self.ship.angle) |
| ang_vel_error = 0 - self.ship.angularVelocity |
|
|
| d_angle = pid_values['attitude'].p * ang_error + \ |
| pid_values['attitude'].d * ang_vel_error |
|
|
Finally, to set goals we can click points within the area which the ship will attempt to hover over to. For fun, it also automatically resets to a random goal position when it reaches, so that it keeps moving around continuously.