Soo Jin Lee's profile

GraIce | Wearable Tech for Male Figure Skaters

GraIce Final Prototype | IAT 320
Team
Project Concept: SooJin Lee
Costume Design and Sewing: SooJin Lee
Sewing Assistants: Ryan Jeon, Jasmine Han
Coding and Testing: Ryan Jeon, SooJin Lee
Presentation Slide, Demo Video: Jasmine Han (Director), Ryan Jeon
What is GraIce? 
GraIce is an interactive garment for male dancers. The inspiration for the project came from male figure skaters and their hand/limb gestures used during step sequence especially. Our wearable technology recognizes tilting and moving of user's body and sends out sparkling LED lights as feedback. As for the LEDs implemented around the waist, it captures sound frequency and displays it as LED lights. The colour of lights goes from red to white to and turns on from sides of user's body to the front of the torso. The sound sensor captures the music used for figure skating and converts it in the LED lights displaying intensity of music and the beat. 
Aesthetics
Inspired by men's and women's fashion from 1800s to 1900s, GraIce costume mixes masculine and feminine qualities together. The emphasis on waist to resemble corset and usage of lace expresses feminine quality while the dark colours and beads layout inspired by embellishments on military coat to communicate the old value of what masculinity used to be. 
Individually, I worked on the final form and aesthetic components of the outfit. As a team, we worked on the wiring and the placement of technology on the garment.  
Consideration of different bead patterns. We later learned that bead could affect the conductive thread stitches since beads contained conductive material. The placement of beads on the garment needed to be considered in order for the tech components to work without any interference to the circuit.
First Costume Design Idea 
Past Costume Design Considerations 
We tried to emphasize the arm area and the waist area. 
After figuring out all the technology and the overall aesthetic look of the costume, I sketched out the patterns we need for the project.
Technical Aspect & First Prototype
Materials used: LEDs, neopixels, Adafruit flora board, accel/gyro sensor,  sound sensor, organza, and dark fabric.

After testing with lux sensor and neopixel LEDs, we decided that the best choice of technology to use was to have two sets of LED strip with different sensors in each set. Originally, we wanted to use LED strip with our sound sensor but due to large battery voltage requirement, we decided to go with simpler setup to consider the total weight of the garment. This decision can affect comfort of the wearer because figure skater has to be flexible and comfortable enough to perform poses requiring stretching of limbs and extreme movements. 

Setup 1. Gyroscope + Neopixels
Setup 2. Sound Sensor + Neopixels. 
Gyroscope test with two neopixels on same digital pin. 
Concept sketches for mapping out where to sew the tech components on the clothing.
Different options for layering tech and sleeve fabrics. 
Arduino Code for Gyroscope

#include <Wire.h>
#include <Adafruit_LSM9DS0.h>
#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel strip = Adafruit_NeoPixel(12, 10, NEO_GRB + NEO_KHZ800);
Adafruit_LSM9DS0 lsm;
 
uint8_t myFavoriteColors[][4] = {{0,   117,   255},   // blue 
                                 {200, 200, 200},   // white
                                 {100, 149, 237}, //Cornflower Blue
                               };

#define FAVCOLORS sizeof(myFavoriteColors) / 3
// lower number = more sensitive
#define MOVE_THRESHOLD 700
 
void setup() 
{
  Serial.begin(9600);
  if (!lsm.begin())
  {
//    Serial.println("Oops ... unable to initialize the LSM303. Check your wiring!");
    while (1);
  }
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}
 
void loop() 
{
  // Take a reading of Gyroscope data
  lsm.read();
  double storedVector = lsm.gyroData.x*lsm.gyroData.x;
  storedVector += lsm.gyroData.y*lsm.gyroData.y;
  storedVector += lsm.gyroData.z*lsm.gyroData.z;
  storedVector = sqrt(storedVector);
//  Serial.print("Len: "); Serial.println(storedVector);
  
  // wait a bit
  delay(100);
  
  // get new data!
  lsm.read();
  double newVector = lsm.gyroData.x*lsm.gyroData.x;
  newVector += lsm.gyroData.y*lsm.gyroData.y;
  newVector += lsm.gyroData.z*lsm.gyroData.z;
  newVector = sqrt(newVector);
//  Serial.print("New Len: "); Serial.println(newVector);
  
  // are we moving 
  if (abs(newVector - storedVector) > MOVE_THRESHOLD) {
//    Serial.println("Twinkle!");
    strip.setBrightness(100);
    flashRandom(1, 3);  // first number is 'wait' delay, shorter num == shorter twinkle
    flashRandom(5, 7);  // second number is how many neopixels to simultaneously light up
    flashRandom(3, 12);
  } else {
    strip.setBrightness(30);
    colorWipe(strip.Color(88, 11, 28), 175); // wine red
    colorWipe(strip.Color(133, 39, 78), 200); // cherry
  }
}
 
void flashRandom(int wait, uint8_t howmany) {
 
  for(uint16_t i=0; i<howmany; i++) {
    // pick a random favorite color!
    int c = random(FAVCOLORS);
    int red = myFavoriteColors[c][0];
    int green = myFavoriteColors[c][1];
    int blue = myFavoriteColors[c][2]; 
 
    // get a random pixel from the list
    int j = random(strip.numPixels());
    //Serial.print("Lighting up "); Serial.println(j); 
    
    // now we will 'fade' it in 5 steps
    for (int x=0; x < 5; x++) {
      int r = red * (x+1); r /= 5;
      int g = green * (x+1); g /= 5;
      int b = blue * (x+1); b /= 5;
      
      strip.setPixelColor(j, strip.Color(r, g, b));
      strip.show();
      delay(wait);
    }
    // & fade out in 5 steps
    for (int x=5; x >= 0; x--) {
      int r = red * x; r /= 5;
      int g = green * x; g /= 5;
      int b = blue * x; b /= 5;
      
      strip.setPixelColor(j, strip.Color(r, g, b));
      strip.show();
      delay(wait);
    }
  }
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
Arduino Code for Sound Sensor
#include <Adafruit_NeoPixel.h>
#include <Adafruit_DotStar.h>
 
#define N_PIXELS  10  // Number of pixels you are using
#define MIC_PIN    9  // Microphone is attached to Trinket GPIO #2/Gemma D2 (A1)
#define LED_PIN    12  // NeoPixel LED strand is connected to GPIO #0 / D0
#define DC_OFFSET  0  // DC offset in mic signal - if unusure, leave 0
#define NOISE     100  // Noise/hum/interference in mic signal
#define SAMPLES   60  // Length of buffer for dynamic level adjustment
#define TOP       (N_PIXELS +1) // Allow dot to go slightly off scale
 
byte
  peak      = 0,      // Used for falling dot
  dotCount  = 0,      // Frame counter for delaying dot-falling speed
  volCount  = 0;      // Frame counter for storing past volume data
  
int
  vol[SAMPLES],       // Collection of prior volume samples
  lvl       = 10,     // Current "dampened" audio level
  minLvlAvg = 0,      // For dynamic adjustment of graph low & high
  maxLvlAvg = 512;
 
Adafruit_NeoPixel  strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
 
void setup() {
  memset(vol, 0, sizeof(vol));
  strip.begin();
}
void loop() {
  uint8_t  i;
  uint16_t minLvl, maxLvl;
  int      n, height;
  n   = analogRead(MIC_PIN);                 // Raw reading from mic 
  n   = abs(n - 512 - DC_OFFSET);            // Center on zero
  n   = (n <= NOISE) ? 0 : (n - NOISE);      // Remove noise/hum
  lvl = ((lvl * 7) + n) >> 3;    // "Dampened" reading (else looks twitchy)
  
  // Calculate bar height based on dynamic min/max levels (fixed point):
  height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
 
  if(height < 0L)       height = 0;      // Clip output
  else if(height > TOP) height = TOP;
  if(height > peak)     peak   = height; // Keep 'peak' dot at top
 
  // Color pixels based on rainbow gradient
  for(i=0; i<N_PIXELS; i++) {  
    if(i >= height)               
       strip.setPixelColor(i,   0,   0, 0);
    else 
       strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
    } 
 
   strip.show(); // Update strip
 
  vol[volCount] = n;                      // Save sample for dynamic leveling
  if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
 
  // Get volume range of prior frames
  minLvl = maxLvl = vol[0];
  for(i=1; i<SAMPLES; i++) {
    if(vol[i] < minLvl)      minLvl = vol[i];
    else if(vol[i] > maxLvl) maxLvl = vol[i];
  }
  // minLvl and maxLvl indicate the volume range over prior frames, used
  // for vertically scaling the output graph (so it looks interesting
  // regardless of volume level).  If they're too close together though
  // (e.g. at very low volume levels) the graph becomes super coarse
  // and 'jumpy'...so keep some minimum distance between them (this
  // also lets the graph go to zero when no sound is playing):
  if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
  minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
  maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
}
 
// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 75) {
   return strip.Color(98, 20, 80);
  } else if(WheelPos < 170) {
   WheelPos -= 75;
   return strip.Color(100, 149, 237);
  } else {
   WheelPos -= 170;
   return strip.Color(0, 117, 200);
  }
}
GraIce | Wearable Tech for Male Figure Skaters
Published:

GraIce | Wearable Tech for Male Figure Skaters

Project Concept: SooJin Lee Costume design: SooJin Lee Sewing assistants: Ryan Jeon, Jasmine Han Coding and testing: Ryan Jeon, SooJin Lee Prese Read More

Published: