The Kraken is the first robot built by TechLabs as part of its instructor training boot camp. It is named after a fearsome mythical sea creature. The instructors have experimented a great deal with both its physical build, and its software (sketch) to reach its current, battle-ready state. The Kraken sketch started with the « Sumo-Base » sketch as a template, designed by Eric Ryan, author of « An Introduction to Robot Programming ». We continue to try out new ideas for interesting Sumo Robot League behaviors on the Kraken. Below is its state-of-the-art sketch as of July, 2017 which features some interesting « battle cry » behavior, battle-ring edge avoidance, and multiple attack modes- feel free to copy:
/********************************************************************
TechLabs – « The Kraken » sketch – July 2017 – Instructor Robot
credit: Miles Prinzen @ TechLabs and Eric Ryan Harrison for portions of code in « Sumo-Base » (https://github.com/SumoRobotLeague/MRK-1/blob/master/Chapter_8/CompetitiveSumoRobot.ino)
********************************************************************/
#include « Motor.h »
#include « Pitches.h »
/************************/
/** PIN CONFIGURATIONS **/
/************************/
// LED pin
#define led 7
// IR Sensor pins
#define leftSensor A1
#define rightSensor A2
#define rearSensor A3
#define IREmitter 6
// Hardware pins
#define button 2
#define buzzer 5
// Ultrasonic sensor pins
#define echoPin A0
#define pingPin 3
// Motor pins
#define rightMotorSpeed 10
#define rightMotorDirection 8
#define leftMotorSpeed 9
#define leftMotorDirection 4
/*************************************************/
/** Global Variables and Configuration Settings **/
/*************************************************/
// Competition configuration settings
#define abortsThreshold 980
#define searchTime 2000
#define attacksDistance 18
// competition configuration variables
int turnDirection = 1; // 1 = left, 2 = right
unsigned long turnTime = 0; // holds the return from millis()
unsigned long forwardTime = 0;
int dAttack = 0; // holds attack duration in ticks
// Create our motor object
Motor motor;
// State variables
int buttonState = 1; // 1 = up, 0 = pressed
int state = 1; // 1 = idle, 2 = competing
void setup() {
Serial.begin(9600);
// pin setup
pinMode(button, INPUT_PULLUP);
pinMode(buzzer, OUTPUT);
// IR sensors
pinMode(leftSensor, INPUT);
pinMode(rightSensor, INPUT);
pinMode(rearSensor, INPUT);
pinMode(IREmitter, OUTPUT);
// Turn on our IR Emitter
digitalWrite(IREmitter, HIGH);
// ultrasonic sensors
pinMode(pingPin, OUTPUT);
pinMode(echoPin, INPUT);
// Motor setup
motor.setupRight(rightMotorSpeed, rightMotorDirection);
motor.setupLeft(leftMotorSpeed, leftMotorDirection);
Serial.println(« The Kraken is powered up. Waiting for button press. »);
}
void loop() {
if ( buttonState == 1 && digitalRead(button) == 0 ) {
buttonState = 0;
}
if ( buttonState == 0 && digitalRead(button) == 1 ) {
buttonState = 1;
if ( state == 1 ) {
// we were in idle mode, so let’s begin our
// competition countdown
Serial.println(« Competition countdown starting… »);
Serial.println(« Good luck! »);
begin_countdown();
} else {
state = 1;
motor.left(0);
motor.right(0);
Serial.println(« Kraken entering idle mode. »);
}
}
// if the countdown has ended, we’re now in competition mode
if ( state == 2 ) {
compete();
}
}
/***************************************************/
/** Competition State Functions **/
/** These functions control the operational state **/
/** of our robot while it’s in state 2. **/
/***************************************************/
// This function is the primary competition control
// function and controls the operational flow and state
// of the robot in competition mode (state 2).
void compete() {
// declare local function variables to read our sensor values and determine what we
// need our robot to do.
int distance = msToCm( ping() );
int leftIR = analogRead(leftSensor);
int rightIR = analogRead(rightSensor);
int rearIR = analogRead(rearSensor);
int rotDirection = random(1, 3); // curve direction
if ( rearIR < abortsThreshold ) {
if (rotDirection == 1) { curveFLeft(); delay(800); } else { curveFRight(); delay(800); }
} else if ( (leftIR < abortsThreshold || rightIR < abortsThreshold) && distance > 5 ) {
aborts();
} else if ( distance < attacksDistance ) {
// We have detected an enemy. attacks!
if ( dAttack < random(50, 300) ) attacks(); else {
playNote(NOTE_G1, 100, 0);
if (rotDirection == 1) reverseL(); else reverseR();
}
} else {
// We don’t see an enemy, search until we find one.
search();
}
Serial.print(« Left/ »); Serial.print(leftIR);
Serial.print( » – Rear/ »); Serial.print(rearIR);
Serial.print( » – Right/ »); Serial.println(rightIR);
}
void reverseR() {
motor.left(-255);
motor.right(-255);
delay(300);
curveBRight(); delay(800); attacks(); delay(500);
}
void reverseL() {
motor.left(-255);
motor.right(-255);
delay(300);
curveBLeft(); delay(800); attacks(); delay(500);
}
void curveFLeft() {
Serial.println(« Rot.FL »);
motor.left(random(-127, 127));
motor.right(random(128, 255));
}
void curveFRight() {
Serial.println(« Rot.FR »);
motor.left(random(128, 255));
motor.right(random(-127, 127));
}
void curveBLeft() {
Serial.println(« Rot.BL »);
dAttack -= random(10, 50);
motor.left(120);
motor.right(-255);
}
void curveBRight() {
Serial.println(« Rot.BR »);
dAttack -= random(10, 50);
motor.left(-255);
motor.right(120);
}
// This function is a simple search algorithm that
// attempts to locate the opponent before moving
// into attacks mode.
void search() {
Serial.println(« Searching… »);
if ( dAttack > 1 ) dAttack -= 3;
digitalWrite(led, LOW);
// our time has exceeded our configured searchTime, reset.
if ( turnTime != 0 && ((millis() – turnTime) > searchTime) ) {
turnTime = 0;
}
if ( forwardTime != 0 && ((millis() – forwardTime) > 5000) ) {
forwardTime = 0;
}
// get random direction and store the start time
if ( turnTime == 0 ) {
turnDirection = random(1, 3);
turnTime = millis();
}
if ( forwardTime == 0 ) {
forwardTime = millis();
}
if ( millis() – forwardTime < 3000) {
// start our turn, 1 = left, 2 = right
if ( turnDirection == 1 ) {
motor.left(255);
motor.right(-255);
} else {
motor.left(-255);
motor.right(255);
}
} else {
motor.left(200);
motor.right(200);
}
}
// The attacks() function is used to control the robot
// when an opponent has been detected closer than the
// defined attacksDistance. It will move ahead at full
// speed in an attempt to push the opponent outside of
// the ring.
void attacks() {
dAttack++;
Serial.println(« Attack! »);
digitalWrite(led, HIGH);
playNote(NOTE_B2, 100, 0);
motor.left(255);
motor.right(255);
}
// The aborts() function is an interrupting function that
// is called whenever the infrared sensors detect the
// ring border. This function will immediately halt
// other control function operations and move in reverse
// at full speed.
void aborts() {
Serial.println(« Run away! »);
motor.left(-255);
motor.right(-255);
delay(800);
}
// This function will begin our competition countdown sequence.
// When the button is depressed when in idle mode, this function
// will be called and begin a 5 second countdown with audible
// beeps at every second.
//
// PRACTICAL EXERCISE SOLUTION:
// This function also turns on the LED each time a note is
// played on the buzzer.
void begin_countdown() {
for ( int i = 0; i < 8; i++ ) {
delay(200); // wait 1 full second
// blinky angrily at our opponent
digitalWrite(led, HIGH);
playNote(NOTE_B2, 100, 0);
digitalWrite(led, LOW);
}
// wait one more full second before playing our next note
delay(1000);
digitalWrite(led, HIGH);
playNote(NOTE_D3, 80, 5);
digitalWrite(led, LOW);
playNote(NOTE_F3, 80, 5);
digitalWrite(led, HIGH);
playNote(NOTE_GS3, 80, 5);
digitalWrite(led, LOW);
playNote(NOTE_D4, 800, 5);
digitalWrite(led, LOW);
playNote(NOTE_F4, 800, 5);
digitalWrite(led, HIGH);
playNote(NOTE_GS4, 800, 5);
digitalWrite(led, LOW);
playNote(NOTE_D5, 80, 5);
digitalWrite(led, LOW);
playNote(NOTE_F5, 80, 5);
digitalWrite(led, HIGH);
playNote(NOTE_GS5, 80, 5);
playNote(NOTE_D6, 800, 5);
playNote(NOTE_F6, 800, 5);
playNote(NOTE_GS6, 800, 5);
// Our countdown is nearly complete. Wait 1
// more second and then set state to 2 so that
// we can begin our competition!
delay(2000);
digitalWrite(led, LOW);
state = 2;
}
/**********************/
/** Helper Functions **/
/**********************/
// Note-playing buzzer helper function.
void playNote(int note, int duration, int rest) {
tone(buzzer, note, duration);
delay(rest);
}
// Helper function to manage our ultrasonic sensor.
long ping() {
long duration;
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(10);
digitalWrite(pingPin, LOW);
duration = pulseIn(echoPin, HIGH);
return duration;
}
// Helper function to return the distance to an object
// detected by the ultrasonic sensor in centimeters.
long msToCm(long microseconds) {
return microseconds / 29 / 2;
}
// Simple blinky function called in loop() whenever a state
// change is made by a user button press.
void blinky(int blinkys) {
for ( int i = 0; i <= blinkys; i++ ) {
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
delay(500);
}
}