ES 155 Final Project

Professor Demba Ba

TFs Yasha Iravantchi, Diana Zhang

Engineering Sciences 155: Biological Signal Processing

Harvard John A. Paulson School of Engineering and Applied Sciences

Predicting free-throw shot success immediately after the release of the basketball could enable more immediate feedback for the practicing shooter. Coupled with some sort of stimulus, immediate feedback regarding should success will reinforce good technique and ultimately lead to more efficient learning and skill development.

Using the Empatica E4 wristband with a three-axis accelerometer, we looked to identify variability in free-throw shot patterns in high-level players. Its light weight and sleek design do not interfere with shooters’ technique.

- What is the accelerometer signature of a free-throw shot?
- How can we identify and segment individual shots from a larger data set?
- Is it possible to distinguish a made shot from a missed shot based on variability in wrist motion?
- What are the main sources of variability in wrist motion during a free-throw shot?

In the game of basketball, players seek to score points by shooting the ball through a hoop (Figure 1). After being fouled, a player is given the opportunity to shoot two uncontested free-throws from a line 13 feet in front of the basket. As many games come down to only one or two points, free-throws become critical to the success of a team. As a result, players spend many hours throughout their career practicing to develop skills at shooting free-throws.

Using video cameras and motion tracking software, Button et al. (2003) studied the free-throw shooting of 9 female players. Contrary to their predictions, they found no clear pattern of a reduction in trajectory variability with increasing skill level. They did not comment on deviations in wrist mechanics based on the success of shots. They did find, however, that higher skill levels were associated with "inertial movement consistency from the elbow and wrist joints." In other words, the release velocity of the ball is a main factor in the success of a shot.

Two male participants were selected for data collection based on their high levels of experience with basketball. The Empatica E4 device was used to record three-axis accelerometer data for sets of about 50 shots, totaling around 100 shots recorded per player. The success of shots was recorded during collection. Both shooters made around 70% of shots taken, which is comparable to the league average for the NBA over the last 40 years.

Using video analysis, the typical shot pattern was identified and correlated with the physical shot (Figure 2). We were able to find that across multiple shooters, this two-second shot “window” was fairly consistent for each shot taken.

At point 1, the shooter lowers the ball in preparation for the shot. At point 2, the shooter begins to thrust the ball towards the hoop. At point 3, denoted by the sharp negative peak in acceleration, the hand reaches full extension, and the ball is released with the flick of the wrist. At point 4, the hand falls back down during the “follow-through”. These movements lend insight to patterns of variability in shots.

In [1]:

```
from IPython.display import YouTubeVideo
YouTubeVideo('q4AZ5n66lgk')
```

Out[1]:

In [2]:

```
#IPython is what you are using now to run the notebook
import IPython
print("IPython version: %6.6s (need at least 1.0)" % IPython.__version__)
# Numpy is a library for working with Arrays
import numpy as np
print( "Numpy version: %6.6s (need at least 1.7.1)" % np.__version__)
# SciPy implements many different numerical algorithms
import scipy as sp
from scipy import signal
print( "SciPy version: %6.6s (need at least 0.12.0)" % sp.__version__)
# Pandas makes working with data tables easier
import pandas as pd
print( "Pandas version: %6.6s (need at least 0.11.0)" % pd.__version__)
# Module for plotting
import matplotlib
%matplotlib inline
print( "Mapltolib version: %6.6s (need at least 1.2.1)" % matplotlib.__version__)
from mpl_toolkits.mplot3d import Axes3D
%pylab inline
pylab.rcParams['figure.figsize'] = (14, 6)
```

In [3]:

```
import csv
with open('round2/ACC.csv', "rt") as f:
rawData = list(csv.reader(f))
samp_rate = np.array(rawData[1]).astype(np.float) #second row is sample rate
walk_ACC = np.array(rawData[2:]).astype(np.float)
#makes time axis
time = np.linspace(1,np.shape(walk_ACC)[0],np.shape(walk_ACC)[0])/samp_rate[0] #converts to seconds
time = np.transpose(time)
time_len = np.shape(time)[0]
shot_mag = np.zeros(time_len)
x = np.zeros(time_len)
y = np.zeros(time_len)
z = np.zeros(time_len)
###find magnitude
for i in range(0,time_len):
shot_mag[i] = np.sqrt(np.square(walk_ACC[i,0])+np.square(walk_ACC[i,1])+np.square(walk_ACC[i,2]))
x[i] = walk_ACC[i,0]
y[i] = walk_ACC[i,1]
z[i] = walk_ACC[i,2]
x0 = pylab.demean(x)
y0 = pylab.demean(y)
z0 = pylab.demean(z)
```

(Shot was identified by manually inspecting the code)

In [4]:

```
axis_font = {'fontname':'Arial', 'size':'26'}
plt.plot(time[1700:2000],y[1700:2000],time[1870:1930],y[1870:1930],linewidth=4)
plt.xlabel('Seconds',**axis_font)
plt.title('Typical Shot',**axis_font)
plt.ylabel('Acceleration (1/64g)',**axis_font)
plt.show()
```

In order to solve the problem of classifying makes from misses to achieve predictive value, it is first necessary to automatically segment the data into individual shots. Full automation of this process will be necessary in order to eventually analyze data containing thousands of shots. This segmentation is non-trivial as the data is very noisy and contains artifacts of minute and large movements in-between shots.

The full set of data from a shooting session can be approximated as the signature of one shot (identified in Figure 2) convoluted with an “impulse train.” Thus, by convoluting the full set of data with the “signature shot,” we were able to obtain another sort of impulse train with peaks at the locations of the shots (Figure 3).

In [5]:

```
ss = y[1870:1930]
Z = np.convolve(ss,y)
import peakutils
shots = peakutils.indexes(Z, thres=.15, min_dist=200)
shots = np.delete(shots,0)
#print (shots)
plt.plot(Z)
plt.plot(shots,Z[shots],'ro')
plt.show()
print("number of shots", np.size(shots))
```

In [6]:

```
time2 = np.linspace(0,149,150)/32.
axis_font = {'fontname':'Arial', 'size':'20'}
for i in range(0,np.size(shots)):
plt.plot(time2,y[(shots[i]-100):(shots[i]+50)])
plt.title('Y Axis ACC',**axis_font)
plt.xlabel('Seconds',**axis_font)
plt.ylabel('Acceleration (1/64g)')
plt.show()
for i in range(0,np.size(shots)):
plt.plot(time2, x[(shots[i]-100):(shots[i]+50)])
plt.title('X Axis ACC',**axis_font)
plt.xlabel('Seconds',**axis_font)
plt.ylabel('Acceleration (1/64g)')
plt.show()
for i in range(0,np.size(shots)):
plt.plot(time2, z[(shots[i]-100):(shots[i]+50)])
plt.title('Z Axis ACC',**axis_font)
plt.xlabel('Seconds',**axis_font)
plt.ylabel('Acceleration (1/64g)')
plt.show()
for i in range(0,np.size(shots)):
plt.plot(time2, shot_mag[(shots[i]-100):(shots[i]+50)])
plt.title('ACC Magnitude',**axis_font)
plt.xlabel('Seconds',**axis_font)
plt.ylabel('Acceleration (1/64g)')
plt.show()
```