Creeping Angel

The Creeping Angel is an interactive animatronic robot doll that plays peek-a-boo with users. When the angel sees someone looking at it, it will hide its face. However, as soon as you look away, the angel unhides and snaps a photo to “get you”.

angelhiding  IMG_5622 2

The project was commissioned for (Re)GenerationWho, a weekend-long convention for fans of the TV show Doctor Who. The concept is based on particular baddies from the series known as a “weeping angel”, who exist in between dimensions and appear to us as stone angels (the kind that grace tombstones and cemeteries). They cannot move while being observed, and attack when one is not looking to steal a person’s future timeline. I thought this was a great starting point for building an interactive piece: a face-tracking, photo-snapping, tweeting, moving doll. The interaction amused quite a lot of people at the event and it was a hit! The angel snapped up more than a few hundred timelines that day >:}

 

UPDATE: For Interventioncon6, the Creeping Angel reappeared to steal even more souls!

In case you would like to view the presentation I gave at Intervention6 2015 download this PDF: Intervention-CreepingPresentation2015

——————– Building For this project, I used Arduino and intended to drive it with RaspberryPi, but my model shorted out. In the end, I decided to use Processing and a small ebook laptop as the brain instead. Here is the Processing code   I don’t have clear diagrams for the assembly of the robot’s body (as it was a lot of trial and error attaching motors to the doll parts), but here are some photos of the basic joints, couplers, and attachments:      

Arduino UNO model

Motors: Standard Size High Torque Servo Motors

 circuit diagram

(code posted at the end of this)  

 

——————-Results

People seemed to have a good time playing with the angel, despite its evil intentions. There were 600 tweets triggered by passers-by and curious observers. Here are some highlights:

 

 

 

——————– Code

For the Arduino, I simply used the Servo Firmata to open it to commands from Processing


/*
"Creeping angel" by Bree Geek, breegeek@gmail.com
Code borrowed from, picked apart, and broken from:
This tutorial helped immensely: https://www.sparkfun.com/tutorials/304

This code requires Arduino: Servo Firmata and OpenCV
*/

/*___________LIBRARIES________________*/
import processing.serial.*;
import cc.arduino.*;

//import opencv and video stuff
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

//import twitter library
import twitter4j.conf.*;
import twitter4j.*;
import twitter4j.auth.*;
import twitter4j.api.*;

//import java utils
import java.util.*;

/*___________VARIABLES________________*/

//set up a twitter thingamado
Twitter twitter;

//set up Arduino
Arduino arduino;

//set up some variables to handle the face detection and taking of pics
boolean isFace = false;
boolean wasFace = false; //boolean to see if there was a face that has gone
int numPics;
int isFaceTimer;
int goneFaceTimer;

//initialize video capture
Capture video;
OpenCV opencv;
OpenCV photocv;

//set up a variable to hold a random value and change it each loop
int rand;

// Creeping angel needs a bunch of taunts to go with photo tweets
// Here is an array of strings that will be our punchy, hilarious taunts
String[] taunt = {
"You have never been here.... .... -CreepingAngel at #regenwho",
"Doo-doo-doo Another one bites the dust! -CreepingAngel at #regenwho",
"Your soul will look exquisite in my collection! -CreepingAngel at #regenwho",
"Back to some forgotten time with ye! -CreepingAngel at #regenwho",
"You Blinked! MUWAHAHAHAHA -CreepingAngel at #regenwho",
"Nice lifetime. Would be a shame if you lost it >:) -CreepingAngel at #regenwho",
"*OMNOMNOM* Tasty human! -CreepingAngel at #regenwho",
"I liked your shirt -CreepingAngel at #regenwho",
"You can run but you can't hide---actually, you can't run, either -CreepingAngel at #regenwho",
};

/*___________SETUP________________*/
void setup() {
size(640, 480);
numPics = 0;

//set up video objects
video = new Capture(this, 640/2, 480/2);
opencv = new OpenCV(this, 640/2, 480/2);
opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE);

video.start();

/*____________configure twitter keys */
//substitute *****s for the actual values from Twitter app
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setOAuthConsumerKey("**********");
cb.setOAuthConsumerSecret("**********");
cb.setOAuthAccessToken("**********");
cb.setOAuthAccessTokenSecret("*********");

//create a twitterfactory object
TwitterFactory tf = new TwitterFactory(cb.build());

//init twitter object
twitter = tf.getInstance();

/*___________configure Arduino */
arduino = new Arduino(this, Arduino.list()[1], 57600); //your offset may vary

arduino.pinMode(9, Arduino.OUTPUT);
arduino.pinMode(7, Arduino.SERVO);
}

void draw() {
//first, choose the random taunt phrase for tweeting
rand = int(random(taunt.length));

// set up the video
scale(2);
opencv.loadImage(video);
image(video, 0, 0 );

//diagnostic squares around faces (keeping it to the first face gives processing some breathing room
noFill();
stroke(0, 255, 0);
strokeWeight(3);
Rectangle[] faces = opencv.detect();
println(faces.length);

//check for faces
for (int i = 0; i < faces.length; i++) { //println(faces[i].x + "," + faces[i].y); rect(faces[0].x, faces[0].y, faces[0].width, faces[0].height); isFace = true; } if (faces.length == 0) { isFace = false; } if (isFace) { wasFace = false; //reset the "face is gone" timer goneFaceTimer=0; //start the "there's a face" timer isFaceTimer ++; //run the function hideFace(); } else { showFace(); goneFaceTimer ++; //check that the face was there long enough. Then see if it's gone if ( isFaceTimer > 1 && goneFaceTimer > 0 ) {
wasFace = true;
}
//also take this opportunity to reset the face timer
isFaceTimer = 0;
}

if (wasFace) {
takePic();
}
//once we tell processing to take a pic/tweet, we should reset the wasFace boolean
wasFace = false;

//change the random phrase to a new 'un
rand = int(random(taunt.length));
}

//this function is necessary for the video capture
void captureEvent(Capture c) {
c.read();
}

void takePic() {
//take a picture and save it, and it will need to be retrieved for tweeting
//this is a bit of a clunky way to do this, but it's what I got to work.

//save the frame coming in from the video feed using the numPics value
saveFrame("pic"+numPics+".jpg");
println ("pics taken"+(numPics+1));
//build the file name and path from your own file pathpath
String photoPath = "C:/Users/USER/Documents/Processing/creepingAngel/";
String photoName = "pic"+numPics+".jpg";
String photoFile = photoPath+photoName;

//let's save this file name in a variable
File file = new File(photoFile);

//The random numbers at the end of randomTweet are to create a new unique tweet everytime. Twitter doesn't like repeat tweets back to back.
String randomTweet = taunt[rand];
//uncomment testPassingFile(file) to test if your file is good and accessible
//testPassingFile(file);
tweetPic(file, randomTweet);
numPics += 1;
}

void tweetPic(File _file, String theTweet)
{
try
{
//the tweet staus comes from our random taunt
StatusUpdate status = new StatusUpdate(theTweet);
//the picture for the tweet comes from that file we saved earlier
status.setMedia(_file);
twitter.updateStatus(status);
}
catch (TwitterException te)
{
println("Error: "+ te.getMessage());
}
}

void hideFace() {
//send a note to close the arms
arduino.analogWrite(9, 0);
arduino.analogWrite(7, 120);
delay(1);
}

void showFace() {
//send a note to open the arms
arduino.analogWrite(9, 120);
arduino.analogWrite(7, 0);
delay(1);
}


©Bree Chapin