🔍

ONE BUTTON AI _v2.0

UPDATED_PROCESSING_OBAI

„ONE BUTTON AI“ ist ein minimalistischer interaktiver Prototyp, der sich mediativ mit dem Trainingsprozess künstlicher Intelligenz auseinandersetzt.

Das Stück besteht nur aus einem Arcade-Button und einem radialen RGB-LED-Ring. Das Konzept ist bewusst minimalistisch gehalten: Per Knopfdruck trainiert der Nutzer ein sehr einfaches KI-System. Die Häufigkeit und Dauer des Tastendrucks wird in Trainingsdaten für ein einfaches künstliches neuronales Netz umgewandelt und dieses damit trainiert.

Das neuronale Netzwerk wird zudem auf einem Bildschirm visualisiert, auf dem der Benutzer beobachten kann, wie sich das Netzwerk mit jedem Tastendruck anpasst und entwickelt.

Der Lichtring und der eingebaute Lautsprecher geben zudem ein direktes visuelles Feedback auf die Eingabe des Benutzers in Form einer KI-Antwort auf die Eingabe des Benutzers.

OBAI demonstriert damit grundlegend die Funktionalität von KI-Training.
Es provoziert eine spielerische Auseinandersetzung mit den Möglichkeiten und Grenzen von KI und fördert ein tieferes Verständnis und damit die Befähigung, sich ein eigenes Bild von dieser Technologie machen zu können.


updated prototype

The first functional prototype inherits the most minimalistic component setup. An Arduino Micro Pro, Adafruit Neopixel Ring 12 and a simple PiezzoBuzzer. The prototype runs completely standalone, but can be completed with an additional Processing based visualization.

https://github.com/bretterbernd/one_button_ai.git

Arduino/Processing code : one_button_ai_v2_01

inital prototype


simple network visualization

The prototype sends a simple array of network structure data via serial port. With Processing, we can create a simple realtime visualization of the state of the neuronal network.


ArrayList arr = new ArrayList();

import processing.serial.*;

 PImage vignette;
 
 PFont fonti;

Serial serialPort; // Declare a Serial object
String message = ""; // Declare a String to store the received message
boolean recording = false;
boolean initalized = false;
int training_count = 0;
 

void setup() {
  size(600, 800);
  
  fonti = loadFont("gothi.vlw");
  textFont(fonti);
  
  printArray(Serial.list());
  serialPort = new Serial(this, Serial.list()[0], 115200); // Initialize the Serial object with the correct port and baud rate
 
  
  resetLocalDB();
  initAllNodes();

  vignette = loadImage("vignette.png");
  
}

void draw() {
  
  // ----------------------------------------
  // ----------------------------------------
  // ----------------------------------------
  
  translate(width/2,height/2);
  
   
    background(255);
  
  
    // ----------------------------------------
  // ----------------------------------------
  // ----------------------------------------
  
  // Check if there is any data available in the serial buffer
  while (serialPort.available() > 0) {
    message = serialPort.readStringUntil('\n'); // Read the data from the serial buffer until a newline character is found
    message = trim(message); // Remove any whitespace characters from the beginning and end of the message
  
  //  println(message);
    if(message == null){return;}
     
     if(message.equals("end")){
    
        println("have new DATA");
        recording = false;
        parseData();
    }
    
    if(recording){
      
       String[] msg_arr = message.split(",");
        //println("msg_arr" + msg_arr.length);
       
          aCON nc = new aCON();
          nc.id = int(msg_arr[0]);
          nc.sid = int(msg_arr[1]);
          nc.oid = int(msg_arr[2]);
          nc.weight = int(msg_arr[3]);
          nc.sNODE = allNODES[nc.sid];//returnNodeByID(nc.sid);
          nc.oNODE = allNODES[nc.oid];//returnNodeByID(nc.oid);
          arr.add(nc);
        
         println("new con: " +  nc.sid + "::" + nc.oid);
          /*
         
          
          println("hea: " + allNODES[0]);
          println("hea: " + allNODES.length);
          println("hea: " + allNODES[11]);
          
          println(" ---------- new connection -------------");
          println( nc.sNODE.id);
          println( nc.oNODE.id);
          */
          //add new connection to SUB_NODE
           nc.sNODE.con_ids[nc.sNODE.con_buff_count] = nc.id;
           nc.sNODE.con_buff_count++;
          
          println(" ---------- new connection -------------");
          println( nc.oNODE.id);
          println( nc.sNODE.con_buff_count );
          
           //add new connection to OBJ_NODE
          // nc.oNODE.con_ids[nc.oNODE.con_buff_count]  = nc.id;
           //nc.oNODE.con_buff_count++;
           
        
    }
    
     if(message.equals("init")){
    
          resetLocalDB();
          resetAllNodes();
          recording = true;
          println("start recording");
    }
}
  
  // ----------------------------------------
  // ----------------------------------------
  // ----------------------------------------
  
   if(!recording){
     
    float a = TWO_PI/(12 );
    for(int i=0;i<12;i++){
      
      float x = sin(-a*i+PI)*170;
       float y = cos(a*i+PI)*170;
       
       x += sin( millis()*.001 + i*.3 )*10;
       y += cos( millis()*.002 + i*.23 )*10;
       
       aNODE cn = allNODES[i];
       
       cn.xpos = x;
       cn.ypos = y;
       //stroke(22);
       
       
       if(cn.con_buff_count>0){
         fill(cn.ncolor);
       }else{
         fill(222);
       }
         noStroke();
        
         circle(x,y,10+cn.con_buff_count*2);
         
        
         
          fill(0);
         circle(x,y,6);
       // text("->" + cn.con_buff_count, x+12,y+12);
        
        
        for(int j=0;j<cn.con_buff_count-1;j++){
          
           aCON cc = returnConnectionByID(cn.con_ids[j]);
           
          // println(cc);
           //line( cc.oNODE.xpos, cc.oNODE.ypos,x,y);
           noFill();
           stroke(0,44+cc.weight*.12);
           strokeWeight(cc.weight*.003+1);
          
           float cx = (cc.oNODE.xpos+x)*(.3 + cc.weight*.0015);
           float cy = (cc.oNODE.ypos+y)*(.3 + cc.weight*.0015);
           
           if(cc.oNODE.id == cn.id){
             
                 // if its the same node > just draw a loop circle!
                circle(x,y,44);
                
           }else{
               
              /*
               pushMatrix();
               textAlign(CENTER);
               translate(
               (x+cc.oNODE.xpos)*.5,
               (y+cc.oNODE.ypos)*.5
               );
               rotate(.6);
               text(cc.weight,20,0);
               popMatrix();
               
               
               
               line(
               
               cc.oNODE.xpos,
               cc.oNODE.ypos,
               x,y
               );
               
               */
               
              
               bezier(
               cc.oNODE.xpos,
               cc.oNODE.ypos,
               cx,
               cy,
               cx,
               cy,
               x,y);
               
           }
        
        }
        
        // draw the label
        textAlign(LEFT);
          noStroke();
         fill(255,120);
         rect(x+12,y-10,60,20);
        // stroke(0,99);
         fill(0,111);
         textSize(12);
         text(cn.timeval+"ms",x+15,y+4);
      
    }
  
   } // end if not recording
  
  
  if(recording){
    fill(222,0,0);
    circle(0,22,22);
    text("NEW DATA",-10,0);
  
  }
  
  image(vignette,-width/2.,-height/2.,float(width),float(height));
  
  text("CONNECTIONS: " + arr.size(), -width/2.+20,-height/2.+20);
  text("TRAINING COUNT: " + training_count, -width/2.+20,-height/2.+36);
  
  
}

//---------------------------------------------
//---------------------------------------------
//---------------------------------------------

aNODE[] allNODES ;
int timesteps[] = { 50,100,150,200,250,300,400,500,600, 700, 800, 900 };
 
 void resetAllNodes(){

      

    for(int i=0;i<12;i++){
      
      allNODES[i].con_buff_count=0;
      
    }
    
 }
 
 
void initAllNodes(){

     allNODES  = new aNODE[12];

    for(int i=0;i<12;i++){
      
     // println("init_NODE: " + i);
       aNODE nn = new aNODE(i);
       allNODES[i]  = nn;

    }
  initalized = true;
 
}


void resetLocalDB(){

           //println("have init");
          arr = new ArrayList();
         

}

color from = color(85,200,255);
color to = color(175,41,175); 

class aNODE{

  int id = -1;
  int[] con_ids = new int[32];
  int con_buff_count = 0;
  float xpos = 0;
  float ypos = 0;
  color ncolor = 0; 
  int timeval = 0;
  
  aNODE(int _id){
  
    
    id = _id;
    ncolor = lerpColor(from, to, float(id)/12. ); 
     
     timeval = timesteps[id];
}

}

aNODE returnNodeByID( int _id){

  //int rid = -1;
    for(int i=0;i< 12;i++){
    
     aNODE cn = allNODES[i];
       if(cn.id == _id){
       
           return cn;
       }
        
    }
    
    // found no node, so create it!
   // aNODE nn = new aNODE(_id);
    // allNODES[i] = nn;
   // allNODES.add(nn);
    
    return null;

}

void parseData(){


  

}



aCON returnConnectionByID(int _cid){

   for(int i=0;i< arr.size();i++){
    
     aCON cc = (aCON)arr.get(i);
     if(cc.id == _cid){
       return cc;
     }
     
   }
    
  return null;
}


aCON allCONS[];

class aCON{
  
  int id = -1;
  int sid = -1;
  int oid = -1;
  int weight = 0;
  aNODE sNODE = null;
  aNODE oNODE = null;
  
  
  aCON(){
    
    
    
  
  }

}

wall mounted setup