2 Dead reckoning

Dead reckoning is a means of navigation that does not rely on external observations. Instead, a robot’s position is estimated by summing its incremental movements relative to a known starting point.

Estimates of the distance traversed are usually obtained from measuring how many times the wheels have turned, and how many times they have turned in relation to each other. For example, the wheels of the robot could be attached to an odometer, similar to the device that records the mileage of a car.

In RoboLab we will calculate the position of a robot from how long it moves in a straight line or rotates about its centre. We will assume that the length of time for which the motors are switched on is directly related to the distance travelled by the wheels.

By design, the simulator does not provide the robot with access to any magical GPS-style service. In principle, we could create a magical ‘simulated-GPS’ sensor that would allow the robot to identify its location from the simulator’s point of view; but in the real world we can’t always guarantee that external location services are available. For example, GPS doesn’t work indoors or underground, or even in many cities where line-of-sight access to four or more GPS satellites is not available.

Furthermore, the robot cannot magically teleport itself to a new location from within a program. Only the magics can teleport the robot to a specific location…

Although the simulator is omniscient and does keep track of where the robot is, the robot must figure out for itself where it is based on things like how far the motors have turned, or from its own sensor readings (ultrasound-based distance to a target, for example, or gyroscope heading); you will learn how to make use of sensors for navigation in later notebooks.

2.1 Activity – Dead reckoning

An environment for the simulated robot to navigate is shown below, based on the 2018 First Lego League ‘Into Orbit’ challenge.

The idea is that the robot must get to the target satellite from its original starting point by avoiding the obstacles in its direct path.

Space scene showing the robot, some satellites against a ‘space’ background, and some wall-like obstacles between the robot’s starting point and a target satellite.

The First Lego League (FLL) is a friendly international youth-based robot competition in which teams compete at national and international level on an annual basis. School teams are often coached by volunteers. In the UK, volunteers often coach teams under the auspices of the STEM Ambassadors Scheme. Many companies run volunteering schemes that allow employees to volunteer their skills in company time using schemes such as STEM Ambassadors.

Load in the simulator in the usual way:

from nbev3devsim.load_nbev3devwidget import roboSim, eds

%load_ext nbev3devsim

To navigate the environment, we will use a small robot configuration within the simulator. The robot configuration can be set via the simulator user interface, or by passing the -r Small_Robot parameter setting in the simulator magic.

The following program should drive the robot from its starting point to the target, whilst avoiding the obstacles. We define the obstacle as being avoided if it is not crossed by the robot’s pen down trail.

Load the FLL_2018_Into_Orbit background into the simulator. Run the following code cell to download the program to the simulator and then, with the pen down, run the program in the simulator.

Remember, you can use the -P / --pencolor flag to change the pen colour and the -C / --clear option to clear the pen trace.

Does the robot reach the target satellite without encountering any obstacles?

%%sim_magic_preloaded -b FLL_2018_Into_Orbit -p -r Small_Robot

# Turn on the spot to the right
tank_turn.on_for_rotations(100, SpeedPercent(70), 1.7 )

# Go forwards
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 20)

# Slight graceful turn to left
tank_drive.on_for_rotations(SpeedPercent(35), SpeedPercent(50), 8.5)

# Turn on the spot to the left
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.8)

# Forwards a bit
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 2.0)

# Turn on the spot a bit more to the right
tank_turn.on_for_rotations(100, SpeedPercent(60), 0.4 )

# Go forwards a bit more and dock on the satellite
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 1.0)

say("Hopefully I have docked with the satellite...")

Add your notes on how well the simulated robot performed the task here.

To set the speeds and times, I used a bit of trial and error.

If the route had been much more complex, then I would have been tempted to comment out the steps up I had already run and add new steps that would be applied from wherever the robot was currently located.

Note that the robot could have taken other routes to get to the satellite – I just thought I should avoid the asteroid!

2.1.1 Using motor tacho counts to identify how far the robot has travelled

In the above example, the motors were turned on for a specific amount of time to move the robot on each leg of its journey. This would not be an appropriate control strategy if we wanted to collect sensor data along the route, because the on_for_X() motor commands are blocking commands.

However, suppose we replaced the forward driving tank_drive.on_for_rotations() commands with commands of the form:

from time import sleep

tank_drive.on(SPEED)
while int(tank_drive.left_motor.position) < DISTANCE:
    # We need something that takes a finite time
    # to run in the loop or the program will hang
    sleep(0.1)

Now we could drive the robot forwards until the motor tacho count exceeds a specified DISTANCE and at the same time, optionally include additional commands, such as sensor data-logging commands, inside the body of each while loop.

As well as tank_drive.left_motor.position we can also refer to tank_drive.right_motor.position. Also note that these values are returned as strings and need to be cast to integers for numerical comparisons.

2.1.2 Activity – Dead reckoning over distances (optional)

Use the .left_motor.position and/or .right_motor.position motor tacho counts in a program that allows the robot to navigate from its home base to the satellite rendezvous.

Your design notes here.

# YOUR CODE HERE

Your notes and observations here.

2.2 Challenge – Reaching the moon base

In the following code cell, write a program to move the simulated robot from its location servicing the satellite to the moon base identified as the circular area marked on the moon in the top right-hand corner of the simulated world.

In the simulator, set the robot’s x location to 1250 and y location to 450.

Use the following code cell to write your own dead-reckoning program to drive the robot to the moon base at location (2150, 950).

%%sim_magic_preloaded

# YOUR CODE HERE

2.3 Dead reckoning with noise

The robot traverses its path using timing information for dead reckoning. In principle, if the simulated robot had a map then it could calculate all the distances and directions for itself, convert these to times, and dead reckon its way to the target. However, there is a problem with dead reckoning: noise.

In many physical systems, a perfect intended behaviour is subject to noise – random perturbations that arise within the system as time goes on as a side effect of its operation. In a robot, noise might arise in the behaviour of the motors, the transmission or the wheels. The result is that the robot does not execute its motion without error. We can model noise effects in the mobility system of our robot by adding a small amount of noise to the motor speeds as the simulator runs. This noise component may speed up or slow down the speed of each motor, in a random way. As with real systems, the noise represents slight random deviations from the theoretical, ideal behaviour.

For the following experiment, create a new, empty background cleared of pen traces.

%sim_magic -b Empty_Map --clear

Run the following code cell to download the program to the simulator using an empty background (select the Empty_Map) and the Pen Down mode selected. Also reset the initial location of the robot to an x value of 150 and y value of 400.

Run the program in the simulator and observe what happens.

%%sim_magic_preloaded -b Empty_Map -p -x 150 -y 400 -r Small_Robot --noisecontrols


tank_drive.on_for_rotations(SpeedPercent(30),
                            SpeedPercent(30), 10)

Record your observations here describing what happens when you run the program.

When you run the program, you should see the robot drive forwards a short way in a straight line, leaving a straight line trail behind it.

Reset the location of the robot. Within the simulator, use the Noise controls to increase the Wheel noise value from zero by dragging the slider to the right a little way. Alternatively, add noise in the range 0...500 using the --motornoise / -M magic flag.

Run the program in the simulator again.

You should notice this time that the robot does not travel in a straight line. Instead, it drifts from side to side, although possibly to one side of the line.

Move the robot back to the start position, or rerun the previous code cell to do so, and run the program in the simulator again. This time, you should see it follows yet another different path.

Depending on how severe the noise setting is, the robot will travel closer (low noise) to the original straight line, or follow an ever-more erratic path (high noise).

Record your own notes and observations here describing the behaviour of the robot for different levels of motor noise.

Clear the pen traces from the simulator by running the following line magic:

%sim_magic -C

Now run the original satellite-finding dead-reckoning program again, using the FLL_2018_Into_Orbit background, but in the presence of Wheel noise. How well does it perform this time compared to previously?

%%sim_magic_preloaded -b FLL_2018_Into_Orbit -p -r Small_Robot

# Turn on the spot to the right
tank_turn.on_for_rotations(100, SpeedPercent(70), 1.7 )

# Go forwards
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 20)

# Slight graceful turn to left
tank_drive.on_for_rotations(SpeedPercent(35), SpeedPercent(50), 8.5)

# Turn on the spot to the left
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.8)

# Forwards a bit
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 2.0)

# Turn on the spot a bit more to the right
tank_turn.on_for_rotations(100, SpeedPercent(60), 0.4 )

# Go forwards a bit more and dock on the satellite
tank_drive.on_for_rotations(SpeedPercent(30), SpeedPercent(30), 1.0)

say("Did I avoid crashing and dock with the satellite?")

Reset the robot to its original location and run the program in the simulator again. Even with the same level of motor noise as on the previous run, how does the path followed by the robot this time compare with the previous run?

Add your own notes and observations here.

2.4 Summary

In this notebook, you have seen how we can use dead reckoning to move the robot along a specified path. Using the robot’s motor speeds and by monitoring how long the motors are switched on for, we can use distance–time calculations to estimate the robot’s path. If we add in accurate measurements regarding how far we want the robot to travel, and in what direction, this provides one way of helping the robot to navigate to a particular waypoint.

However, in the presence of noise, this approach is likely to be very unreliable: whilst the robot may think it is following one path, as determined by how long it has turned its motors on, and at what speed, it may in fact be following another path. In a real robot, the noise may be introduced in all sorts of ways, including from friction in the motor bearings, the time taken to accelerate from a standing start and get up to speed, and loss of traction effects such as wheel spin and slip as the robot’s wheels turn.

Whilst in some cases it may reach the target safely, in others it may end somewhere completely different, or encounter an obstacle along the way.