detecting when Spark is stuck

Hi, folks. I unpacked my new Sparki over the weekend to entertain a loved one who’s been laid up following surgery, and we spent a unch of time experimenting with writing our own wall avoidance and edge finding code from scratch.

Getting the robot up and running was a little bumpy at first – in particular, it’s not always clear when I need to hit reset to get my new code to boot, and sometimes I have to upload the code multiple times before it’ll take. But, minor quirks aside, our experience has been pretty fun overall.

At this point, we’ve got the little robot tooling around the coffee table, steering around soda cans and its own carton, backing up when it gets into corners, as well as stopping before it tips off the edge of the table. So far so good.

However, eventually Sparki runs into my Macbook Air, which, sitting closed on the table, is a couple centimeters high, so it’s low enough that the sonic rangefinder doesn’t see it, but high enough that Sparki won’t drive over it. So the robot sits there, spinning its wheels, thinking it’s moving forward, when in actuality it’s totally stuck.

How can we use the sensors onboard to detect this condition? I thought about using the accelerometer, but it doesn’t seem to provide meaningful values in the Y axis from one moment to the next – which makes sense, because acceleration in the Y axis ought to be nearly zero when moving at a constant velocity or not moving at all.

It seems like a 3-axis gyroscope might be a solution to this problem, but then we’d have to buy an extra board, solder on connectors, use up one or several of the precious analog input pins on the expansion port, and figure out how to mount the gyro board on Sparki’s chassis. Seems like a lot of work. Was there any reason why a gyro wasn’t part of the included kit?

The other idea I had was to use the sonic rangefinder to ping periodically at 0º, apply a smoothing function to the returned range, and , if the robot thinks it’s moving but the range to a dead-ahead obstacle hasn’t changed, then conclude that it’s stuck and act accordingly.

Anyone have any thoughts on this? It seems like the final piece to having a Sparki that will wander contentedly around a office or home, poking into corners and scaring the cat, but otherwise keeping out of trouble.

The magnetometer can tell you when the values are not changing. If Sparki’s x and y values are not changing by some value that would tell your robot it is stuck. Look at the magnetometer.

@Schuyler: Great question, so I looked into it. The code below is basically the remote code, but it also measures the accelerometer on x, y & z. It displays current, min & max values for all 3 axis on the lcd.

I played around with it, and did as many variations of movement transitions as possible while in open space. (i.e.-> from forward to backwards, forwards to left, etc…), so that I could get Sparki to record the max & min on all 3 axis while under “normal” usage.

I then replicated you running into your iPad, but I saw no reliable further decrease or increase on any axis to be able to use it as a trigger to indicate that it’s stuck.

Rwardjr5’s idea of using the magnetometer would probably work better.

Anyway, here’s the code. Could be modified to use magnetometer instead I guess.

[code]/*******************************************
IR Remote

Sparki has a sensor that lets it receives
commands from the included remote control.
Try moving it around!
********************************************/
#include <Sparki.h> // include the sparki library
float x_min = 1023;
float x_max = 0;
float y_min = 1023;
float y_max = 0;
float z_min = 1023;
float z_max = 0;

void setup()
{
}

// /------^-----
// | |
// | 69 70 71 |
// | 68 64 67 |
// | 7 21 9 |
// | 22 25 13 |
// | 12 24 94 |
// | 8 28 90 |
// | 66 82 74 |
// ____________/

void loop()
{
int code = sparki.readIR();

//if(code != -1){
//sparki.print("Received code: ");
//sparki.println(code);
sparki.clearLCD();
float x = sparki.accelX(); // measure the accelerometer x-axis
float y = sparki.accelY(); // measure the accelerometer y-axis
float z = sparki.accelZ() + 9.81; // measure the accelerometer z-axis. The +9.81 is to offset reading gravity.

//if (x < x_min) {
// x_min = x;
//}
if (x < x_min) x_min = x;
if (x > x_max) x_max = x;
if (y < y_min) y_min = y;
if (y > y_max) y_max = y;
if (z < z_min) z_min = z;
if (z > z_max) z_max = z;

// write the measurements to the screen
sparki.print(“Accel X: “);
sparki.println(x);
sparki.print(“MinX:”);
sparki.print(x_min, 1);
sparki.print(” MaxX:”);
sparki.println(x_max);
sparki.println("");

sparki.print(“Accel y: “);
sparki.println(y);
sparki.print(“MinY:”);
sparki.print(y_min, 1);
sparki.print(” MaxY:”);
sparki.println(y_max);
sparki.println("");

sparki.print(“Accel z: “);
sparki.println(z);
sparki.print(“MinZ:”);
sparki.print(z_min, 1);
sparki.print(” MaxZ:”);
sparki.print(z_max);

sparki.updateLCD(); // display all of the information written to the screen
delay(100);
//}

switch(code){

// Movement buttons
case 22: sparki.clearLCD();
delay(1000); break;

case 70: sparki.moveForward(); break;
case 21: sparki.moveBackward(); break;
case 71:
case 67: sparki.moveRight(); break;
case 69:
case 68: sparki.moveLeft(); break;
case 64: sparki.moveStop();
sparki.gripperStop();
break;

// Gripper Buttons
case 9: sparki.gripperOpen(); break;
case 7: sparki.gripperClose(); break;

// buzzer
case 13: sparki.beep(); break;

// Servo Buttons
case 12: sparki.servo(SERVO_LEFT); break;
case 24: sparki.servo(SERVO_CENTER); break;
case 94: sparki.servo(SERVO_RIGHT); break;

// RGB LED
case 90: sparki.RGB(RGB_OFF); break;
case 66: sparki.RGB(RGB_RED); break;
case 82: sparki.RGB(RGB_GREEN); break;
case 74: sparki.RGB(RGB_BLUE); break;

default:
break;

}

}
[/code]