Autonomous Robot Part 3 – Destroying Search

 

Introduction

Sometimes, things just don’t work out. This project was a second iteration of our earlier project, an autonomous robot named Search. It never came to full fruition. For this second iteration the robot’s behavior was modeled to explore rather than search to provide more interesting behavior.

Our project, the ranker, combines a degree of autonomy, intelligence, and obedience to highlight these aspects. We choose to send robots to solve our problems, but we often forget what a different approach they take. It is human nature to respond to stimuli with intuition; for example, to solve the challenge the ranker solves, a person would eye up the different heights and fathom guesses as to which peak is the highest. On the other hand, robots approach challenges with systematic calculations. Although our ranker has a systematic way of mapping its environment and completing tasks, it simultaneously has a personality.

 

Implementation

 

Mechanical structure

The structure is modeled after a music box, with a switch to turn the mechanism on, and a performance of sort located on the top of the box. This is performance, our ranker interacting with its environment, is created by the controlled movement of a servo and a stepper motor. The stepper motor sits within a box and rotates a shaft on its axis. Mounted to this shaft is a servo that raises and lowers the ranker’s arm, which is wrapped nearly completely in wire. This wire serves as our ranker’s sole sensory input from the world as it experiences it. In other words, our ranker knows when it comes in contact with what it observes as its environment when the wire, which is hooked up into a digital input pin (pin 2) and a pullup resistor, comes in contact with a wire loop connected with ground. The wire loop is mounted on four poles, and is itself height-adjustable.

 

Software

The structure runs on an FSM made up of 6 states: wait, ascend, descend, calculate, load, and travel. The mechanism starts off in the wait stage, and awaits for the human to rearrange its surroundings and click the “go” button, which sends the ranker off to explore its environments. This exploration is controlled by the two state,s ascend and descend, and lasts for 200 steps by the stepper motor. In order to map out its environment, the mechanism makes a full circle, while periodically making brief contact with the surrounding wires. First, the servo brings the arm up a certain height, then lowers it until it senses contact with ground. It then records the height and number of steps, and breaks contact. Once the ranker has finished exploring its environment, it calculates using the collected data, which are both stored in two arrays of length 200 each. The height array contains all -1s, while the direction array contains numbers 0 to 199, to represent each step. When contact is made, the new height at that point replaces the -1 in its corresponding place in the array. Then the non(-1) elements in the height array, along with their corresponding step values, are copied over to a new array. These arrays are then treated as a linked array, and are sorted based on descending height. The values are then pushed onto a stack, and ready to be loaded. Thus, we enter the load state, where we pop off the first value, the lowest height, and move the servo and motor to point to that spot. After the ranker reaches its first point, it pops off another value and moves to that point, and continues this behavior until the stack is empty, and it has arrived at the highest peak. Throughout this process, when our ranker senses contact with the wire, it will raise its arm slightly as not to alter the form of its surroundings. After it has completed its task, it will hold and wait until a person rearranges its environment, and sends it off to explore again.

Ultimately, our robot was not able to come to life, partially due to inexperience with the process of developing software, and marrying software to hardware. Although we had a gist of what we wanted in terms of performance, and were able to describe what we were looking for with Arduino code, we did not get enough time in the end to slowly debug the code bit by bit. A better approach would have been to test each task the robot has to know separately, then put these tasks together to make up the robot’s overall behavior. Furthermore, we had trouble testing the hardware with the software due to unexpected behavior in the software leading the hardware to behave erratically. A safer approach would be to serial print outputs to predict behavior instead of directly hooking up the software with the hardware.

Code

#include <Servo.h>

#include <StackArray.h>

#define BAUD_RATE 115200

 

//set up input pins below

#define BUTTON_INPUT_PIN 4 //button

#define CONTACT_INPUT_PIN 5 //contact with ground digital pin

 

//declare servo

Servo servo;

 

//declare motor?

#define DIR_PIN 2     // The direction pin controls the direction of stepper motor rotation.

#define STEP_PIN 3    // Each pulse on the STEP pin moves the stepper motor one angular

 

enum state_name_t {WAIT = 0, ASCEND = 1, DESCEND = 2, CALC = 3, LOAD = 4, TRAVEL = 5} state_index;

 

 

/****************************************************************/

// Global variables.

int steps = 0; // position of motor in a particular rotation (0  reference is starting position)

//servo

int theta = 90; //initial servo position

int max_height = 10; //how high to go relative from the contact height

//misc declarations

int start = 0; //start triggered with start button press

int contact; //contact with ground

int done = false; //done performing

//array declarations

int R[200];

int T[200];

//global stacks

StackArray <int> X;

StackArray <int> Y;

//target declarations

int target_x;

int target_y;

 

/****************************************************************/

void setup()

{

// initialize the Serial port

Serial.begin( BAUD_RATE );

// servo setup

servo.attach(9);

// initialize motor.. so digital output pins output drive mode

pinMode(DIR_PIN, OUTPUT);

pinMode(STEP_PIN, OUTPUT);

// initialize the state machine

state_index = WAIT;

for (int i = 0; i < 200; i++) {

T[i] = -1;

}

for (int i = 0; i < 200; i++) {

R[i] = i;

}

}

//helper functions

void sort(int a[], int size)

{

for(int i=0; i<(size-1); i++)

{

for(int o=0; o<(size-(i+1)); o++)

{

if(a[o] > a[o+1])

{

int t = a[o];

a[o] = a[o+1];

a[o+1] = t;

}

}

}

}

 

 

void measuredheights()

{

int count =0;

for (int i =0; i<200; i++)

{

if (T[i] != -1) count+=1;

}

int new_R[count];

int new_T[count];

 

int index = 0;

for (int j = 0; j < 200; j++)

{

if(T[j] != -1)

new_R[index] = R[j];

new_T[index] = T[j];

index++;

}

sort(new_R, count);

sort(new_T, count);

 

for (int n=0; n < count; n++)

{

X.push(R[n]); // x and y position stacks ready to go

Y.push(T[n]);

}

}

 

//moves motor up or down a step given direction

void rotate(int dist)

{

int dir = (dist > 0) ? HIGH:LOW;

digitalWrite(DIR_PIN,dir);

 

digitalWrite(STEP_PIN, HIGH);

delayMicroseconds(350);

 

digitalWrite(STEP_PIN, LOW);

delayMicroseconds(350);

}

 

int debounce_button() {

int reading1 = digitalRead(BUTTON_INPUT_PIN);

delay(10);

int reading2 = digitalRead(BUTTON_INPUT_PIN);

return(reading1 & reading2); //return HIGH only when both are at HIGH

}

int debounce_contact() {

int reading1 = digitalRead(CONTACT_INPUT_PIN);

delay(10);

int reading2 = digitalRead(CONTACT_INPUT_PIN);

return(reading1 & reading2); //return HIGH only when both are at HIGH

}

 

void loop()

{

if (X.isEmpty() && Y.isEmpty())

done = true;

switch(state_index) {

case WAIT: {

done = false;

//Serial.println(“entering WAIT”);

break;

}

case ASCEND: {

theta++; //raise the servo

steps++;

rotate(1);

//Serial.println(“entering ASCEND”);

break;

}

case DESCEND: {

theta–; //lower the servo

steps++;

rotate(1);

//Serial.println(“entering DESCEND”);

break;

}

 

case CALC: {

steps = 0; //reset steps

measuredheights(); //call the highest level array sort function

//Serial.println(“entering LOAD”);

break;

}

 

case TRAVEL: {

 

if ((debounce_contact() == HIGH) && (done == false)){ //OR LOW???!??!?!HJ:HFKJL;JSAKL;FJ

theta = theta + 3; //if we hit a wire, jump up

}

 

//move servo up or down

else if(theta < target_y) theta++;

else if (theta > target_y) theta–;

 

//move motor counter/clockwise

if(steps < target_x) rotate(1);

else if (steps < target_x) rotate(-1);

 

//Serial.println(“entering TRAVEL”);

break;

}

 

case LOAD: {

target_x = X.pop();

target_y = Y.pop();

}

 

}

 

switch( state_index ) {

 

case WAIT: {

if (debounce_button() == HIGH) state_index = ASCEND; //OR LOW???

else state_index = WAIT;

break;

}

 

case ASCEND: {

if(steps == 200) state_index = CALC;

else if (theta >= max_height) state_index = DESCEND;

else state_index = ASCEND;

break;

}

 

case DESCEND: {

if(steps == 200) state_index = CALC;

else if (debounce_contact() == HIGH) { //OR LOW???!??!?!HJ:HFKJL;JSAKL;FJ

T[steps] = theta; //update the theta array

state_index = ASCEND;

}

else state_index = DESCEND;

break;

}

 

case CALC: {

state_index = LOAD;

break;

}

 

case LOAD: {

if(done == true) state_index = WAIT;

else if((done == false) && (steps != target_x) && (theta != target_y)) state_index = TRAVEL;

else state_index = LOAD;

break;

}

 

case TRAVEL: {

if((done == false) && (steps == target_x) && (theta == target_y)) state_index = WAIT;

else state_index = TRAVEL;

break;

}

 

}

}