For this assignment, I chose the image teachable machine and taught it two different commands. The first command was a high-five, labeled Start, and the second command was a hand fist, labeled Stop. As shown in the images below, I collected roughly 130 images to train each model. The results seemed quite accurate and the only issue I experienced was if there was no hand at all, it typically defaulted to “Start”. In the future, I think implementing a null model would be best to handle situations like this, in which there should not be any output.
Next, I made use of the p5.js template provided by **yining1023** and included the link provided by the teachable machine, allowing p5 to project the results of the machine learning image model. The template only required small changes to variables and from there it was good to go. As shown in the images below, the p5 canvas projected “Stop” when a fist appeared, and “Start” when a hand appeared.
Lastly, I configured the Arduino template provided by Yining to receive values from serial communication through the p5.serialControl app and uploaded the code to a previous LED strip project I worked on a few years ago (Lucid Doodle). I ran into some errors here with p5 timing out, but I’m uncertain if that was my computer or some sort of bug. Regardless, the end results allowed me to control my LED box with my hands, turning the lights on with a high-five and turning them off with a fist. I am really impressed how simple this was to implement given all the computing involved and really excited to dive deeper. I want to explore this same concept but with advance gestures and effects on the lights. I plan on making a new version of Lucid Doodle, but this time powered with Machine Learning as previewed here.
const myImageModelURL = '<https://teachablemachine.withgoogle.com/models/T0J7iE4bh/>';
let myImageModel;
let resultDiv;
let serial;// variable to hold an instance of the serialport library
let portName = '/dev/cu.usbmodem144201';// fill in your serial port name here
let outByte = 0;// for outgoing data
let video;
function preload() {
video = createCapture(VIDEO);
myImageModel = ml5.imageClassifier(myImageModelURL+ 'model.json');
}
function setup() {
resultDiv = createElement('h1', '...');
serial = new p5.SerialPort(); // make a new instance of the serialport library
serial.on('error', serialError); // callback for errors
serial.open(portName); // open a serial port
myImageModel.classify(video, gotResults);
}
function serialError(err) {
console.log('Something went wrong with the serial port. ' + err);
}
function gotResults(err, results) {
if (err) console.log(err);
if (results) {
console.log(results);
// Wait for 0.5 second before classifying again
setTimeout(() => myImageModel.classify(video, gotResults), 500);
if (results[0].confidence < 0.7) return;
resultDiv.html('Result is: ' + results[0].label);
console.log(results[0].label);
if (results[0].label === 'wave') {
outByte = 1;
}
// else if (results[0].label === 'paper') {
// outByte = 2;
// } else if (results[0].label === 'scissor') {
// outByte = 3;
// }
else {
outByte = 0;
}
// send it out the serial port:
console.log('outByte: ', outByte)
serial.write(outByte);
}
}
//
// "Pacifica"
// Gentle, blue-green ocean waves.
// December 2019, Mark Kriegsman and Mary Corey March.
// For Dan.
//
#define FASTLED_ALLOW_INTERRUPTS 0
#include <FastLED.h>
FASTLED_USING_NAMESPACE
#define LED_PIN 6
#define NUM_LEDS 49
//#define MAX_POWER_MILLIAMPS 500
#define COLOR_ORDER GRB
//////////////////////////////////////////////////////////////////////////
CRGB leds[NUM_LEDS];
void setup() {
Serial.begin(9600); // initialize serial communications
FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS)
.setCorrection( TypicalLEDStrip );
// FastLED.setMaxPowerInVoltsAndMilliamps( 5, MAX_POWER_MILLIAMPS);
}
void loop()
{
if (Serial.available() > 0) { // if there's serial data available
int inByte = Serial.read(); // read it
if (inByte == 1) {
EVERY_N_MILLISECONDS( 20) {
pacifica_loop();
FastLED.show();
}
} else if (inByte == 0) {
FastLED.clear();
}
}
}
//////////////////////////////////////////////////////////////////////////
//
// The code for this animation is more complicated than other examples, and
// while it is "ready to run", and documented in general, it is probably not
// the best starting point for learning. Nevertheless, it does illustrate some
// useful techniques.
//
//////////////////////////////////////////////////////////////////////////
//
// In this animation, there are four "layers" of waves of light.
//
// Each layer moves independently, and each is scaled separately.
//
// All four wave layers are added together on top of each other, and then
// another filter is applied that adds "whitecaps" of brightness where the
// waves line up with each other more. Finally, another pass is taken
// over the led array to 'deepen' (dim) the blues and greens.
//
// The speed and scale and motion each layer varies slowly within independent
// hand-chosen ranges, which is why the code has a lot of low-speed 'beatsin8' functions
// with a lot of oddly specific numeric ranges.
//
// These three custom blue-green color palettes were inspired by the colors found in
// the waters off the southern coast of California, <https://goo.gl/maps/QQgd97jjHesHZVxQ7>
//
CRGBPalette16 pacifica_palette_1 =
{ 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 };
CRGBPalette16 pacifica_palette_2 =
{ 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117,
0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F };
CRGBPalette16 pacifica_palette_3 =
{ 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33,
0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF };
void pacifica_loop()
{
// Increment the four "color index start" counters, one for each wave layer.
// Each is incremented at a different speed, and the speeds vary over time.
static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4;
static uint32_t sLastms = 0;
uint32_t ms = GET_MILLIS();
uint32_t deltams = ms - sLastms;
sLastms = ms;
uint16_t speedfactor1 = beatsin16(3, 179, 269);
uint16_t speedfactor2 = beatsin16(4, 179, 269);
uint32_t deltams1 = (deltams * speedfactor1) / 256;
uint32_t deltams2 = (deltams * speedfactor2) / 256;
uint32_t deltams21 = (deltams1 + deltams2) / 2;
sCIStart1 += (deltams1 * beatsin88(1011,10,13));
sCIStart2 -= (deltams21 * beatsin88(777,8,11));
sCIStart3 -= (deltams1 * beatsin88(501,5,7));
sCIStart4 -= (deltams2 * beatsin88(257,4,6));
// Clear out the LED array to a dim background blue-green
fill_solid( leds, NUM_LEDS, CRGB( 2, 6, 10));
// Render each of four layers, with different scales and speeds, that vary over time
pacifica_one_layer( pacifica_palette_1, sCIStart1, beatsin16( 3, 11 * 256, 14 * 256), beatsin8( 10, 70, 130), 0-beat16( 301) );
pacifica_one_layer( pacifica_palette_2, sCIStart2, beatsin16( 4, 6 * 256, 9 * 256), beatsin8( 17, 40, 80), beat16( 401) );
pacifica_one_layer( pacifica_palette_3, sCIStart3, 6 * 256, beatsin8( 9, 10,38), 0-beat16(503));
pacifica_one_layer( pacifica_palette_3, sCIStart4, 5 * 256, beatsin8( 8, 10,28), beat16(601));
// Add brighter 'whitecaps' where the waves lines up more
pacifica_add_whitecaps();
// Deepen the blues and greens a bit
pacifica_deepen_colors();
}
// Add one layer of waves into the led array
void pacifica_one_layer( CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff)
{
uint16_t ci = cistart;
uint16_t waveangle = ioff;
uint16_t wavescale_half = (wavescale / 2) + 20;
for( uint16_t i = 0; i < NUM_LEDS; i++) {
waveangle += 250;
uint16_t s16 = sin16( waveangle ) + 32768;
uint16_t cs = scale16( s16 , wavescale_half ) + wavescale_half;
ci += cs;
uint16_t sindex16 = sin16( ci) + 32768;
uint8_t sindex8 = scale16( sindex16, 240);
CRGB c = ColorFromPalette( p, sindex8, bri, LINEARBLEND);
leds[i] += c;
}
}
// Add extra 'white' to areas where the four layers of light have lined up brightly
void pacifica_add_whitecaps()
{
uint8_t basethreshold = beatsin8( 9, 55, 65);
uint8_t wave = beat8( 7 );
for( uint16_t i = 0; i < NUM_LEDS; i++) {
uint8_t threshold = scale8( sin8( wave), 20) + basethreshold;
wave += 7;
uint8_t l = leds[i].getAverageLight();
if( l > threshold) {
uint8_t overage = l - threshold;
uint8_t overage2 = qadd8( overage, overage);
leds[i] += CRGB( overage, overage2, qadd8( overage2, overage2));
}
}
}
// Deepen the blues and greens
void pacifica_deepen_colors()
{
for( uint16_t i = 0; i < NUM_LEDS; i++) {
leds[i].blue = scale8( leds[i].blue, 145);
leds[i].green= scale8( leds[i].green, 200);
leds[i] |= CRGB( 2, 5, 7);
}
}