Puara Gestures
Note: Article still a Work in Progress
Read More
- Source Code
- Documentation
High-Level gesture vocabulary for the T-Stick
Presented codes are arduino compatible.
Summary:
- Puara Gestures
- Read More
- High-Level gesture vocabulary for the T-Stick
Signal structures and global variables:
byte touchByteSize = 2; // bytes necessary to represent all the stripes (1-bit per stripe) = 2 for the sopranino
byte touchSizeAll = touchByteSize * 8 ; // total amount of T-Stick stripes (8 bits per byte)
byte touchSizeEdge = 4; // amount of T-Stick stripes for top and bottom portions of the T-Stick (arbitrary)
float leakyConstant = 0.05;
float shakeArray[5] = {0,0,0,0,0};
byte shakeArrayindex = 0;
float jabArray[5] = {0,0,0,0,0};
byte jabArrayindex = 0;
float shakeAccum = 0;
float cartopolAmplitude = 0;
float cartopolAngle = 0;
struct RawDataStruct { // initialized as RawData
byte touch[touchByteSize]; // /raw/capsense, i..., 0--255, ... (1 int per 8 capacitive stripes -- 8 bits)
float fsr; // /raw/fsr, i, 0--4095
float piezo; // /raw/piezo, i, 0--1023
float accl[3]; // /raw/accl, fff, +/-16, +/-16, +/-16 (g linear acceleration full scale), converted from sensor data with range +/-32767 (integers)
float gyro[3]; // /raw/gyro, fff, +/-2000, +/-2000, +/-2000 (dps angular rate full scale), converted from sensor data with range +/-34.90659 (floats)
float magn[3]; // /raw/magn, iii, fff, +/-16, +/-16, +/-16 (gauss magnetic full scale), converted from sensor data with range +/-32767 (integers)
float raw[9]; // /raw (IMU data to be send to callibration app)
float quat[4]; // /raw/quat, ffff, ?, ? ,? ,?
float ypr[3]; // /raw/ypr, fff, ?, ? ,?
};
struct NormDataStruct { // initialized as NormData
float fsr; // /norm/fsr, f, 0--1
float piezo; // /norm/piezo, f, 0--1
float accl[3]; // /norm/accl, fff, +/-1, +/-1, +/-1
float gyro[3]; // /norm/gyro, fff, +/-1, +/-1, +/-1
float magn[3]; // /norm/magn, fff, +/-1, +/-1, +/-1
};
struct LastStateDataStruct { // initialized as LastStateData
byte touch[touchByteSize]; // last state of /raw/capsense
byte brushUp;
byte brushDown;
float shake;
float shakeAccum;
};
struct InstrumentDataStruct { // initialized as InstrumentData
byte touchAll[touchByteSize]; // surface contact
byte touchTop;
byte touchBody[touchByteSize-1];
byte touchMiddle[touchByteSize-2];
byte touchBottom;
byte brushUp;
byte brushDown;
byte brushAmplitude;
byte energy;
float amplitude;
float roll;
float tilt;
float shake;
float jerk;
};
functions
- Implement bit read/write function (not needed for arduino, bitRead and bitWrite function is already included):
bool bitRead(byte target, byte index){
return ((target >> index) & 1U);
}
void bitWrite(byte destiny, byte index, byte origin){
if (bitRead(destiny, index) != bitRead(origin, index)) {
destiny.flip(index);
}
}
- Implement function to calculate mean for a given bit range
float bitMean (byte capsense_array[], byte first_bit, byte last_bit) { // calculates mean for a given bit range
float mean = 0;
For ( i = first_bit; i < last_bit; i++ ) {
byte array_index = i/8;
byte target_index = i - (8 * array_index);
If ( bitRead(capsense_array[array_index], target_index) ) { // read each bit ...
mean += 1/ (last_bit - first_bit); // ... and add if its high
}
}
return mean;
}
- Leaky integrator:
float leakyIntegrator (float reading, float old_value,float leak) {
return reading + (old_value * leak)
}
- Windowed Extrema (getting max and min values out of a window). Dont forget to populate
shakeArray
float windowedExtremaMax (float array[]) {
float result = 0;
For ( i=0; i < sizeof(array)/sizeof(array[0]); i++ ) {
result = max(result, array[i]);
}
return result;
}
float windowedExtremaMin (float array[]) {
float result = 0;
For ( i=0; i < sizeof(array)/sizeof(array[0]); i++ ) {
result = min(result, array[i]);
}
return result;
}
- 1st order IIR filter for sensor data:
float lowPassFilter (data,olddata,k) {
return olddata + ((data - olddata)/k)
}
/instrument
/instrument/touch/
/instrument/touch/all (InstrumentData.touchAll)
- InstrumentData.touchAll: get the "amount of touch" normalized between 0 and 1
InstrumentData.touchAll = bitMean(RawData.touch, 0, touchSizeAll);
/instrument/touch/top (InstrumentData.touchTop)
- InstrumentData.touchTop: similar to /instrument/touch/all, but applied only to the "top" region of the capsense
InstrumentData.touchTop = bitMean(RawData.touch, 0, touchSizeEdge);
/instrument/touch/body (InstrumentData.touchBody)
- InstrumentData.touchBody: similar to /instrument/touch/all, but applied only to the "body" region of the capsense
InstrumentData.touchBody = bitMean(RawData.touch, touchSizeEdge, touchSizeAll);
/instrument/touch/middle (InstrumentData.touchMiddle)
- InstrumentData.touchMiddle: similar to /instrument/touch/middle, but applied only to the "middle" region of the capsense
InstrumentData.touchMiddle = bitMean(RawData.touch, touchSizeEdge, (touchSizeAll - touchSizeEdge) );
/instrument/touch/bottom (InstrumentData.touchBottom)
- InstrumentData.touchBottom: similar to /instrument/touch/all, but applied only to the "top" region of the capsense
InstrumentData.touchBottom = bitMean(RawData.touch, (touchSizeAll - touchSizeEdge), touchSizeAll);
/instrument/brush
/instrument/brush/up /down
operation_up = (LastStateData.brushUp & RawData.touch)
operation_down = (LastStateData.brushDown & RawData.touch)
For ( i = 0; i < number_of_touch; i++ ) {
if ( bitRead(operation_up, i) ) {
InstrumentData.brushUp = (number_of_touch - i) / number_of_touch;
mean_up += 1/ number_of_touch;
}
if ( bitRead(operation_down, i) ) {
InstrumentData.brushDown = (number_of_touch - i) / number_of_touch;
mean_down += 1/ number_of_touch;
}
}
/instrument/brush/amplitude (brush)
InstrumentData.brushAmplitude = InstrumentData.brushDown - InstrumentData.brushUp
/instrument/brush/energy (rub)
if (InstrumentData.brushUp > InstrumentData.brushDown) {
InstrumentData.energy = InstrumentData.brushUp
}
else {
InstrumentData.energy = InstrumentData.brushDown
}
/instrument/amplitude /roll /tilt
(range +/- 2 ?)
cartopolAmplitude = sqrt( pow((((RawData.accl[1] * 2) - 128) * -1),2) + pow((((RawData.accl[2] * 2) - 128) * -1)) )
cartopolAngle = tan-1 ( (((RawData.accl[2] * 2) - 128) * -1) / (((RawData.accl[1] * 2) - 128) * -1) )
InstrumentData.amplitude = sqrt( pow(cartopolAmplitude) + pow(((RawData.accl[0] * 2) - 128)) )
InstrumentData.roll = cartopolAngle + 3.14159
InstrumentData.tilt = tan-1 ( ((RawData.accl[0] * 2) - 128) / cartopolAmplitude)
/instrument/shake /jerk
// populating shakeArray each loop)
shakeArray[shakeArrayindex] = InstrumentData.amplitude;
if (shakeArrayindex < 5) {
shakeArrayindex += 1;
}
else {
shakeArrayindex = 0;
}
shakeAccum = -(windowedExtremaMax(shakeArray[]) - windowedExtremaMin(shakeArray[]));
shakeAccum = leakyIntegrator(shakeAccum,LastStateData.shakeAccum,leakyConstant);
InstrumentData.shake = lowPassFilter(shakeAccum,LastStateData.shake,20);
InstrumentData.jerk = shakeAccum - InstrumentData.shake;
/instrument/jab (INCOMPLETE)
// populating jabArray each loop)
shakeArray[jabArrayindex] = InstrumentData.jab;
if (jabArrayindex < 5) {
jabArrayindex += 1;
}
else {
jabArrayindex = 0;
}
InstrumentData.jab = windowedExtremaMax(jabArray[]) - windowedExtremaMin(jabArray[]);
RawData.accl[0]
Andrew
thrust
shake
spin
frame (populated inside?)
rest vs effort
Travis
Jab
eggbeater
eggwhip
tilt
hold
squeez
#### Update Last State Values
LastStateData.brushUp = (RawData.touch << 1) & (~ RawData.touch) LastStateData.brushDown = (RawData.touch >> 1) & (~ RawData.touch)