In [1]:
%matplotlib inline
DEFAULT_FIGSIZE = (12, 8)

import itertools
import sys

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')

sys.path.append('..')
from antlia.record import load_records
from antlia import plot_braking as braking

%load_ext autoreload
%autoreload 2

import matplotlib as mpl
mpl.rcParams['figure.figsize'] = DEFAULT_FIGSIZE

In [2]:
records = load_records(sync=True)


Unable to determine range 2
Unable to determine range 1

In [3]:
import IPython.display

def animate_braking(rid, tid, xlim, ylim, speedup=1, **kwargs):
    record = records[rid]
    trial = record.trial[tid]
    
    m, _, _, _ = braking.get_metrics(trial.data)
    
    t0 = m['braking starttime']
    t1 = m['braking endtime']

    frames = record.lidar.frame(lambda t: (t >= t0 - 1) & (t < t1 + 1))
        
    c = sns.color_palette('Paired', 10)[1::2]
    colors = [c[2]
              if (t > t0) and (t < t1) else c[1]
              for t in frames.time]
    
    return frames.animate(xlim, ylim, speedup, colors, **kwargs)

def display_animation(animation):
    plt.close(animation._fig)
    IPython.display.display(IPython.display.HTML(ani.to_html5_video()))
    
    
for rid, tid in [[0, 6]]:
#for rid, tid in itertools.product(range(3, 4), range(1)):
    record = records[rid]
    trial = record.trial[tid]
    
    ani = animate_braking(rid, tid,
                          xlim=(0, 50),
                          ylim=(0, 2),
                          speedup=0.5,
                          plot_kwargs={'marker': 'o'})
    ani._fig.get_axes()[0].set_title('rider {} trial {:02d}'.format(rid, tid))
    display_animation(ani)



In [4]:
## calculate forward length
# We make some simplifications in the bicycle geometry
# (namely ignoring changes in roll and pitch, and mostly
# assuming yaw to be zero) as they will be small as we
# are near the nominal orientation.

bb_to_front_hub = 0.655 # m
front_wheel_radius = 0.359 # m
head_tube_angle = (90 - 72) * np.pi/180 # rad TODO: measure this

import sympy
import sympy.physics.vector as sv

yaw, steer = sympy.symbols('yaw steer')
N = sv.ReferenceFrame('N')
A = N.orientnew('A', 'axis', [yaw, N.z])
B = A.orientnew('B', 'axis', [head_tube_angle, A.y])
C = B.orientnew('C', 'axis', [steer, B.z])

# vector pointing from front hub center to most forward
# point on front wheel
n = sv.cross(C.y, N.z).normalize()

o = sv.Point('o')
p = o.locatenew('p', bb_to_front_hub*A.x + front_wheel_radius*n)

d = sv.dot(p.pos_from(o), N.x)

def forward_length(steer_angle, yaw_angle = 0):
    # distance from bottom bracket to front hub,
    # projected onto rear frame body fixed x-axis
    return d.subs({yaw: yaw_angle, steer: steer_angle})

print(forward_length(0))
print(forward_length(15*np.pi/180))
print(forward_length(-15*np.pi/180))
print(forward_length(30*np.pi/180))
print(forward_length(-30*np.pi/180))


1.01400000000000
1.00288180754931
1.00288180754931
0.969681991356316
0.969681991356316

In [5]:
plt.close('all')
for rid, tid in [[0, 2]]:
    record = records[rid]
    trial = record.trial[tid]
    try:
        m, _, _, _ = braking.get_metrics(trial.data)
    except TypeError:
        continue
    print('rider {}, trial {}'.format(rid, tid))
    
    index1 = record.lidar.frame_index(m['braking starttime'][0])
    
    xlim = (0, 50)
    ylim = (0.5, 1.5)
    fig, ax = plt.subplots()
        
    x, y = record.lidar[index1 - 1].cartesian(xlim=xlim, ylim=ylim)
    ax.plot(x.compressed(), y.compressed(), linestyle=' ', marker='.')
    x, y = record.lidar[index1].cartesian(xlim=xlim, ylim=ylim)
    ax.plot(x.compressed(), y.compressed(), linestyle=' ', marker='.')
        
    ax.set_xlabel('position [m]')
    ax.set_ylabel('position [m]')
    ax.set_xlim((0, 50))
    ax.set_ylim(ylim)
    
    rider_x = x[np.where((x > 10) & (x < 48))] 
    print('average rider x: {}'.format(rider_x.mean()))
    
    # from bottom bracket to front wheel center
    # projected on inertia x-axis
    dtc1 = 50 - rider_x.mean() - 0.580
    print('trial dtc, fl = .580: {}'.format(dtc1))
    
    steer_angle = np.interp(m['braking starttime'][0],
                            trial.data.time,
                            trial.data['steer angle'])
    dtc2 = 50 - rider_x.mean() - forward_length(steer_angle)
    print('trial dtc, fl func: {}'.format(dtc2))
plt.show()


rider 0, trial 2
average rider x: 42.97164073139187
trial dtc, fl = .580: 6.448359268608128
trial dtc, fl func: 6.02880269616110

In [6]:
def calculate_dtc(rid, tid):
    record = records[rid]
    trial = record.trial[tid]
    m, _, _, _ = braking.get_metrics(trial.data)
        
    index = record.lidar.frame_index(m['braking starttime'][0])
    index = [-1 + index, index]
    
    xlim = (0, 50)
    ylim = (0.5, 1.5)
        
    dtc = []
    for i in index:
        x, _ = record.lidar[[i]].cartesian(xlim=xlim, ylim=ylim)
        x = x.compressed()
        rider_x = x[np.where((x > 10) & (x < 48))]
        
        steer_angle = np.interp(record.lidar[i].time[0],
                                trial.data.time,
                                trial.data['steer angle'])
        
        dtc.append(50 - rider_x.mean() - forward_length(steer_angle))
    
    dtc = np.array(dtc).astype(np.float64)
    t = record.lidar[np.array(index)].time.flatten()
    return np.interp(m['braking starttime'][0], t, dtc), m

calculate_dtc(0, 2)


Out[6]:
(6.0975533337432655,
 array([ (-1.72348435,  715.84898323, -0.74410543,   2.76832297e-44,  0.0994677,  6.07819461,  1.952244,  8.70374047,  411.786928,  413.739172, 55, [2212, 2456], 0, 0, 0)],
       dtype=[('linregress slope', '<f8'), ('linregress intercept', '<f8'), ('linregress r-value', '<f8'), ('linregress p-value', '<f8'), ('linregress stderr', '<f8'), ('starting velocity', '<f8'), ('braking duration', '<f8'), ('braking distance', '<f8'), ('braking starttime', '<f8'), ('braking endtime', '<f8'), ('window size', '<i8'), ('braking range', '<i8', (2,)), ('lockup ranges', '<i8'), ('rider id', '<i8'), ('trial id', '<i8')]))

In [7]:
import pandas as pd

def get_dataframe(records):
    """Define function to get metrics for lidar data.
    """
    metrics = []
    dtc = []
    for rid, tid in itertools.product(range(4), range(15)):
        try:
            x, m = calculate_dtc(rid, tid)
        except TypeError:
            print('skipping {}, {}'.format(rid, tid))
            continue
        
        dtc.append(x)
        
        m['rider id'] = rid
        m['trial id'] = tid
        metrics.append(m)
        
    metrics = np.concatenate(metrics)
    # exclude non-scalar fields
    names = [dtype[0]
             for dtype in metrics.dtype.fields.items()
             if dtype[1][0].shape == ()]
    
    df = pd.DataFrame(metrics[names])
    df['distance-to-collision'] = dtc
    
    f = lambda row: row['distance-to-collision']/row['starting velocity']
    df['time-to-collision'] = df.apply(f, axis=1)
    return df
 
df = get_dataframe(records)
df.to_pickle('braking_ttc.p.gz')
# DTC/TTC calculations will be 'nan' if not detected by lidar
# lidar only has a guaranteed detection range of 30 m
df


skipping 0, 0
skipping 0, 1
/Users/oliver/miniconda3/envs/dev/lib/python3.5/site-packages/ipykernel_launcher.py:22: RuntimeWarning: Mean of empty slice.
/Users/oliver/miniconda3/envs/dev/lib/python3.5/site-packages/numpy/core/_methods.py:80: RuntimeWarning: invalid value encountered in double_scalars
  ret = ret.dtype.type(ret / rcount)
Out[7]:
linregress stderr braking starttime braking endtime linregress p-value linregress intercept linregress slope braking duration trial id window size linregress r-value lockup ranges starting velocity braking distance rider id distance-to-collision time-to-collision
0 0.099468 411.786928 413.739172 2.768323e-44 715.848983 -1.723484 1.952244 2 55 -0.744105 0 6.078195 8.703740 0 6.097553 1.003185
1 0.514137 493.557209 494.157285 8.387193e-03 691.587816 -1.393157 0.600076 3 55 -0.302307 0 3.937731 2.140694 0 NaN NaN
2 0.118824 571.154965 572.667154 5.695965e-23 773.491082 -1.345448 1.512189 4 55 -0.637766 0 4.857473 6.070381 0 7.685615 1.582225
3 0.213894 677.698360 678.858507 2.569676e-21 1630.390552 -2.397210 1.160147 5 55 -0.683830 0 5.533575 5.122003 0 5.334500 0.964024
4 0.130077 766.029467 767.317630 4.054087e-11 710.990535 -0.922756 1.288163 6 55 -0.490318 0 4.031114 4.557585 0 NaN NaN
5 0.199042 846.439577 847.655731 1.672730e-15 1504.730026 -1.771827 1.216154 7 55 -0.587936 0 4.699092 4.753165 0 5.562428 1.183724
6 0.119038 910.143588 911.839800 1.107465e-34 1618.459859 -1.771589 1.696212 8 55 -0.716459 0 5.855901 7.729463 0 8.116734 1.386078
7 0.432361 1020.986526 1021.778623 9.763750e-05 1798.833210 -1.757588 0.792097 9 55 -0.381527 0 4.146971 2.901759 0 4.766233 1.149329
8 0.272392 1090.427257 1091.723418 7.108176e-09 1821.773991 -1.665790 1.296161 10 55 -0.435266 0 4.927042 5.537081 0 5.782857 1.173697
9 0.131949 1152.203024 1154.227278 4.058286e-25 1766.450561 -1.527542 2.024254 11 55 -0.589990 0 6.070815 9.849418 0 7.363308 1.212903
10 0.590566 1227.340468 1228.124568 9.067728e-06 3403.827194 -2.769317 0.784100 12 55 -0.431701 0 4.393109 3.015768 0 3.324807 0.756823
11 0.227213 1296.417151 1297.841334 1.777402e-07 1606.978394 -1.235655 1.424183 13 55 -0.379297 0 4.911338 5.944619 0 6.410373 1.305219
12 0.221304 1358.652981 1360.045157 1.134258e-32 4476.042180 -3.289115 1.392176 14 55 -0.749813 0 7.264251 6.941725 0 6.967701 0.959177
13 0.372370 149.144789 150.308935 2.130479e-03 178.284159 -1.165539 1.164146 0 55 -0.256599 0 4.468970 4.390792 1 5.851486 1.309359
14 0.315063 267.399657 268.519797 2.374467e-11 618.441244 -2.291882 1.120140 1 55 -0.526470 0 5.332766 4.826932 1 5.867699 1.100310
15 0.247502 345.050421 346.462602 7.241352e-16 768.100148 -2.208759 1.412181 2 55 -0.565956 0 5.820894 6.224143 1 6.047911 1.039000
16 0.521148 431.805332 432.653438 9.615722e-03 597.924467 -1.374868 0.848106 3 55 -0.250448 0 4.084662 3.109326 1 3.841801 0.940543
17 0.105052 511.564360 513.884651 7.957886e-25 614.627484 -1.189345 2.320291 4 55 -0.554965 0 6.257176 11.186423 1 10.939693 1.748343
18 0.267451 618.606801 619.966989 5.918754e-09 1020.617117 -1.640769 1.360188 5 55 -0.427813 0 5.095139 6.135208 1 5.991892 1.176002
19 0.303229 701.457234 702.481363 3.583644e-04 784.351425 -1.112454 1.024129 6 55 -0.310662 0 3.922852 3.526178 1 4.685507 1.194414
20 0.159852 780.423163 782.223391 4.134105e-18 1187.822777 -1.514316 1.800228 7 55 -0.535680 0 5.694162 8.375723 1 7.702101 1.352631
21 0.181183 904.446757 906.014955 2.957331e-11 1161.410129 -1.278428 1.568198 8 55 -0.451911 0 4.762297 6.489323 1 7.379062 1.549476
22 0.451207 1041.463986 1042.336094 2.854776e-04 1767.360226 -1.692859 0.872108 9 55 -0.340969 0 3.978648 3.113868 1 3.719035 0.934748
23 0.131998 1120.657943 1122.730203 1.139927e-18 1417.486938 -1.259601 2.072260 10 55 -0.511491 0 5.860524 9.532710 1 8.631915 1.472891
24 0.178199 1198.235696 1199.795896 3.484063e-15 1833.545261 -1.525589 1.560200 11 55 -0.524629 0 5.052453 6.771815 1 6.786150 1.343140
25 0.426958 1296.765087 1297.569187 4.772477e-05 2360.607031 -1.817068 0.804100 12 55 -0.394954 0 4.095955 2.867340 1 4.115599 1.004796
26 0.080633 1368.302085 1370.866405 1.159383e-30 1422.442075 -1.035140 2.564320 13 55 -0.584857 0 5.688769 12.129617 1 11.712218 2.058832
27 0.213803 1450.108366 1451.532548 2.380192e-09 1956.712171 -1.345778 1.424182 14 55 -0.428662 0 4.920177 6.023610 1 6.134057 1.246715
28 0.192734 67.937108 69.477294 2.184056e-12 104.074664 -1.451001 1.540186 0 55 -0.484273 0 5.250042 6.746693 2 7.678202 1.462503
29 0.459561 159.108565 159.932668 6.778942e-03 206.168884 -1.270426 0.824103 1 55 -0.265221 0 4.140467 2.892420 2 NaN NaN
30 0.186733 240.858843 242.387040 3.175743e-20 472.449027 -1.935900 1.528197 2 55 -0.602094 0 5.756837 7.169094 2 6.853391 1.190479
31 0.184803 327.793768 329.481980 6.320371e-17 558.665530 -1.685668 1.688212 3 55 -0.533608 0 5.759884 7.919973 2 7.918953 1.374846
32 0.117980 388.025342 390.185616 2.197673e-24 522.255291 -1.330083 2.160274 4 55 -0.567176 0 6.033922 10.180508 2 11.340628 1.879479
33 0.460033 460.818498 461.354565 1.989741e-04 839.788339 -1.814344 0.536067 5 55 -0.439426 0 3.632680 1.725589 2 4.638613 1.276912
34 0.123615 523.414369 525.470628 2.590222e-21 678.380714 -1.284263 2.056259 6 55 -0.545340 0 5.959406 9.990297 2 9.548608 1.602275
35 0.283712 595.659453 596.819600 1.985175e-08 1010.307303 -1.687670 1.160147 7 55 -0.445380 0 4.712639 4.700794 2 5.249717 1.113965
36 0.237073 666.012298 667.372468 9.150321e-09 960.560949 -1.434223 1.360170 8 55 -0.422943 0 5.274323 5.951734 2 6.443613 1.221695
37 0.143553 725.987840 728.004092 7.698399e-21 1075.893731 -1.472820 2.016252 9 55 -0.544331 0 6.195570 10.403022 2 9.697881 1.565293
38 0.160362 792.288173 793.992385 7.577289e-20 1290.271951 -1.621243 1.704212 10 55 -0.571253 0 5.585574 7.496743 2 7.334811 1.313170
39 0.368513 861.340855 862.108949 2.970089e-04 1196.854970 -1.384924 0.768094 11 55 -0.361420 0 3.821096 2.635392 2 4.625546 1.210529
40 0.621001 937.198392 937.798466 2.872655e-04 2221.722279 -2.366124 0.600074 12 55 -0.407285 0 4.009770 2.091206 2 4.683763 1.168088
41 0.090844 1002.855650 1005.279956 2.040731e-39 1398.661282 -1.388128 2.424306 13 55 -0.660943 0 6.113808 11.847343 2 11.258227 1.841443
42 0.104427 1063.131230 1065.155483 4.680409e-24 1254.729751 -1.175198 2.024253 14 55 -0.579102 0 4.894521 8.402625 2 8.338216 1.703581
43 0.088299 200.234093 202.194340 1.588852e-47 329.475777 -1.613547 1.960247 0 55 -0.760791 0 6.213133 9.423451 3 10.671649 1.717595
44 0.377837 290.552450 291.232535 2.714446e-06 557.072111 -1.903188 0.680085 1 55 -0.483859 0 3.848757 2.345546 3 3.392364 0.881418
45 0.162949 372.902804 374.262975 2.418834e-11 439.577530 -1.166514 1.360171 2 55 -0.483472 0 4.516728 5.152085 3 6.312491 1.397580
46 0.128711 446.240024 447.784214 9.314305e-43 1035.378559 -2.305830 1.544190 3 55 -0.791776 0 5.800934 7.172419 3 8.631196 1.487898
47 0.192970 523.161695 524.369850 3.227420e-12 771.154586 -1.464629 1.208155 4 55 -0.528038 0 4.810463 4.871084 3 5.296799 1.101100
48 0.411237 606.516175 607.252267 3.406169e-02 540.559473 -0.885047 0.736092 5 55 -0.221236 0 3.892019 2.531203 3 3.507543 0.901214
49 0.645426 701.688144 702.288220 2.535772e-06 2316.762848 -3.295095 0.600076 6 55 -0.512936 0 4.479492 2.187171 3 2.614892 0.583747
50 0.135457 775.569433 777.009615 1.436463e-23 1225.343357 -1.573038 1.440182 7 55 -0.656544 0 5.242828 6.064177 3 6.605595 1.259930
51 0.241547 849.714755 850.610868 1.089151e-13 1747.392063 -2.050825 0.896113 8 55 -0.629199 0 4.720554 3.456587 3 5.106999 1.081864
52 0.289432 958.456427 959.152515 2.809079e-06 1396.529880 -1.452917 0.696088 9 55 -0.478195 0 3.818448 2.412781 3 3.940970 1.032087
53 0.159471 1035.458110 1036.570250 2.661685e-17 1609.419085 -1.549688 1.112140 10 55 -0.638778 0 4.617103 4.359397 3 5.472000 1.185159
54 0.154867 1107.235135 1108.499294 1.926171e-32 2602.650230 -2.345220 1.264159 11 55 -0.771458 0 5.681426 5.636111 3 6.804707 1.197711
55 0.242277 1189.645494 1190.477600 2.394909e-17 2956.201500 -2.480941 0.832106 12 55 -0.711977 0 4.459765 3.102752 3 4.550239 1.020287
56 0.382559 1273.521044 1274.121119 3.178336e-04 1845.580331 -1.446112 0.600075 13 55 -0.404598 0 3.837834 2.095830 3 2.564490 0.668213
57 0.116723 1342.429710 1343.949900 3.627982e-35 2419.708028 -1.798177 1.520190 14 55 -0.746988 0 5.966686 6.712599 3 7.548745 1.265149

In [8]:
plt.close('all')
fig, ax = plt.subplots()
sns.swarmplot(x='rider id', y='starting velocity', data=df, ax=ax)
ax.set_title('rider vs starting velocity (braking event)')

fig, ax = plt.subplots()
sns.swarmplot(x='rider id', y='linregress slope', data=df, ax=ax)
ax.set_title('rider vs acceleration (braking event)')

fig, ax = plt.subplots()
sns.swarmplot(x='rider id', y='distance-to-collision', data=df, ax=ax)
ax.set_title('rider vs DTC')

fig, ax = plt.subplots()
sns.swarmplot(x='rider id', y='time-to-collision', data=df, ax=ax)
ax.set_title('rider vs TTC')
plt.show()



In [9]:
from antlia.plotdf import plotjoint

colors = sns.color_palette()

plt.close('all')
g = plotjoint('starting velocity', 'distance-to-collision',
              df[np.isfinite(df['time-to-collision'])],
              ('rider id', colors))
g.fig.set_size_inches(DEFAULT_FIGSIZE)
g.set_axis_labels('velocity [m/s]', 'distance-to-collision [m]')

g = plotjoint('starting velocity', 'time-to-collision',
              df[np.isfinite(df['time-to-collision'])],
              ('rider id', colors))
g.fig.set_size_inches(DEFAULT_FIGSIZE)
g.set_axis_labels('velocity [m/s]', 'time-to-collision [s]')
plt.show()