How Escaping Corners Works

The example script escaping_corners.py is an evolution of the original roaming_with_whiskers.  If you didn't read through How Roaming with Whiskers Works, you might want to go back and do so now.

This script begins with the four familiar navigation function definitions for forward, left, right, and backwards.

def forward():
    bot(18).servo_speed(75)
    bot(19).servo_speed(-75)

def backwards():
    bot(18).servo_speed(-75)
    bot(19).servo_speed(75)
    sleep(750)
    
def right():
    bot(18).servo_speed(75)
    bot(19).servo_speed(75)
    sleep(500)
    
def left():
    bot(18).servo_speed(-75)
    bot(19).servo_speed(-75)
    sleep(500)

 

Next comes an initialization section.  There are five variable declarations. counter will be used to keep track of alternate whisker presses.  w_l and w_r are shortened versions of the roaming code's whisker_left and whisker_right; remember that long variable names take more memory!   With "o" for "old," the variables o_w_l and o_w_r will store the previous whisker states in order to compare them to the current whisker states each time through the main loop.

counter = 0
w_l = 0
w_r = 0
o_w_l = 0
o_w_r = 1

 

Notice that o_w_r is given an initial value of 1, not zero.  This is necessary because the code always compares an alternating pattern; if o_w_l and o_w_r were both initialized to zero, the code would never find the second nested if statement to be true and would not count alternate corner presses.

The main routine starts with a short tone to signal the start of the program. Then, it enters and infinite while_True: loop.  Just as with the roaming script, this loop begins by testing the whisker circuits' input pin states and saving the resulting binary values to variables.

bot(22).tone(4000,50)

# main routine

while True:                                   
    w_l = bot(7).read_digital()               # check left whisker state
    w_r = bot(9).read_digital()               # check right whisker state

 

Now the fun begins! The outermost if statement uses the not-equals comparison operator != to see if w_l and w_r are different, which indicates one of the whiskers is pressed—it does not matter which one!  If this is true, the nested if statement right below it uses the not-equals operator again  o_w_l != w_l  and to see if each whisker's current input state is different from its old input state.

    if (w_l != w_r):                          # if a whisker was pressed
        if (o_w_l != w_l) and (o_w_r != w_r): # different from last time?

 

If it's true that both current whisker input states are different from their previous input states, the cyber:bot must have gone from having one whisker pressed to having the opposite whisker pressed—in other words, it encountered a corner. This event is tracked by incrementing the variable counter by one. Then, the value of w_l is stored in o_w_l and w_r is stored in  o_w_r  to make a another comparison next time through the loop.

            counter = counter + 1             # in corner - add to counter
            o_w_l = w_l                       # record left whisker press
            o_w_r = w_r                       # record right whisker press

 

Next comes the innermost of the three nested if statements.  It checks to see if counter has exceeded four, pretty strong evidence that the cyber:bot is indeed stuck in a corner.  If true, then a tone plays signalling an escape is imminent. Before that happens, counter, w_l, and w_r are all set to 1, so the cyber:bot will resume roaming after executing the backwards and two left function calls to escape.

            if (counter > 4):                 # if in corner too long...
                bot(22).tone(3000, 50)        # sound alarm
                counter = 1                   # reset corner counter
                w_l = 1                       # reset left whisker value
                w_r = 1                       # reset right whisker value
                backwards()                   # back out of corner
                left()                        # turn away from corner
                left()

 

These nested if statements end with an else: that sets counter back to 1 before moving on to the regular roaming with whiskers behavior.

        else:
            counter = 1                       # else, reset counter

 

Finally, after exiting the outermost nested if block, the code moves on to a more familiar if...elif...elif...else similar to the one in roaming_with_whiskers.py.  Note that only the first if...checks both whisker states, and the others use the minimal amount of logic needed to accomplish the navigation tasks.

    if (w_l == 0 and w_r == 0):               # roam with whiskers
        backwards()                                 
        right()
    elif w_l == 0:   
        backwards()                                  
        left()
    elif w_r == 0:  
        backwards()                                   
        right()
    else:                                           
        forward()