Roaming with Ultrasound

Now that you can measure centimeter distances with the PING))) sensor, and know which kinds of objects it can sense, let’s put it to work in navigation. 

All your code has to do is make the robot go forward until it receives a distance measurement from the PING))) sensor that’s less than some pre-defined value, say 20 cm.  Then, slow down to a stop.  After that, turn in place until the measured distance is more than 20 cm.  At that point, it’ll be safe to go forward again.  To make the ActivityBot's navigation a little more interesting, your code can also use random numbers to decide which direction to turn. 

Test the Sense, Stop, and Turn Maneuver

Before actually roaming, it’s important to test a smaller program to make sure your ActivityBot will stop in front of an obstacle, and turn away from it.  This example program makes the ActivityBot go forward while the PING))) measured distance is greater than 20 cm.  If the distance measures less than or equal to 20 cm, the robot ramps down to a stop.  Then, it turns in place until it measures more than 20 cm, which means the robot turned until the object is no longer visible.

  • Click SimpleIDE’s Open Project button.
  • Open Detect and Turn from Obstacle from ...Documents\SimpleIDE\Learn\Examples\Robots\ActivityBot. 
  • Click the Load EEPROM & Run button.
  • Disconnect your robot from its programming cable.
  • Set the power switch to 2.
  • Press and release the reset button.
  • Send it toward a wall, block, water bottle, or other obstacle.
  • Verify that it goes full speed until it gets closer than 20 cm, then ramps down and stops, and turns.
  • Try it several times so that you can see that the direction it picks to turn is random.


How it Works

This program doesn’t use distance = ping_cm(8).  Instead, it just takes the value ping_cm(8) returns, and uses it in decisions.  So, it doesn’t need to declare a distance variable; however, it does still need a variable for storing a random number to decide which way to turn. So before the main function, there's a declaration for a variable named turn

The program starts with drive_setRampStep(10), which sets the number of ticks per second that the speed is allowed to change for every 50th of a second.  Since the function call passes 10, it means the speed can only change by 10 ticks per second, every 50th of a second.  Next, drive_ramp(128, 128) ramps up to full speed, in steps of 10 ticks per second.  With a ramp step of 10, it takes 13 50ths of a second = 0.26 seconds to ramp up to full speed.  All the steps are by 10, except for the last one, which is from 120 to 128. 

  Detect and Turn from Obstacle.c

  Detect obstacles in the ActivityBot's path, and turn a random direction to avoid them.


#include "simpletools.h"                      // Include simpletools header
#include "abdrive.h"                          // Include abdrive header
#include "ping.h"                             // Include ping header

int turn;                                     // Navigation variable

int main()                                    // main function
  drive_setRampStep(10);                      // 10 ticks/sec / 20 ms

  drive_ramp(128, 128);                       // Forward 2 RPS

  // While disatance greater than or equal
  // to 20 cm, wait 5 ms & recheck.
  while(ping_cm(8) >= 20) pause(5);           // Wait until object in range

  drive_ramp(0, 0);                           // Then stop

  // Turn in a random direction
  turn = rand() % 2;                          // Random val, odd = 1, even = 0

  if(turn == 1)                               // If turn is odd
    drive_speed(64, -64);                     // rotate right
  else                                        // else (if turn is even)
    drive_speed(-64, 64);                     // rotate left

  // Keep turning while object is in view
  while(ping_cm(8) < 20);                     // Turn till object leaves view

  drive_ramp(0, 0);                           // Stop & let program end

Next, the program enters a one-line loop: while(ping_cm() >= 20) pause(5).  This translates to "check to see if the ping_cm function returned a value equal to or greater than 20 (no object is detected within 20 cm), and if this is true, pause for 5 ms and then check again."  As long as ping_cm returns greater than 20, the while loop keeps looping, the ActivityBot continues to drive straight forward.  

If a ping_cm function call returns a value of 20 or less (an object is detected within 20 cm), then the while condition is no longer true and the loop stops repeating.  This allows the code to move on to drive_ramp(0, 0).  This slows the ActivityBot down to a stop, which also takes about 0.26 seconds. 

After stopping, the ActivityBot has to decide which direction to turn to avoid the object.  This example uses the math library’s rand function (included by simpletools) to randomly choose between left and right.  The rand function returns a pseudo random value somewhere in the 0 to 2,147,483,648 range.  Each time the code calls rand, it returns a new value in its pseudo-random sequence.  The statement turn = rand() % 2 divides 2 into the random number, takes the remainder (which will be a 1 or 0), and copies it to the turn variable. 

This turn variable’s random 1 or 0 is used to decide which direction to turn.  If turn gets a random one, if(turn == 1) drive_speed(64, -64) statement makes the ActivityBot rotate right.  If instead turn gets a random zero, drive_speed(-64, 64) makes it rotate left.  After that, while(ping_cm(8) < 20) keeps repeating while the object is still in view.  As soon as the ActivityBot has turned far enough, it stops.

If all that worked, most of the code in the main function can be put in a while(1) loop to make the ActivityBot go looking for new obstacles.


Did You Know?

Library List — This application uses functions from four libraries, simpletools (pause), abdrive (anything starting with drive), ping (ping_cm) , and math (rand).  The math library is not declared here because simpletools already includes it, but you could just as easily add #include <math.h> to the list.  It would be enclosed in < > symbols instead of quotes by convention since it’s part of Propeller GCC and not part of the Propeller C Tutorial's custom libraries (like simpletools, abdrive, and ping). 


Try This – Roaming with Ping)))

This program just needs a while(1) loop to make it move on in search of the next obstacle and continue to repeat what it just did.  The only thing that doesn’t need to be part of the while loop is the last drive_ramp(0, 0).

  • Use the Save Project As button to save a copy of the example program into My Projects.  Use the name Roaming with Ping (Try This). 
  • Modify the program to match the example below.  You are just adding while(1) followed by an opening brace {.  Just above the last statement you’ll be adding a closing brace }.
  • For readability, indent all the statements between the braces you just added by two spaces.  (You can just shade the block and press the Tab key.)
  • Use the Load EEPROM & Run button to load the program into the ActivityBot. Set PWR to 0 and disconnect from the programming cable.
  • Set the PWR switch to 2, and hold down the reset button.
  • Set it in an open area with several obstacles, and observe how the ActivityBot goes up to one and then turns away in search for the next obstacle.



Your Turn

Here are three challenges for you.

  • Modify the code so that it turns alternate directions with each obstacle.
    Hints: Instead of turn = rand() % 2, use turn = (turn + 1) % 2.  This will add 1 to turn, then take the remainder of turn / 2, which will be 1, then 0, then  1, then 0….
  • Make it so that the ActivityBot stops after it finds four obstacles.
    Hints: Declare a variable for counting obstacles with turn, and initialize it to 0 before the while(1) loop starts.  Add 1 to the counting variable each time through the while(1) loop.  There’s more than one way to limit the repetitions to 4 - can you figure one out?  
  • The simplest initial approach to exploring a maze is to take a right turn at each wall.  Not necessarily the best approach, but certainly the simplest.  Modify the code so that it only turns right when it encounters an obstacle.