CS 470, Sometimes Rizz Doesnt Work

Pkmazarakis
6 min readMar 23, 2023

--

An AI story of love. This project was incredibly fun to build. It started with learning to use Wekinator, moved into how to transmit data into an arduino, and then putting the whole system together to create a moving robot. It was so frustrating to have what in my head was such an amzing milestone only to not bring it home and have the best final project presentation. But, its funny because I am actually way more happy with this ending then I probably would have been with a working 2D robot drawer. The reasons are as follows. First, the project fully worked in the end, the wekinator read my hand inputs and the code successfully extrapolated them and converted them into x, y coordinates and then the arduino successfully converted those x’s and y’s into wireLength1’s and wireLength2’s. The only part that broke in the end was the tension of the track wire to the motor which messed up the calculations by being a bit loose. I definitely never expected to come even 1/10th of the way there at the beginning of the quarter. The second reason is because I actually love the story that was told in the end. Sometimes you have to accept failure and learn from it. This acceptance was so healing. Like actually! Instead of feeling totally bummed and feeling like a failed, I recognize the hoops I had to jump through and also that I LOVE telling stories. While this first story had many rough edges, I still loved it and felt as though some people could relate to what I was attempting to express. In the end, that is what is truly beautiful, the fact that I was able to bring out a certain emotion in people.

How it was done:

  • I trained Wekinator to map the hands on an x and y plane, so bottom left hand corner was (0,0) and top right corner was (1,1).
  • This information was fed to a max pad which then converted those values on a scale from 0:12 and then passed it into the arduino.
  • The arduino code then took those two values and calculated the wireLengths that were necessary on both sides of the marker that was dangling to achieve that specific x and y value. So (0,0) was bottom right hand corner and that had to be 12 inches on the left wire and sqrt(12*12 + 12*12) since it is the hypotenuse of the triangle of the distance between both motors and the left edge.
  • Then, the arduino converts this length into # of steps each motor must take to get to that distance and then moves the motor in that direction.
#include <AccelStepper.h>

// Define the stepper motor connections and steps per revolution
#define motor1Step 8
#define motor1Dir 9
#define motor2Step 10
#define motor2Dir 11
#define stepsPerRevolution 200

// Define the maximum speed and acceleration of the stepper motors
#define maxSpeed 500
#define acceleration 500

// Define the coordinates of the two stepper motors and the distance between them
#define motor1Pos 0
#define motor2Pos 12 // 12 feet = 144 inches = 12 * 58
#define distanceBetweenMotors 12 // in inches

// Define the range of the x and y coordinates
#define minX 0
#define maxX 100
#define minY 0
#define maxY 100

float x, y;
byte buffer[8];
String inputString = "";

// Define the maximum distance between motors and the hanging point
#define maxDistance 29.0 * 0.393701 // in inches, converted from cm

// Create two stepper motor objects
AccelStepper motor1(AccelStepper::DRIVER, motor1Step, motor1Dir);
AccelStepper motor2(AccelStepper::DRIVER, motor2Step, motor2Dir);

void setup() {
// Set the maximum speed and acceleration of the stepper motors
motor1.setMaxSpeed(maxSpeed);
motor1.setAcceleration(acceleration);
motor2.setMaxSpeed(maxSpeed);
motor2.setAcceleration(acceleration);

// Initialize Serial communication
Serial.begin(9600);
}

void loop() {
// Wait for x and y inputs from Serial Monitor
if (Serial.available() >= 8) { // check if there are at least 8 bytes available (2 floats)

for (int i = 0; i < 8; i++) {
buffer[i] = Serial.read(); // read the incoming bytes one at a time
}
memcpy(&x, buffer, sizeof(float)); // convert the first 4 bytes to a float and assign to x
memcpy(&y, buffer+4, sizeof(float)); // convert the next 4 bytes to a float and assign to y
Serial.print("x = ");
Serial.print(x);
Serial.print(", y = ");
Serial.println(y);
Serial.println(x);
Serial.println(y);

// convert the next 4 bytes to a float and assign to y

// Clear the input string for the next coordinate pair
inputString = "";

// Calculate the lengths of the wires for each motor
// float wireLength1 = sqrt(pow(x, 2) + pow(y + 12, 2)); // Motor 1 at position (0, 100)
// float wireLength2 = sqrt(pow(12 - x, 2) + pow(y + 12, 2));
float wireLength1 = sqrt(pow(x, 2) + pow(y, 2)); // x = 50, y = 25
float wireLength2 = sqrt(pow(distanceBetweenMotors - x, 2) + pow(y, 2));

Serial.print("Wire Length 1: ");
Serial.println(wireLength1);
Serial.print("Wire Length 2: ");
Serial.println(wireLength2);

// Convert the wire lengths to steps for each motor
int steps1 = wireLength1 * stepsPerRevolution / maxDistance;
int steps2 = wireLength2 * stepsPerRevolution / maxDistance;

Serial.print("Steps 1: ");
Serial.println(steps1);
Serial.print("Steps 2: ");
Serial.println(steps2);

// Set the speed of the stepper motors based on the distance
motor1.setSpeed(maxSpeed);
motor2.setSpeed(maxSpeed);

// Move the stepper motors to the target positions
motor1.moveTo(steps1);
motor2.moveTo(steps2);


while (motor1.distanceToGo() != 0 || motor2.distanceToGo() != 0) {
motor1.run();
motor2.run();
}
}
}
//----------------------------------------------------------------------------
// name: face-relay.ck
// desc: relay FaceOSC messages to Wekinator
//
// get FaceOSC here (and also see the OSC message it sends)
// https://github.com/kylemcdonald/ofxFaceTracker/releases
//
// author: Ge Wang (https://ccrma.stanford.edu/~ge/)
// date: Winter 2023
//----------------------------------------------------------------------------

// our OSC receiver (from FaceOSC)
OscIn oin;
// incoming port (from FaceOSC)
9527 => oin.port;
// our OSC message shuttle
OscMsg msg;
// listen for all message
oin.listenAll();

// destination host name
"localhost" => string hostname;
// destination port number: 6448 is Wekinator default
6448 => int port;
// our OSC sender (to Wekinator)
OscOut xmit;
// aim the transmitter at destination
xmit.dest( hostname, port );

// just two of the many parameters
float HANDS_X;
float HANDS_Y;
float MULTIPLE_HANDS;



// print
cherr <= "listening for messages on port " <= oin.port()
<= "..." <= IO.newline();

// spork the listener
spork ~ incoming();

// main shred loop
while( true )
{
// can do things here at a different rate
// for now, do nothing

// advance time
1::second => now;
}

// listener
fun void incoming()
{
// infinite event loop
while( true )
{
// wait for event to arrive
oin => now;

// grab the next message from the queue.
while( oin.recv(msg) )
{
// print message type
cherr <= "RECEIVED: \"" <= msg.address <= "\": ";
// print arguments
printArgs( msg );
<<< "x: ", msg.getFloat(4), " y: ", msg.getFloat(5), " hands: ", msg.getInt(2) >>>;


// handle message
if( msg.address == "/hands/arr" )
{
// save
msg.getFloat(4) => HANDS_X;
msg.getFloat(5) => HANDS_Y;
msg.getInt(2) => MULTIPLE_HANDS;
}

}

// reformat and relay message to Wekinator
send2wek();
}
}

// reformat and send what we want to Wekinator
fun void send2wek()
{
// start the message...
xmit.start( "/wek/inputs" );

// print
cherr <= " *** SENDING: \"/wek/inputs/\": "
<= HANDS_X <= " " <= HANDS_Y <= IO.newline();

// add each for sending
HANDS_X => xmit.add;
HANDS_Y => xmit.add;
MULTIPLE_HANDS => xmit.add;


// send it
xmit.send();
}

// print argument
fun void printArgs( OscMsg msg )
{
// iterate over

for( int i; i < msg.numArgs(); i++ )
{
if( msg.typetag.charAt(i) == 'f' ) // float
{
cherr <= msg.getFloat(i) <= " ";
}
else if( msg.typetag.charAt(i) == 'i' ) // int
{
cherr <= msg.getInt(i) <= " ";
}
else if( msg.typetag.charAt(i) == 's' ) // string
{
cherr <= msg.getString(i) <= " ";
}
}

// new line
cherr <= IO.newline();
}
Unlisted

--

--