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.