Count Rows on an old rowing machine
Idea
At the end of 2019 a mechanical rowing machine came into my possession. This machine (a Hanseatic Rowing Machine) has no electronics at all but I want to measure the rows I am doing.
Recording
So I bought an I2C based Accelerometer: MMA7455 and soldered a raspberry-pi zero shield for it:
The recording is done with a Python script that is started on boot via systemd.
This code shows howto read the current value from the sensor:
import smbus bus = smbus.SMBus(1) # MMA7455L address is 0x1D bus.write_byte_data(0x1D, 0x16, 0x01) # read values data = bus.read_i2c_block_data(0x1D, 0x00, 6) # Convert the data to 10-bits xAcc = (data[1] & 0x03) * 256 + data [0] if xAcc > 511 : xAcc -= 1024 yAcc = (data[3] & 0x03) * 256 + data [2] if yAcc > 511 : yAcc -= 1024 zAcc = (data[5] & 0x03) * 256 + data [4] if zAcc > 511 : zAcc -= 1024 print(f"Acceleration {xAcc:5d} {yAcc:5d} {zAcc:5d}")
The full record script used on the raspberry pi is additionally logging to a csv file: https://github.com/mfa/rowing-count/blob/master/record.py.
Evaluation
The latest version of the evaluation as a jupyter notebook: https://github.com/mfa/rowing-count/blob/master/experiments.ipynb
First, we need to find the best curve for the problem. Here we see 1 row, 2 rows and 5 rows:
And only the 5 rows zoomed in:
Only in the x axis curve the rows are clearly distinguishable.
So the isolated x axis looks like this
Because it is easier to detect peaks on top we negate the curve:
And now smoothen the curve using a savgol filter:
We got the parameters for the savgol by trial and error.
The next step is the peak finding. Scipy has a find_peaks method that works after some tweaking quite good:
The (orange) line below the found peak is the prominence. This "height" helps filtering too small peaks. The complete filtering methods looks like this
def get_peaks(x): _ = np.negative(x) _ = scipy.signal.savgol_filter(_, 51, 3) peaks, properties = scipy.signal.find_peaks(_, prominence=5, width=40) return sum(map(lambda i: i>12, properties["prominences"]))
The sum in the last line filters all peaks with a prominence higher than 12 and only sums them.
Another example with 100 rows:
The full code: https://github.com/mfa/rowing-count/