from PIL import Image, ImageDraw, ImageFont
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import os
Launch Video Analysis¶
Since the IMU saturated during launch, let's see if we can get more information from the video. We have high-speed video from the launch, which we will use to estimate the apogee and time of flight of the rocket.
# Read the timestamps of each frame from the previously stored export file
timestamps = pd.read_csv("cansat-slowmo-full/snippet.csv")
timestamps
| frame_index | timestamp | |
|---|---|---|
| 0 | 1 | 28.560000 |
| 1 | 2 | 28.564167 |
| 2 | 3 | 28.576667 |
| 3 | 4 | 28.580833 |
| 4 | 5 | 28.585000 |
| ... | ... | ... |
| 301 | 302 | 30.397917 |
| 302 | 303 | 30.402083 |
| 303 | 304 | 30.414583 |
| 304 | 305 | 30.418750 |
| 305 | 306 | 30.443750 |
306 rows × 2 columns
# The first sign of movement was in frame 268 - so we use the mean of timestamp from frame 267 and 268 as the launch time
launch_time = (timestamps.loc[timestamps['frame_index'] == 267, 'timestamp'].values[0] +
timestamps.loc[timestamps['frame_index'] == 268, 'timestamp'].values[0]) / 2
timestamps['mission_time'] = timestamps['timestamp'] - launch_time
timestamps['dt'] = timestamps['mission_time'].diff().fillna(0)
timestamps
| frame_index | timestamp | mission_time | dt | |
|---|---|---|---|---|
| 0 | 1 | 28.560000 | -1.631667 | 0.000000 |
| 1 | 2 | 28.564167 | -1.627500 | 0.004167 |
| 2 | 3 | 28.576667 | -1.615000 | 0.012500 |
| 3 | 4 | 28.580833 | -1.610834 | 0.004166 |
| 4 | 5 | 28.585000 | -1.606667 | 0.004167 |
| ... | ... | ... | ... | ... |
| 301 | 302 | 30.397917 | 0.206250 | 0.004167 |
| 302 | 303 | 30.402083 | 0.210416 | 0.004166 |
| 303 | 304 | 30.414583 | 0.222916 | 0.012500 |
| 304 | 305 | 30.418750 | 0.227083 | 0.004167 |
| 305 | 306 | 30.443750 | 0.252083 | 0.025000 |
306 rows × 4 columns
ts_records = timestamps.set_index('frame_index').to_dict(orient="records")
ts_records
[{'timestamp': 28.56, 'mission_time': -1.6316670000000038, 'dt': 0.0},
{'timestamp': 28.564167,
'mission_time': -1.6275000000000013,
'dt': 0.004167000000002474},
{'timestamp': 28.576667,
'mission_time': -1.615000000000002,
'dt': 0.01249999999999929},
{'timestamp': 28.580833,
'mission_time': -1.610834000000004,
'dt': 0.004165999999997894},
{'timestamp': 28.585,
'mission_time': -1.6066670000000016,
'dt': 0.004167000000002474},
{'timestamp': 28.589167,
'mission_time': -1.6025000000000027,
'dt': 0.004166999999998922},
{'timestamp': 28.593333,
'mission_time': -1.5983340000000013,
'dt': 0.004166000000001446},
{'timestamp': 28.5975,
'mission_time': -1.5941670000000023,
'dt': 0.004166999999998922},
{'timestamp': 28.601667,
'mission_time': -1.5900000000000034,
'dt': 0.004166999999998922},
{'timestamp': 28.605833,
'mission_time': -1.585834000000002,
'dt': 0.004166000000001446},
{'timestamp': 28.61,
'mission_time': -1.581667000000003,
'dt': 0.004166999999998922},
{'timestamp': 28.614167,
'mission_time': -1.5775000000000041,
'dt': 0.004166999999998922},
{'timestamp': 28.618333,
'mission_time': -1.5733340000000027,
'dt': 0.004166000000001446},
{'timestamp': 28.630833,
'mission_time': -1.5608340000000034,
'dt': 0.01249999999999929},
{'timestamp': 28.635,
'mission_time': -1.556667000000001,
'dt': 0.004167000000002474},
{'timestamp': 28.6475,
'mission_time': -1.5441670000000016,
'dt': 0.01249999999999929},
{'timestamp': 28.651667,
'mission_time': -1.5400000000000027,
'dt': 0.004166999999998922},
{'timestamp': 28.664167,
'mission_time': -1.5275000000000034,
'dt': 0.01249999999999929},
{'timestamp': 28.668333,
'mission_time': -1.523334000000002,
'dt': 0.004166000000001446},
{'timestamp': 28.6725,
'mission_time': -1.519167000000003,
'dt': 0.004166999999998922},
{'timestamp': 28.685,
'mission_time': -1.5066670000000038,
'dt': 0.01249999999999929},
{'timestamp': 28.689167,
'mission_time': -1.5025000000000013,
'dt': 0.004167000000002474},
{'timestamp': 28.701667,
'mission_time': -1.490000000000002,
'dt': 0.01249999999999929},
{'timestamp': 28.705833,
'mission_time': -1.485834000000004,
'dt': 0.004165999999997894},
{'timestamp': 28.71,
'mission_time': -1.4816670000000016,
'dt': 0.004167000000002474},
{'timestamp': 28.714167,
'mission_time': -1.4775000000000027,
'dt': 0.004166999999998922},
{'timestamp': 28.718333,
'mission_time': -1.4733340000000013,
'dt': 0.004166000000001446},
{'timestamp': 28.7225,
'mission_time': -1.4691670000000023,
'dt': 0.004166999999998922},
{'timestamp': 28.726667,
'mission_time': -1.4650000000000034,
'dt': 0.004166999999998922},
{'timestamp': 28.730833,
'mission_time': -1.460834000000002,
'dt': 0.004166000000001446},
{'timestamp': 28.735,
'mission_time': -1.456667000000003,
'dt': 0.004166999999998922},
{'timestamp': 28.739167,
'mission_time': -1.4525000000000041,
'dt': 0.004166999999998922},
{'timestamp': 28.743333,
'mission_time': -1.4483340000000027,
'dt': 0.004166000000001446},
{'timestamp': 28.7475,
'mission_time': -1.4441670000000038,
'dt': 0.004166999999998922},
{'timestamp': 28.76,
'mission_time': -1.431667000000001,
'dt': 0.012500000000002842},
{'timestamp': 28.764167,
'mission_time': -1.427500000000002,
'dt': 0.004166999999998922},
{'timestamp': 28.776667,
'mission_time': -1.4150000000000027,
'dt': 0.01249999999999929},
{'timestamp': 28.780833,
'mission_time': -1.4108340000000013,
'dt': 0.004166000000001446},
{'timestamp': 28.785,
'mission_time': -1.4066670000000023,
'dt': 0.004166999999998922},
{'timestamp': 28.801667,
'mission_time': -1.3900000000000041,
'dt': 0.01666699999999821},
{'timestamp': 28.80625,
'mission_time': -1.385417000000004,
'dt': 0.004583000000000226},
{'timestamp': 28.810417,
'mission_time': -1.3812500000000014,
'dt': 0.004167000000002474},
{'timestamp': 28.822917,
'mission_time': -1.3687500000000021,
'dt': 0.01249999999999929},
{'timestamp': 28.827083,
'mission_time': -1.3645840000000042,
'dt': 0.004165999999997894},
{'timestamp': 28.830833,
'mission_time': -1.360834000000004,
'dt': 0.003750000000000142},
{'timestamp': 28.835,
'mission_time': -1.3566670000000016,
'dt': 0.004167000000002474},
{'timestamp': 28.839167,
'mission_time': -1.3525000000000027,
'dt': 0.004166999999998922},
{'timestamp': 28.843333,
'mission_time': -1.3483340000000013,
'dt': 0.004166000000001446},
{'timestamp': 28.8475,
'mission_time': -1.3441670000000023,
'dt': 0.004166999999998922},
{'timestamp': 28.851667,
'mission_time': -1.3400000000000034,
'dt': 0.004166999999998922},
{'timestamp': 28.855833,
'mission_time': -1.335834000000002,
'dt': 0.004166000000001446},
{'timestamp': 28.86,
'mission_time': -1.331667000000003,
'dt': 0.004166999999998922},
{'timestamp': 28.864583,
'mission_time': -1.3270840000000028,
'dt': 0.004583000000000226},
{'timestamp': 28.86875,
'mission_time': -1.322917000000004,
'dt': 0.004166999999998922},
{'timestamp': 28.88125,
'mission_time': -1.310417000000001,
'dt': 0.012500000000002842},
{'timestamp': 28.885,
'mission_time': -1.306667000000001,
'dt': 0.003750000000000142},
{'timestamp': 28.8975,
'mission_time': -1.2941670000000016,
'dt': 0.01249999999999929},
{'timestamp': 28.901667,
'mission_time': -1.2900000000000027,
'dt': 0.004166999999998922},
{'timestamp': 28.905833,
'mission_time': -1.2858340000000013,
'dt': 0.004166000000001446},
{'timestamp': 28.918333,
'mission_time': -1.273334000000002,
'dt': 0.01249999999999929},
{'timestamp': 28.9225,
'mission_time': -1.269167000000003,
'dt': 0.004166999999998922},
{'timestamp': 28.935417,
'mission_time': -1.2562500000000014,
'dt': 0.012917000000001622},
{'timestamp': 28.939583,
'mission_time': -1.2520840000000035,
'dt': 0.004165999999997894},
{'timestamp': 28.94375,
'mission_time': -1.247917000000001,
'dt': 0.004167000000002474},
{'timestamp': 28.95625,
'mission_time': -1.2354170000000018,
'dt': 0.01249999999999929},
{'timestamp': 28.960417,
'mission_time': -1.2312500000000028,
'dt': 0.004166999999998922},
{'timestamp': 28.964583,
'mission_time': -1.2270840000000014,
'dt': 0.004166000000001446},
{'timestamp': 28.96875,
'mission_time': -1.2229170000000025,
'dt': 0.004166999999998922},
{'timestamp': 28.972917,
'mission_time': -1.2187500000000036,
'dt': 0.004166999999998922},
{'timestamp': 28.977083,
'mission_time': -1.214584000000002,
'dt': 0.004166000000001446},
{'timestamp': 28.98125,
'mission_time': -1.2104170000000032,
'dt': 0.004166999999998922},
{'timestamp': 28.985417,
'mission_time': -1.2062500000000007,
'dt': 0.004167000000002474},
{'timestamp': 28.989583,
'mission_time': -1.2020840000000028,
'dt': 0.004165999999997894},
{'timestamp': 28.99375,
'mission_time': -1.197917000000004,
'dt': 0.004166999999998922},
{'timestamp': 28.997917,
'mission_time': -1.1937500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.002083,
'mission_time': -1.1895840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.014583,
'mission_time': -1.1770840000000042,
'dt': 0.01249999999999929},
{'timestamp': 29.01875,
'mission_time': -1.1729170000000018,
'dt': 0.004167000000002474},
{'timestamp': 29.03125,
'mission_time': -1.1604170000000025,
'dt': 0.01249999999999929},
{'timestamp': 29.035417,
'mission_time': -1.1562500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.039583,
'mission_time': -1.152084000000002,
'dt': 0.004166000000001446},
{'timestamp': 29.052083,
'mission_time': -1.1395840000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.05625,
'mission_time': -1.135417000000004,
'dt': 0.004166999999998922},
{'timestamp': 29.06875,
'mission_time': -1.122917000000001,
'dt': 0.012500000000002842},
{'timestamp': 29.072917,
'mission_time': -1.1187500000000021,
'dt': 0.004166999999998922},
{'timestamp': 29.077083,
'mission_time': -1.1145840000000042,
'dt': 0.004165999999997894},
{'timestamp': 29.089583,
'mission_time': -1.1020840000000014,
'dt': 0.012500000000002842},
{'timestamp': 29.09375,
'mission_time': -1.0979170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.097917,
'mission_time': -1.0937500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.102083,
'mission_time': -1.089584000000002,
'dt': 0.004166000000001446},
{'timestamp': 29.10625,
'mission_time': -1.0854170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.110417,
'mission_time': -1.0812500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.114583,
'mission_time': -1.0770840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.11875,
'mission_time': -1.072917000000004,
'dt': 0.004166999999998922},
{'timestamp': 29.122917,
'mission_time': -1.0687500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.127083,
'mission_time': -1.0645840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.139583,
'mission_time': -1.0520840000000042,
'dt': 0.01249999999999929},
{'timestamp': 29.14375,
'mission_time': -1.0479170000000018,
'dt': 0.004167000000002474},
{'timestamp': 29.15625,
'mission_time': -1.0354170000000025,
'dt': 0.01249999999999929},
{'timestamp': 29.160417,
'mission_time': -1.0312500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.172917,
'mission_time': -1.0187500000000007,
'dt': 0.012500000000002842},
{'timestamp': 29.177083,
'mission_time': -1.0145840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.18125,
'mission_time': -1.010417000000004,
'dt': 0.004166999999998922},
{'timestamp': 29.19375,
'mission_time': -0.997917000000001,
'dt': 0.012500000000002842},
{'timestamp': 29.197917,
'mission_time': -0.9937500000000021,
'dt': 0.004166999999998922},
{'timestamp': 29.210417,
'mission_time': -0.9812500000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.214583,
'mission_time': -0.9770840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.21875,
'mission_time': -0.9729170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.222917,
'mission_time': -0.9687500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.227083,
'mission_time': -0.9645840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.23125,
'mission_time': -0.9604170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.235417,
'mission_time': -0.9562500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.239583,
'mission_time': -0.9520840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.24375,
'mission_time': -0.9479170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.247917,
'mission_time': -0.9437500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.252083,
'mission_time': -0.9395840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.264583,
'mission_time': -0.9270840000000042,
'dt': 0.01249999999999929},
{'timestamp': 29.26875,
'mission_time': -0.9229170000000018,
'dt': 0.004167000000002474},
{'timestamp': 29.272917,
'mission_time': -0.9187500000000028,
'dt': 0.004166999999998922},
{'timestamp': 29.285417,
'mission_time': -0.9062500000000036,
'dt': 0.01249999999999929},
{'timestamp': 29.289583,
'mission_time': -0.9020840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.302083,
'mission_time': -0.8895840000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.30625,
'mission_time': -0.8854170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.31875,
'mission_time': -0.872917000000001,
'dt': 0.012500000000002842},
{'timestamp': 29.322917,
'mission_time': -0.8687500000000021,
'dt': 0.004166999999998922},
{'timestamp': 29.327083,
'mission_time': -0.8645840000000042,
'dt': 0.004165999999997894},
{'timestamp': 29.339583,
'mission_time': -0.8520840000000014,
'dt': 0.012500000000002842},
{'timestamp': 29.34375,
'mission_time': -0.8479170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.347917,
'mission_time': -0.8437500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.352083,
'mission_time': -0.8395840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.35625,
'mission_time': -0.8354170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.360417,
'mission_time': -0.8312500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.364583,
'mission_time': -0.8270840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.36875,
'mission_time': -0.8229170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.372917,
'mission_time': -0.8187500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.377083,
'mission_time': -0.8145840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.38125,
'mission_time': -0.810417000000001,
'dt': 0.004167000000002474},
{'timestamp': 29.385417,
'mission_time': -0.8062500000000021,
'dt': 0.004166999999998922},
{'timestamp': 29.397917,
'mission_time': -0.7937500000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.402083,
'mission_time': -0.7895840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.414583,
'mission_time': -0.7770840000000021,
'dt': 0.01249999999999929},
{'timestamp': 29.41875,
'mission_time': -0.7729170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.422917,
'mission_time': -0.7687500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.435417,
'mission_time': -0.7562500000000014,
'dt': 0.01249999999999929},
{'timestamp': 29.439583,
'mission_time': -0.7520840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.452083,
'mission_time': -0.7395840000000042,
'dt': 0.01249999999999929},
{'timestamp': 29.45625,
'mission_time': -0.7354170000000018,
'dt': 0.004167000000002474},
{'timestamp': 29.460417,
'mission_time': -0.7312500000000028,
'dt': 0.004166999999998922},
{'timestamp': 29.464583,
'mission_time': -0.7270840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.46875,
'mission_time': -0.7229170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.472917,
'mission_time': -0.7187500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.477083,
'mission_time': -0.7145840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.48125,
'mission_time': -0.7104170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.485417,
'mission_time': -0.7062500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.489583,
'mission_time': -0.7020840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.49375,
'mission_time': -0.6979170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.50625,
'mission_time': -0.685417000000001,
'dt': 0.012500000000002842},
{'timestamp': 29.510417,
'mission_time': -0.6812500000000021,
'dt': 0.004166999999998922},
{'timestamp': 29.522917,
'mission_time': -0.6687500000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.527083,
'mission_time': -0.6645840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.53125,
'mission_time': -0.6604170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.54375,
'mission_time': -0.6479170000000032,
'dt': 0.01249999999999929},
{'timestamp': 29.547917,
'mission_time': -0.6437500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.560417,
'mission_time': -0.6312500000000014,
'dt': 0.01249999999999929},
{'timestamp': 29.564583,
'mission_time': -0.6270840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.56875,
'mission_time': -0.622917000000001,
'dt': 0.004167000000002474},
{'timestamp': 29.585417,
'mission_time': -0.6062500000000028,
'dt': 0.01666699999999821},
{'timestamp': 29.589583,
'mission_time': -0.6020840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.59375,
'mission_time': -0.5979170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.597917,
'mission_time': -0.5937500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.602083,
'mission_time': -0.5895840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.60625,
'mission_time': -0.5854170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.610417,
'mission_time': -0.5812500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.614583,
'mission_time': -0.5770840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.61875,
'mission_time': -0.5729170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.622917,
'mission_time': -0.5687500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.627083,
'mission_time': -0.5645840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.639583,
'mission_time': -0.5520840000000042,
'dt': 0.01249999999999929},
{'timestamp': 29.64375,
'mission_time': -0.5479170000000018,
'dt': 0.004167000000002474},
{'timestamp': 29.65625,
'mission_time': -0.5354170000000025,
'dt': 0.01249999999999929},
{'timestamp': 29.660417,
'mission_time': -0.5312500000000036,
'dt': 0.004166999999998922},
{'timestamp': 29.664583,
'mission_time': -0.5270840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.677083,
'mission_time': -0.5145840000000028,
'dt': 0.01249999999999929},
{'timestamp': 29.68125,
'mission_time': -0.5104170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.69375,
'mission_time': -0.49791700000000105,
'dt': 0.012500000000002842},
{'timestamp': 29.697917,
'mission_time': -0.49375000000000213,
'dt': 0.004166999999998922},
{'timestamp': 29.702083,
'mission_time': -0.48958400000000424,
'dt': 0.004165999999997894},
{'timestamp': 29.71875,
'mission_time': -0.4729170000000025,
'dt': 0.016667000000001764},
{'timestamp': 29.722917,
'mission_time': -0.46875000000000355,
'dt': 0.004166999999998922},
{'timestamp': 29.727083,
'mission_time': -0.4645840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.73125,
'mission_time': -0.4604170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.735417,
'mission_time': -0.4562500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.739583,
'mission_time': -0.4520840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.74375,
'mission_time': -0.4479170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.747917,
'mission_time': -0.4437500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.752083,
'mission_time': -0.4395840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.75625,
'mission_time': -0.43541700000000105,
'dt': 0.004167000000002474},
{'timestamp': 29.760417,
'mission_time': -0.43125000000000213,
'dt': 0.004166999999998922},
{'timestamp': 29.772917,
'mission_time': -0.41875000000000284,
'dt': 0.01249999999999929},
{'timestamp': 29.777083,
'mission_time': -0.4145840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.78125,
'mission_time': -0.4104170000000025,
'dt': 0.004166999999998922},
{'timestamp': 29.79375,
'mission_time': -0.3979170000000032,
'dt': 0.01249999999999929},
{'timestamp': 29.797917,
'mission_time': -0.3937500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.810417,
'mission_time': -0.3812500000000014,
'dt': 0.01249999999999929},
{'timestamp': 29.814583,
'mission_time': -0.3770840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.81875,
'mission_time': -0.37291700000000105,
'dt': 0.004167000000002474},
{'timestamp': 29.83125,
'mission_time': -0.36041700000000176,
'dt': 0.01249999999999929},
{'timestamp': 29.835417,
'mission_time': -0.35625000000000284,
'dt': 0.004166999999998922},
{'timestamp': 29.847917,
'mission_time': -0.34375000000000355,
'dt': 0.01249999999999929},
{'timestamp': 29.852083,
'mission_time': -0.3395840000000021,
'dt': 0.004166000000001446},
{'timestamp': 29.85625,
'mission_time': -0.3354170000000032,
'dt': 0.004166999999998922},
{'timestamp': 29.860417,
'mission_time': -0.3312500000000007,
'dt': 0.004167000000002474},
{'timestamp': 29.864583,
'mission_time': -0.3270840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.86875,
'mission_time': -0.3229170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.872917,
'mission_time': -0.3187500000000014,
'dt': 0.004167000000002474},
{'timestamp': 29.877083,
'mission_time': -0.3145840000000035,
'dt': 0.004165999999997894},
{'timestamp': 29.88125,
'mission_time': -0.31041700000000105,
'dt': 0.004167000000002474},
{'timestamp': 29.885417,
'mission_time': -0.30625000000000213,
'dt': 0.004166999999998922},
{'timestamp': 29.889583,
'mission_time': -0.30208400000000424,
'dt': 0.004165999999997894},
{'timestamp': 29.89375,
'mission_time': -0.29791700000000176,
'dt': 0.004167000000002474},
{'timestamp': 29.90625,
'mission_time': -0.2854170000000025,
'dt': 0.01249999999999929},
{'timestamp': 29.910417,
'mission_time': -0.28125000000000355,
'dt': 0.004166999999998922},
{'timestamp': 29.922917,
'mission_time': -0.2687500000000007,
'dt': 0.012500000000002842},
{'timestamp': 29.927083,
'mission_time': -0.2645840000000028,
'dt': 0.004165999999997894},
{'timestamp': 29.93125,
'mission_time': -0.2604170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.94375,
'mission_time': -0.24791700000000105,
'dt': 0.012500000000002842},
{'timestamp': 29.947917,
'mission_time': -0.24375000000000213,
'dt': 0.004166999999998922},
{'timestamp': 29.960417,
'mission_time': -0.23125000000000284,
'dt': 0.01249999999999929},
{'timestamp': 29.964583,
'mission_time': -0.2270840000000014,
'dt': 0.004166000000001446},
{'timestamp': 29.96875,
'mission_time': -0.22291700000000247,
'dt': 0.004166999999998922},
{'timestamp': 29.972917,
'mission_time': -0.21875000000000355,
'dt': 0.004166999999998922},
{'timestamp': 29.985417,
'mission_time': -0.2062500000000007,
'dt': 0.012500000000002842},
{'timestamp': 29.989583,
'mission_time': -0.20208400000000282,
'dt': 0.004165999999997894},
{'timestamp': 29.99375,
'mission_time': -0.1979170000000039,
'dt': 0.004166999999998922},
{'timestamp': 29.997917,
'mission_time': -0.19375000000000142,
'dt': 0.004167000000002474},
{'timestamp': 30.002083,
'mission_time': -0.18958400000000353,
'dt': 0.004165999999997894},
{'timestamp': 30.00625,
'mission_time': -0.18541700000000105,
'dt': 0.004167000000002474},
{'timestamp': 30.010417,
'mission_time': -0.18125000000000213,
'dt': 0.004166999999998922},
{'timestamp': 30.014583,
'mission_time': -0.17708400000000424,
'dt': 0.004165999999997894},
{'timestamp': 30.01875,
'mission_time': -0.17291700000000176,
'dt': 0.004167000000002474},
{'timestamp': 30.022917,
'mission_time': -0.16875000000000284,
'dt': 0.004166999999998922},
{'timestamp': 30.035417,
'mission_time': -0.15625000000000355,
'dt': 0.01249999999999929},
{'timestamp': 30.039583,
'mission_time': -0.1520840000000021,
'dt': 0.004166000000001446},
{'timestamp': 30.04375,
'mission_time': -0.14791700000000318,
'dt': 0.004166999999998922},
{'timestamp': 30.060417,
'mission_time': -0.13125000000000142,
'dt': 0.016667000000001764},
{'timestamp': 30.064583,
'mission_time': -0.12708400000000353,
'dt': 0.004165999999997894},
{'timestamp': 30.06875,
'mission_time': -0.12291700000000105,
'dt': 0.004167000000002474},
{'timestamp': 30.08125,
'mission_time': -0.11041700000000176,
'dt': 0.01249999999999929},
{'timestamp': 30.085417,
'mission_time': -0.10625000000000284,
'dt': 0.004166999999998922},
{'timestamp': 30.089583,
'mission_time': -0.1020840000000014,
'dt': 0.004166000000001446},
{'timestamp': 30.102083,
'mission_time': -0.0895840000000021,
'dt': 0.01249999999999929},
{'timestamp': 30.10625,
'mission_time': -0.08541700000000318,
'dt': 0.004166999999998922},
{'timestamp': 30.110417,
'mission_time': -0.08125000000000071,
'dt': 0.004167000000002474},
{'timestamp': 30.114583,
'mission_time': -0.07708400000000282,
'dt': 0.004165999999997894},
{'timestamp': 30.11875,
'mission_time': -0.0729170000000039,
'dt': 0.004166999999998922},
{'timestamp': 30.122917,
'mission_time': -0.06875000000000142,
'dt': 0.004167000000002474},
{'timestamp': 30.127083,
'mission_time': -0.06458400000000353,
'dt': 0.004165999999997894},
{'timestamp': 30.13125,
'mission_time': -0.06041700000000105,
'dt': 0.004167000000002474},
{'timestamp': 30.135417,
'mission_time': -0.05625000000000213,
'dt': 0.004166999999998922},
{'timestamp': 30.139583,
'mission_time': -0.05208400000000424,
'dt': 0.004165999999997894},
{'timestamp': 30.14375,
'mission_time': -0.047917000000001764,
'dt': 0.004167000000002474},
{'timestamp': 30.147917,
'mission_time': -0.04375000000000284,
'dt': 0.004166999999998922},
{'timestamp': 30.160417,
'mission_time': -0.03125000000000355,
'dt': 0.01249999999999929},
{'timestamp': 30.164583,
'mission_time': -0.027084000000002106,
'dt': 0.004166000000001446},
{'timestamp': 30.16875,
'mission_time': -0.022917000000003185,
'dt': 0.004166999999998922},
{'timestamp': 30.18125,
'mission_time': -0.010417000000003895,
'dt': 0.01249999999999929},
{'timestamp': 30.185417,
'mission_time': -0.006250000000001421,
'dt': 0.004167000000002474},
{'timestamp': 30.197917,
'mission_time': 0.006249999999997868,
'dt': 0.01249999999999929},
{'timestamp': 30.202083,
'mission_time': 0.010415999999995762,
'dt': 0.004165999999997894},
{'timestamp': 30.214583,
'mission_time': 0.022915999999998604,
'dt': 0.012500000000002842},
{'timestamp': 30.21875,
'mission_time': 0.027082999999997526,
'dt': 0.004166999999998922},
{'timestamp': 30.222917,
'mission_time': 0.031249999999996447,
'dt': 0.004166999999998922},
{'timestamp': 30.235417,
'mission_time': 0.04374999999999929,
'dt': 0.012500000000002842},
{'timestamp': 30.239583,
'mission_time': 0.04791599999999718,
'dt': 0.004165999999997894},
{'timestamp': 30.24375,
'mission_time': 0.052082999999996105,
'dt': 0.004166999999998922},
{'timestamp': 30.247917,
'mission_time': 0.05624999999999858,
'dt': 0.004167000000002474},
{'timestamp': 30.252083,
'mission_time': 0.06041599999999647,
'dt': 0.004165999999997894},
{'timestamp': 30.25625,
'mission_time': 0.06458299999999895,
'dt': 0.004167000000002474},
{'timestamp': 30.260417,
'mission_time': 0.06874999999999787,
'dt': 0.004166999999998922},
{'timestamp': 30.264583,
'mission_time': 0.07291599999999576,
'dt': 0.004165999999997894},
{'timestamp': 30.26875,
'mission_time': 0.07708299999999824,
'dt': 0.004167000000002474},
{'timestamp': 30.272917,
'mission_time': 0.08124999999999716,
'dt': 0.004166999999998922},
{'timestamp': 30.277083,
'mission_time': 0.0854159999999986,
'dt': 0.004166000000001446},
{'timestamp': 30.28125,
'mission_time': 0.08958299999999753,
'dt': 0.004166999999998922},
{'timestamp': 30.29375,
'mission_time': 0.10208299999999682,
'dt': 0.01249999999999929},
{'timestamp': 30.297917,
'mission_time': 0.10624999999999929,
'dt': 0.004167000000002474},
{'timestamp': 30.302083,
'mission_time': 0.11041599999999718,
'dt': 0.004165999999997894},
{'timestamp': 30.314583,
'mission_time': 0.12291599999999647,
'dt': 0.01249999999999929},
{'timestamp': 30.31875,
'mission_time': 0.12708299999999895,
'dt': 0.004167000000002474},
{'timestamp': 30.33125,
'mission_time': 0.13958299999999824,
'dt': 0.01249999999999929},
{'timestamp': 30.335417,
'mission_time': 0.14374999999999716,
'dt': 0.004166999999998922},
{'timestamp': 30.339583,
'mission_time': 0.1479159999999986,
'dt': 0.004166000000001446},
{'timestamp': 30.352083,
'mission_time': 0.1604159999999979,
'dt': 0.01249999999999929},
{'timestamp': 30.35625,
'mission_time': 0.16458299999999682,
'dt': 0.004166999999998922},
{'timestamp': 30.36875,
'mission_time': 0.1770829999999961,
'dt': 0.01249999999999929},
{'timestamp': 30.372917,
'mission_time': 0.18124999999999858,
'dt': 0.004167000000002474},
{'timestamp': 30.377083,
'mission_time': 0.18541599999999647,
'dt': 0.004165999999997894},
{'timestamp': 30.38125,
'mission_time': 0.18958299999999895,
'dt': 0.004167000000002474},
{'timestamp': 30.385417,
'mission_time': 0.19374999999999787,
'dt': 0.004166999999998922},
{'timestamp': 30.389583,
'mission_time': 0.19791599999999576,
'dt': 0.004165999999997894},
{'timestamp': 30.39375,
'mission_time': 0.20208299999999824,
'dt': 0.004167000000002474},
{'timestamp': 30.397917,
'mission_time': 0.20624999999999716,
'dt': 0.004166999999998922},
{'timestamp': 30.402083,
'mission_time': 0.2104159999999986,
'dt': 0.004166000000001446},
{'timestamp': 30.414583,
'mission_time': 0.2229159999999979,
'dt': 0.01249999999999929},
{'timestamp': 30.41875,
'mission_time': 0.22708299999999682,
'dt': 0.004166999999998922},
{'timestamp': 30.44375,
'mission_time': 0.25208299999999895,
'dt': 0.02500000000000213}]
start_image = 268
end_image = 288
images = [
Image.open(f'cansat-slowmo-full/frame_{i:04d}.png')
for i in range(start_image, end_image+1)
]
cropped_images = []
for i in range(len(images)):
row = ts_records[start_image + i - 1]
ts = row['mission_time']
left = 850 + ts * 350
right = left + 100
image = images[i]
image = image.crop((left, 0, right, image.height))
cropped_images.append(dict(image=image, row=row, left=left, right=right, frame_index=start_image + i - 1))
scaling = 20_000
num_images = len(cropped_images)
max_time = timestamps[timestamps['frame_index'] <= end_image]['mission_time'].max()
total_width = scaling * max_time
total_height = cropped_images[0]['image'].height
combined_image = Image.new('RGB', (int(total_width), total_height))
for i, img in enumerate(cropped_images):
row = img['row']
ts = row['mission_time']
left = ts*scaling - 125
img['paste_left'] = left
combined_image.paste(img['image'], (int(left), 0))
combined_image.save("060-launch-analysis-no-labels.png")
font = ImageFont.truetype("freefont/FreeSansBold.ttf", size=24)
draw = ImageDraw.Draw(combined_image)
# Add labels
for i, img in enumerate(cropped_images):
row = img['row']
ts = row['mission_time']
dt = row['dt']
frame_index = img['frame_index']
draw.text((20+img['paste_left'], 930), f"{ts:.3f}", fill="white", font=font)
draw.text((20+img['paste_left'], 960), f"{dt:.3f}", fill="white", font=font)
draw.text((20+img['paste_left'], 990), f"{frame_index}", fill="white", font=font)
# Add lines
line_spacing = 50 # Adjust the spacing as needed
for y in range(0, combined_image.height-150, line_spacing):
draw.line([(0, y), (combined_image.width, y)], fill="white", width=1)
combined_image.save("060-launch-analysis.png")
combined_image
Correcting for perspective distortion¶
In the image above we see that the bottle towards the top of the image is smaller than the bottle towards the bottom of the image. This is due to perspective distortion.
We can use the above image to measure the height of the bottle in each slice and then use this to correct for perspective distortion.
To do this I use a tool called Napari to manually measure the height of the bottle in each slice and save these as a csv file.
from collections import defaultdict
measurements = pd.read_csv("060-launch-analysis-measurements.csv").to_dict(orient="records")
mission_times = timestamps['mission_time'].to_dict()
lines = defaultdict(dict)
for m in measurements:
lines[m['index']][m['vertex-index']] = m
def to_line_data(l, ix):
p0 = l[0]
p1 = l[1]
# length = np.sqrt((p0['axis-1'] - p1['axis-1'])**2 + (p0['axis-0'] - p1['axis-0'])**2)
length = p1['axis-0'] - p0['axis-0'] # We really only consider the vertical length here
centre = (p0['axis-1'] + p1['axis-1']) / 2, (p0['axis-0'] + p1['axis-0']) / 2
frame_id = ix + 267
return {
'line_id': ix,
'y0': p0['axis-0'],
'x0': p0['axis-1'],
'y1': p1['axis-0'],
'x1': p1['axis-1'],
'yc': centre[1],
'length': length,
'centre': centre,
'frame_id': frame_id,
'time': mission_times[frame_id],
}
lines =[to_line_data(l, ix) for ix, l in lines.items()]
line_df = pd.DataFrame(lines)
line_df
| line_id | y0 | x0 | y1 | x1 | yc | length | centre | frame_id | time | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 735.359000 | 50.431026 | 924.83594 | 42.816807 | 830.097470 | 189.476940 | (46.6239165, 830.09747) | 267 | 0.006250 |
| 1 | 1 | 733.309100 | 131.258880 | 927.47144 | 124.688470 | 830.390270 | 194.162340 | (127.973675, 830.39027) | 268 | 0.010416 |
| 2 | 2 | 719.544860 | 377.842400 | 913.12180 | 370.813960 | 816.333330 | 193.576940 | (374.32818, 816.3333299999999) | 269 | 0.022916 |
| 3 | 3 | 712.809270 | 461.013100 | 906.38600 | 454.277200 | 809.597635 | 193.576730 | (457.64515, 809.597635) | 270 | 0.027083 |
| 4 | 4 | 702.391850 | 543.625900 | 893.36615 | 535.544560 | 797.879000 | 190.974300 | (539.58523, 797.8789999999999) | 271 | 0.031250 |
| 5 | 5 | 651.679500 | 791.860350 | 839.42633 | 781.480040 | 745.552915 | 187.746830 | (786.670195, 745.552915) | 272 | 0.043750 |
| 6 | 6 | 629.113830 | 876.256160 | 815.95776 | 864.973300 | 722.535795 | 186.843930 | (870.61473, 722.535795) | 273 | 0.047916 |
| 7 | 7 | 602.486270 | 959.749300 | 787.07400 | 950.722800 | 694.780135 | 184.587730 | (955.23605, 694.780135) | 274 | 0.052083 |
| 8 | 8 | 572.699500 | 1040.985800 | 754.12790 | 1031.508200 | 663.413700 | 181.428400 | (1036.2469999999998, 663.4137) | 275 | 0.056250 |
| 9 | 9 | 537.948300 | 1127.186900 | 718.02280 | 1117.709700 | 627.985550 | 180.074500 | (1122.4483, 627.98555) | 276 | 0.060416 |
| 10 | 10 | 500.489230 | 1210.680000 | 677.85596 | 1200.299800 | 589.172595 | 177.366730 | (1205.4899, 589.172595) | 277 | 0.064583 |
| 11 | 11 | 461.022400 | 1296.229000 | 638.00410 | 1285.119000 | 549.513250 | 176.981700 | (1290.674, 549.51325) | 278 | 0.068750 |
| 12 | 12 | 420.033100 | 1381.272200 | 589.73650 | 1370.163100 | 504.884800 | 169.703400 | (1375.71765, 504.8848) | 279 | 0.072916 |
| 13 | 13 | 375.213070 | 1466.698500 | 544.15020 | 1454.440600 | 459.681635 | 168.937130 | (1460.56955, 459.681635) | 280 | 0.077083 |
| 14 | 14 | 328.477630 | 1552.124800 | 498.18090 | 1540.248500 | 413.329265 | 169.703270 | (1546.18665, 413.32926499999996) | 281 | 0.081250 |
| 15 | 15 | 282.508330 | 1638.700200 | 448.76404 | 1628.357200 | 365.636185 | 166.255710 | (1633.5286999999998, 365.636185) | 282 | 0.085416 |
| 16 | 16 | 232.708270 | 1723.360400 | 397.04846 | 1712.634400 | 314.878365 | 164.340190 | (1717.9974, 314.878365) | 283 | 0.089583 |
| 17 | 17 | 86.901470 | 1978.973000 | 249.60226 | 1972.849900 | 168.251865 | 162.700790 | (1975.91145, 168.251865) | 284 | 0.102083 |
| 18 | 18 | 39.228405 | 2065.571800 | 197.55544 | 2058.095700 | 118.391923 | 158.327035 | (2061.83375, 118.3919225) | 285 | 0.106250 |
| 19 | 19 | -2.321506 | 2149.546400 | 149.00763 | 2145.081500 | 73.343062 | 151.329136 | (2147.3139499999997, 73.343062) | 286 | 0.110416 |
lines
[{'line_id': 0,
'y0': 735.359,
'x0': 50.431026,
'y1': 924.83594,
'x1': 42.816807,
'yc': 830.09747,
'length': 189.47694,
'centre': (46.6239165, 830.09747),
'frame_id': 267,
'time': 0.006249999999997868},
{'line_id': 1,
'y0': 733.3091,
'x0': 131.25888,
'y1': 927.47144,
'x1': 124.68847,
'yc': 830.39027,
'length': 194.16234000000009,
'centre': (127.973675, 830.39027),
'frame_id': 268,
'time': 0.010415999999995762},
{'line_id': 2,
'y0': 719.54486,
'x0': 377.8424,
'y1': 913.1218,
'x1': 370.81396,
'yc': 816.3333299999999,
'length': 193.57694000000004,
'centre': (374.32818, 816.3333299999999),
'frame_id': 269,
'time': 0.022915999999998604},
{'line_id': 3,
'y0': 712.80927,
'x0': 461.0131,
'y1': 906.386,
'x1': 454.2772,
'yc': 809.597635,
'length': 193.57673,
'centre': (457.64515, 809.597635),
'frame_id': 270,
'time': 0.027082999999997526},
{'line_id': 4,
'y0': 702.39185,
'x0': 543.6259,
'y1': 893.36615,
'x1': 535.54456,
'yc': 797.8789999999999,
'length': 190.97429999999997,
'centre': (539.58523, 797.8789999999999),
'frame_id': 271,
'time': 0.031249999999996447},
{'line_id': 5,
'y0': 651.6795,
'x0': 791.86035,
'y1': 839.42633,
'x1': 781.48004,
'yc': 745.552915,
'length': 187.74683000000005,
'centre': (786.670195, 745.552915),
'frame_id': 272,
'time': 0.04374999999999929},
{'line_id': 6,
'y0': 629.11383,
'x0': 876.25616,
'y1': 815.95776,
'x1': 864.9733,
'yc': 722.535795,
'length': 186.84393,
'centre': (870.61473, 722.535795),
'frame_id': 273,
'time': 0.04791599999999718},
{'line_id': 7,
'y0': 602.48627,
'x0': 959.7493,
'y1': 787.074,
'x1': 950.7228,
'yc': 694.780135,
'length': 184.58772999999997,
'centre': (955.23605, 694.780135),
'frame_id': 274,
'time': 0.052082999999996105},
{'line_id': 8,
'y0': 572.6995,
'x0': 1040.9858,
'y1': 754.1279,
'x1': 1031.5082,
'yc': 663.4137,
'length': 181.4284,
'centre': (1036.2469999999998, 663.4137),
'frame_id': 275,
'time': 0.05624999999999858},
{'line_id': 9,
'y0': 537.9483,
'x0': 1127.1869,
'y1': 718.0228,
'x1': 1117.7097,
'yc': 627.98555,
'length': 180.07449999999994,
'centre': (1122.4483, 627.98555),
'frame_id': 276,
'time': 0.06041599999999647},
{'line_id': 10,
'y0': 500.48923,
'x0': 1210.68,
'y1': 677.85596,
'x1': 1200.2998,
'yc': 589.172595,
'length': 177.36672999999996,
'centre': (1205.4899, 589.172595),
'frame_id': 277,
'time': 0.06458299999999895},
{'line_id': 11,
'y0': 461.0224,
'x0': 1296.229,
'y1': 638.0041,
'x1': 1285.119,
'yc': 549.51325,
'length': 176.9817,
'centre': (1290.674, 549.51325),
'frame_id': 278,
'time': 0.06874999999999787},
{'line_id': 12,
'y0': 420.0331,
'x0': 1381.2722,
'y1': 589.7365,
'x1': 1370.1631,
'yc': 504.8848,
'length': 169.7034,
'centre': (1375.71765, 504.8848),
'frame_id': 279,
'time': 0.07291599999999576},
{'line_id': 13,
'y0': 375.21307,
'x0': 1466.6985,
'y1': 544.1502,
'x1': 1454.4406,
'yc': 459.681635,
'length': 168.93713000000002,
'centre': (1460.56955, 459.681635),
'frame_id': 280,
'time': 0.07708299999999824},
{'line_id': 14,
'y0': 328.47763,
'x0': 1552.1248,
'y1': 498.1809,
'x1': 1540.2485,
'yc': 413.32926499999996,
'length': 169.70327000000003,
'centre': (1546.18665, 413.32926499999996),
'frame_id': 281,
'time': 0.08124999999999716},
{'line_id': 15,
'y0': 282.50833,
'x0': 1638.7002,
'y1': 448.76404,
'x1': 1628.3572,
'yc': 365.636185,
'length': 166.25571000000002,
'centre': (1633.5286999999998, 365.636185),
'frame_id': 282,
'time': 0.0854159999999986},
{'line_id': 16,
'y0': 232.70827,
'x0': 1723.3604,
'y1': 397.04846,
'x1': 1712.6344,
'yc': 314.878365,
'length': 164.34018999999998,
'centre': (1717.9974, 314.878365),
'frame_id': 283,
'time': 0.08958299999999753},
{'line_id': 17,
'y0': 86.90147,
'x0': 1978.973,
'y1': 249.60226,
'x1': 1972.8499,
'yc': 168.251865,
'length': 162.70078999999998,
'centre': (1975.91145, 168.251865),
'frame_id': 284,
'time': 0.10208299999999682},
{'line_id': 18,
'y0': 39.228405,
'x0': 2065.5718,
'y1': 197.55544,
'x1': 2058.0957,
'yc': 118.3919225,
'length': 158.327035,
'centre': (2061.83375, 118.3919225),
'frame_id': 285,
'time': 0.10624999999999929},
{'line_id': 19,
'y0': -2.321506,
'x0': 2149.5464,
'y1': 149.00763,
'x1': 2145.0815,
'yc': 73.343062,
'length': 151.329136,
'centre': (2147.3139499999997, 73.343062),
'frame_id': 286,
'time': 0.11041599999999718}]
annotated_image = Image.open("060-launch-analysis.png")
draw = ImageDraw.Draw(annotated_image)
for m in lines:
draw.line([(m['x0'], m['y0']), (m['x1'], m['y1'])], fill="red", width=2)
draw.circle([m['centre'][0], m['centre'][1]], 5, fill="blue")
annotated_image
So, let's see how the length of the bottle changes with height. Pixels in the image are measured from top (0) to bottom (1080) - so I've flipped the x-axis so that the points appear in the 'time' order as in the image.
line_df.set_index('yc')['length'].plot(marker='o', xlim=(900, 0), xlabel='Height in Image (pixels)', ylabel='Length (pixels)')
<Axes: xlabel='Height in Image (pixels)', ylabel='Length (pixels)'>
We can indeed see that the length of the bottle decreases with height.
x = line_df['yc'].values
y = line_df['length'].values
# Linear fit: degree = 1
linear_coeffs = np.polyfit(x, y, deg=1)
linear_fit = np.poly1d(linear_coeffs)
# Plot
fig, ax = plt.subplots()
plt.plot(x, y, 'o', label='Data', markersize=6)
plt.plot(x, linear_fit(x), label='Linear Fit', linestyle='--')
ax.set_xlim(900, 0)
# Compute R-squared
linear_pred = linear_fit(x)
ss_res_linear = np.sum((y - linear_pred) ** 2)
ss_tot_linear = np.sum((y - np.mean(y)) ** 2)
r_squared_linear = 1 - (ss_res_linear / ss_tot_linear)
ax.text(200,190, fr"$y = {linear_coeffs[0]:0.3f}x + {linear_coeffs[1]:0.3f}$", fontsize=12, color='red', ha='right')
ax.text(200,187, fr"$R^2 = {r_squared_linear:0.3f}$", fontsize=12, color='red', ha='right')
ax.set_title("Linear Fit");
# Quadratic fit: degree = 2
poly2_coeffs = np.polyfit(x, y, deg=2)
poly2_fit = np.poly1d(poly2_coeffs)
fig, ax = plt.subplots()
ax.plot(x, y, 'o', label='Data', markersize=6)
ax.plot(x, poly2_fit(x), label='Quadratic Fit', linestyle=':')
ax.set_xlim(900, 0)
# Compute R-squared
poly2_pred = poly2_fit(x)
ss_res_poly2 = np.sum((y - poly2_pred) ** 2)
ss_tot_poly2 = np.sum((y - np.mean(y)) ** 2)
r_squared_poly2 = 1 - (ss_res_poly2 / ss_tot_poly2)
ax.text(200,190, fr"$y = {poly2_coeffs[0]:0.3f}x^2 + {poly2_coeffs[1]:0.3f}x + {poly2_coeffs[2]:0.3f}$", fontsize=12, color='red', ha='right')
ax.text(200,187, fr"$R^2 = {r_squared_poly2:0.3f}$", fontsize=12, color='red', ha='right')
ax.set_title("Quadratic Fit");
Mesh Warp Image¶
We can now use our quadratic fit to undistort the image. Basically we now how a function for how the 'real' length of the bottle relates to the height in the image. We can use this to create a y-distortion map for each pixel in the image.
We then use the mesh warp transform to apply this distortion to the image.
def undistort(x, y):
factor = poly2_fit(y) / poly2_fit(0)
y_pred = y * factor
return x, y_pred
def build_mesh(size, grid_size):
width, height = size
mesh = []
dx = width / grid_size
dy = height / grid_size
for j in range(grid_size): # Note: Outer loop is Y (rows)
# Source rectangle corners
x0 = 0
y0 = int(j * dy)
x1 = width
y1 = int((j + 1) * dy)
# Source rectangle (left, top, right, bottom)
src_rect = (x0, y0, x1, y1)
# Destination rectangle corners after undistortion
top_left = undistort(x0, y0)
top_right = undistort(x1, y0)
bottom_right = undistort(x1, y1)
bottom_left = undistort(x0, y1)
# Pillow expects the destination quad as:
# (top-left.x, top-left.y, top-right.x, top-right.y, bottom-right.x, bottom-right.y, bottom-left.x, bottom-left.y)
dst_quad = (
top_left[0], top_left[1],
bottom_left[0], bottom_left[1],
bottom_right[0], bottom_right[1],
top_right[0], top_right[1],
)
mesh.append((src_rect, dst_quad))
return mesh
def simple(size):
width, height = size
factor = poly2_fit(height) / poly2_fit(0)
return [
[
(0, 0, width, height),
(0, 0, 0, height*factor, width, height*factor, width, 0),
]
]
# Load and transform image
img = Image.open("060-launch-analysis-no-labels.png")
mesh = build_mesh(img.size, grid_size=500)
# mesh = simple(img.size)
undistorted = img.transform(img.size, Image.MESH, mesh, Image.BICUBIC)
font = ImageFont.truetype("freefont/FreeSansBold.ttf", size=24)
draw = ImageDraw.Draw(undistorted)
# Add labels
for i, img in enumerate(cropped_images):
row = img['row']
ts = row['mission_time']
dt = row['dt']
frame_index = img['frame_index']
draw.text((20+img['paste_left'], 930), f"{ts:.3f}", fill="white", font=font)
draw.text((20+img['paste_left'], 960), f"{dt:.3f}", fill="white", font=font)
draw.text((20+img['paste_left'], 990), f"{frame_index}", fill="white", font=font)
# Add lines
line_spacing = 50 # Adjust the spacing as needed
for y in range(0, undistorted.height-150, line_spacing):
draw.line([(0, y), (undistorted.width, y)], fill="white", width=1)
undistorted
We can now see that the bottle is roughly three divisions long throughout the image... it's not perfect, but let's see if it's good enough.
Let's update our measurements to use the corrected measurements.
Calculations¶
The "rocket" is about 50cm long - so we can use our measurements to calculate the height of the rocket at any point in the image.
ROCKET_LENGTH = 0.50
calc_df = line_df.copy()
calc_df['length_calc'] = poly2_fit(calc_df['yc'])
calc_df['scale'] = ROCKET_LENGTH / calc_df['length_calc']
# Delta height - is yc minus yc of the previous line
calc_df['dl'] = -calc_df['yc'].diff().fillna(0)
calc_df['dh'] = calc_df['dl'] * calc_df['scale']
calc_df['dt'] = calc_df['time'].diff().fillna(0)
# Velocity is delta height divided by delta time
calc_df['v'] = calc_df['dh'] / calc_df['dt']
# Acceleration is delta velocity divided by delta time
calc_df['a'] = calc_df['v'].diff().fillna(0) / calc_df['dt']
# G-Force is acceleration divided by gravity
calc_df['g'] = calc_df['a'] / 9.81
calc_df.fillna(0, inplace=True)
calc_df
| line_id | y0 | x0 | y1 | x1 | yc | length | centre | frame_id | time | length_calc | scale | dl | dh | dt | v | a | g | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 735.359000 | 50.431026 | 924.83594 | 42.816807 | 830.097470 | 189.476940 | (46.6239165, 830.09747) | 267 | 0.006250 | 193.233290 | 0.002588 | -0.000000 | -0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 1 | 1 | 733.309100 | 131.258880 | 927.47144 | 124.688470 | 830.390270 | 194.162340 | (127.973675, 830.39027) | 268 | 0.010416 | 193.254042 | 0.002587 | -0.292800 | -0.000758 | 0.004166 | -0.181842 | 0.000000 | 0.000000 |
| 2 | 2 | 719.544860 | 377.842400 | 913.12180 | 370.813960 | 816.333330 | 193.576940 | (374.32818, 816.3333299999999) | 269 | 0.022916 | 192.263126 | 0.002601 | 14.056940 | 0.036557 | 0.012500 | 2.924521 | 248.509028 | 25.332215 |
| 3 | 3 | 712.809270 | 461.013100 | 906.38600 | 454.277200 | 809.597635 | 193.576730 | (457.64515, 809.597635) | 270 | 0.027083 | 191.792185 | 0.002607 | 6.735695 | 0.017560 | 0.004167 | 4.214034 | 309.458244 | 31.545183 |
| 4 | 4 | 702.391850 | 543.625900 | 893.36615 | 535.544560 | 797.879000 | 190.974300 | (539.58523, 797.8789999999999) | 271 | 0.031250 | 190.978839 | 0.002618 | 11.718635 | 0.030680 | 0.004167 | 7.362720 | 755.624238 | 77.025916 |
| 5 | 5 | 651.679500 | 791.860350 | 839.42633 | 781.480040 | 745.552915 | 187.746830 | (786.670195, 745.552915) | 272 | 0.043750 | 187.439886 | 0.002668 | 52.326085 | 0.139581 | 0.012500 | 11.166478 | 304.300657 | 31.019435 |
| 6 | 6 | 629.113830 | 876.256160 | 815.95776 | 864.973300 | 722.535795 | 186.843930 | (870.61473, 722.535795) | 273 | 0.047916 | 185.931200 | 0.002689 | 23.017120 | 0.061897 | 0.004166 | 14.857627 | 886.017451 | 90.317783 |
| 7 | 7 | 602.486270 | 959.749300 | 787.07400 | 950.722800 | 694.780135 | 184.587730 | (955.23605, 694.780135) | 274 | 0.052083 | 184.150945 | 0.002715 | 27.755660 | 0.075361 | 0.004167 | 18.085233 | 774.563520 | 78.956526 |
| 8 | 8 | 572.699500 | 1040.985800 | 754.12790 | 1031.508200 | 663.413700 | 181.428400 | (1036.2469999999998, 663.4137) | 275 | 0.056250 | 182.190448 | 0.002744 | 31.366435 | 0.086081 | 0.004167 | 20.657895 | 617.389420 | 62.934701 |
| 9 | 9 | 537.948300 | 1127.186900 | 718.02280 | 1117.709700 | 627.985550 | 180.074500 | (1122.4483, 627.98555) | 276 | 0.060416 | 180.041612 | 0.002777 | 35.428150 | 0.098389 | 0.004166 | 23.617087 | 710.319668 | 72.407713 |
| 10 | 10 | 500.489230 | 1210.680000 | 677.85596 | 1200.299800 | 589.172595 | 177.366730 | (1205.4899, 589.172595) | 277 | 0.064583 | 177.767272 | 0.002813 | 38.812955 | 0.109168 | 0.004167 | 26.198197 | 619.416995 | 63.141386 |
| 11 | 11 | 461.022400 | 1296.229000 | 638.00410 | 1285.119000 | 549.513250 | 176.981700 | (1290.674, 549.51325) | 278 | 0.068750 | 175.529517 | 0.002849 | 39.659345 | 0.112971 | 0.004167 | 27.110772 | 219.000426 | 22.324202 |
| 12 | 12 | 420.033100 | 1381.272200 | 589.73650 | 1370.163100 | 504.884800 | 169.703400 | (1375.71765, 504.8848) | 279 | 0.072916 | 173.115551 | 0.002888 | 44.628450 | 0.128898 | 0.004166 | 30.940438 | 919.266971 | 93.707133 |
| 13 | 13 | 375.213070 | 1466.698500 | 544.15020 | 1454.440600 | 459.681635 | 168.937130 | (1460.56955, 459.681635) | 280 | 0.077083 | 170.782948 | 0.002928 | 45.203165 | 0.132341 | 0.004167 | 31.759294 | 196.509743 | 20.031574 |
| 14 | 14 | 328.477630 | 1552.124800 | 498.18090 | 1540.248500 | 413.329265 | 169.703270 | (1546.18665, 413.32926499999996) | 281 | 0.081250 | 168.508562 | 0.002967 | 46.352370 | 0.137537 | 0.004167 | 33.006272 | 299.250744 | 30.504663 |
| 15 | 15 | 282.508330 | 1638.700200 | 448.76404 | 1628.357200 | 365.636185 | 166.255710 | (1633.5286999999998, 365.636185) | 282 | 0.085416 | 166.292600 | 0.003007 | 47.693080 | 0.143401 | 0.004166 | 34.421769 | 339.773626 | 34.635436 |
| 16 | 16 | 232.708270 | 1723.360400 | 397.04846 | 1712.634400 | 314.878365 | 164.340190 | (1717.9974, 314.878365) | 283 | 0.089583 | 164.072622 | 0.003047 | 50.757820 | 0.154681 | 0.004167 | 37.120460 | 647.633978 | 66.017735 |
| 17 | 17 | 86.901470 | 1978.973000 | 249.60226 | 1972.849900 | 168.251865 | 162.700790 | (1975.91145, 168.251865) | 284 | 0.102083 | 158.461126 | 0.003155 | 146.626500 | 0.462658 | 0.012500 | 37.012611 | -8.627917 | -0.879502 |
| 18 | 18 | 39.228405 | 2065.571800 | 197.55544 | 2058.095700 | 118.391923 | 158.327035 | (2061.83375, 118.3919225) | 285 | 0.106250 | 156.824247 | 0.003188 | 49.859943 | 0.158968 | 0.004167 | 38.149168 | 272.751827 | 27.803448 |
| 19 | 19 | -2.321506 | 2149.546400 | 149.00763 | 2145.081500 | 73.343062 | 151.329136 | (2147.3139499999997, 73.343062) | 286 | 0.110416 | 155.463711 | 0.003216 | 45.048861 | 0.144885 | 0.004166 | 34.778073 | -809.192132 | -82.486456 |
fit_df = calc_df[(calc_df.time > 0.015) & (calc_df.time < 0.1)]
x = fit_df['time'].values
y = fit_df['v'].values
# Linear fit: degree = 1
linear_coeffs = np.polyfit(x, y, deg=1)
linear_fit = np.poly1d(linear_coeffs)
# Plot
plt.plot(calc_df['time'].values, calc_df['v'].values, 'o', label='Data', markersize=6)
plt.plot(x, linear_fit(x), label='Linear Fit', linestyle='--')
a, c = linear_coeffs
x_intercept = -c / a
print(f"linear_coeffs: {linear_coeffs}")
print(f"x-intercept: {x_intercept:.4f} s")
linear_coeffs: [533.24435989 -9.76507529] x-intercept: 0.0183 s
So it looks like our takeoff estimate is off by about 0.018s. Anyway, assuming we have a constant acceleration of 533.24/9.81 = 54.36 g, then we can estimate the altitude after different thrust times as:
def altitude(t):
h = a * t**2 / 2
print(f"altitude({t:.2f}) = {h:.1f} m")
altitude(0.07)
altitude(0.08)
altitude(0.09)
altitude(0.07) = 1.3 m altitude(0.08) = 1.7 m altitude(0.09) = 2.2 m
From dynamic analysis we found that the "launch" speed was 32.19 m/s - let's figure out the time and altitude was at this point.
v = 32.19 # target speed in m/s
t = v / a
print(f"Time to reach {v} m/s: {t:.4f} seconds")
Time to reach 32.19 m/s: 0.0604 seconds
This is shorter than we had previously estimated, but we also found that we assumed a launch 0.0183 s before the fit indicates, that means a MECO still at around 0.8s which isn't that far off our estimate of 0.9 to 1.1 seconds. This also matches quite well when we can see from the video analysis that the water "exhaust" dies down.
Back of the envelope flight parameters¶
(We have more accurate measures in some of the other notebooks)
The middle of the flight seems to have a fairly constant acceleration - let's see if we can estimate the apogee from this. If we assume this acceleration continued until t=0.085s which seems to correlate reasonably well with our IMU readings, then we have an initial velocity of:
v_final = linear_fit(0.085)
v_final
np.float64(35.560695302234166)
With a velocity of 47m/s we can estimate the apogee as:
v**2 / (2 * g)
apogee = v_final**2 / (2 * 9.81)
apogee
np.float64(64.45275486128129)
apogee_time = v_final / 9.81
apogee_time
np.float64(3.624943455885236)
Both of theasure measurements correlate well with the flight computer data.