Lab 6: Experimenting with Sensor Noise

In this lab, you will design a protocol, and run a series of experiments to test how sensor noise affects a robot's behavior. To get a copy of this notebook, open a terminal and type:

cp /home/meeden/public/SensorNoiseExperiments.ipynb s3p

This will copy the notebook from my directory to your s3p directory. Then type:

cd s3p

source /usr/swat/bin/s3penv

jupyter notebook

Then you can open this notebook and begin work.

In [1]:
from jyro.simulator import *
from math import pi
from random import random
from time import time

The world

The world consists of a maze of twisting cooridors with a light at the end. The robot's goal is to reach the light as fast as possible.

In [2]:
def make_maze_world(physics):
    physics.addBox(0, 0, 5, 5, fill="backgroundgreen", wallcolor="gray")
    physics.addBox(1.5, 2, 1.7, 3.5, fill="blue", wallcolor="blue")
    physics.addBox(3.0, 5, 3.2, 3.5, fill="blue", wallcolor="blue")
    physics.addBox(1.5, 2, 3.0, 1.8, fill="blue", wallcolor="blue")
    physics.addBox(3.0, 2, 3.2, 0, fill="blue", wallcolor="blue")
    physics.addBox(3.0, 3.5, 5.0, 3.7, fill="blue", wallcolor="blue")
    physics.addLight(4, 1, 1.0) #paramters are x, y, brightness

The robot

The robot is equipped with a camera, sonars, and light sensors.

In [3]:
def make_robot():
    robot = Pioneer("Pioneer", 2, 0.5, 0) #paremeters are name, x, y, heading (in radians)
    robot.addDevice(Camera())
    robot.addDevice(Pioneer16Sonars())
    light_sensors = PioneerFrontLightSensors(3) #parameter defines max range in meters
    robot.addDevice(light_sensors)
    return robot
In [4]:
robot = make_robot()
vsim = VSimulator(robot, make_maze_world) 

Noisy sensors

One of the biggest problems with using simulated robots rather than real robots, is that the real world is much more complex than a simulated one. In addition, real sensors are often noisy rather than clean. In other words, they are often inconsistent, giving random data at times.

One approach to deal with this discrepancy between simulations and the real world is to explicitly add random noise to the simulator. The function noisySonars, defined below, has two parameters: the robot and the percent noise to add to the robot's sonar values. To add no noise, use 0 as the percentNoise. To add 10% noise, use 0.1 as the percentNoise. To add 20% noise, use 0.2 as the percentNoise. And so on.

In [5]:
def noisySonars(robot, percentNoise):
    sonars = robot["sonar"].getData()
    noisy_sonars = sonars[:]
    for i in range(len(sonars)):
        noise = percentNoise * sonars[i] * random()
        if random() > 0.5:
            noise *= -1
        noisy_sonars[i]+= noise
    return noisy_sonars

Robot brain to traverse a maze

The brain below is designed to traverse a maze, timing how long it takes to reach a light. In your experiments you will manipulate the amount of noise applied to the sonar sensors and measure how this affects the time it takes the robot to reach the light.

This brain has many different cases that it checks when deciding how to move the robot. These cases are always checked in order. The first case that is true will be executed, and all remaining cases will be skipped. The overarching idea is that when there is an obstacle in front, the brain chooses to turn towards the side where there is more open space. And once it starts a turn in one direction, it continues to turn in that same direction until the front is no longer blocked.

In [6]:
global leftTurn, rightTurn, startTime, endTime, begin, done

begin = True       # Is set to False as soon as the startTime is set
done = False       # Is set to True once the experiment is complete
leftTurn = False   # Is set to True when initiating a left turn, and back to False when completed
rightTurn = False  # Is set to True when initiating a right turn, and back to False when completed

def avoidToLightBrain(robot):
    global leftTurn, rightTurn, startTime, endTime, begin, done
    light = robot["light"].getData()
    sonars = noisySonars(robot, 0.0)  # <-------- MANIPULATE THE AMOUNT OF NOISE HERE
    front = min(sonars[2:6])  
    left = min(sonars[15], sonars[0])
    right = min(sonars[7:9])
    if begin:
        # at the very beginning set the starting time of the experiment
        startTime = time() 
        begin = False
    elif done:
        # when done, print the elapsed time
        print("Time to find light", endTime-startTime, "seconds")
    elif sum(light) > 1.5:
        # stop when light is found and set done to be True
        robot.move(0,0)
        endTime = time()
        done = True
    elif leftTurn and front > 0.5:
        # when front becomes clear while turning left, end turn
        leftTurn = False
        robot.move(1.0, 0)
    elif rightTurn and front > 0.5:
        # when front becomes clear while turning right, end turn
        rightTurn = False
        robot.move(1.0, 0)
    elif leftTurn:
        # continue turning left
        robot.move(-0.05, 0.75)
    elif rightTurn:
        # continue turning right
        robot.move(-0.05, -0.75)
    elif front < 0.5 and left > right:
        # when front is blocked and left side is more open, start left turn
        leftTurn = True
        robot.move(-0.15, 0.75)
    elif front < 0.5 and right > left:
        # when front is blocked and right side is more open, start right turn
        rightTurn = True
        robot.move(-0.15, -0.75)
    else:
        # otherwise go straight
        robot.move(1.0, 0)    
        
robot.brain = avoidToLightBrain

Protocol

Give a detailed protocol for your experiments here. Be sure to run multiple experiments at each level of noise that you decide to test.

Results

Present the results of your experiments here. Be sure to calculate the average time needed to reach the light for each level of noise tested. Use a markdown table to clearly present the results.

Discussion

Summarize your results here. Based on your results, how did the amount of noise affect the robot's behavior?