Base Movement

Tutorial written by Yosser D'Avanzo, 2016.

We will now focus on moving the robot precisely “x” meters (m) from its starting point. The problem we face here is that the inputs “v” for the setWheelVelocity() are in terms of meters per second (m/s). The solution is to set the wheel velocity, and then wait “t” seconds until we travel the required distance. We could try guessing how long we have to wait for, or use good old math instead. Distance divided by speed tells us the exact time we need to travel.

Note: to make the robot travel straight, set both wheel velocities to the same value.

Now let’s expand the problem a little more. The previous exercise is great if the robot is facing the right direction, but what if it isn’t? Moving to the desired angle is a similar problem. Instead of speed in meters per second, we need to find the rate the robot turns in degrees per second. We can use an empirical method to do this. Using a stopwatch, time how long it takes for the robot to perform one rotation. To make the robot rotate about its center, set the wheel velocities to equal magnitudes but opposite signs:

johnny.setWheelVelocity( 1.8 , -1.8 )

Notice that which is negative only affects the direction it turns in, but not the rate it turns at.

One rotation, or 360 degrees (deg), should be approximately equal to 9 seconds. This means that the robot rotates at a rate of 40 deg/s when one wheel is 1.8 and the other is -1.8. To move “d” degrees clockwise, we wait “t” seconds of turning where “t” is calculated by dividing “d” by the turning rate:

Using these two methods, the robot can be hardcoded to follow a predetermined path. You can have it deliver things between two people on other sides of the room or building, or attach a paintbrush to the base and have it paint the floor with intricate art - there’s plenty to do with just this.

To formalize turning, we need to be a little more clever. The concept is the same, only instead of setting the amount to turn, we input the current direction and the desired direction and move the difference. This system has many more useful applications and will make future projects more intuitive:

Note: The methods used above is known as dead reckoning and works on the assumption that there is no slipping while the robot moves. Slipping can be minimized on hard, clean floors.

navigate()

Now let's consolidate what we’ve learned so far into a function called navigate(). With a map, navigate() is used to make the robot move from its current coordinate to a destination coordinate. The map will represent the robot’s actual surroundings as an n by m list of integers where every entry is either a 0 or a 1. 0 represents open space in the room, and 1 represents an obstacle at that coordinate. Every spot on this map represents 0.5 meters of actual space. We assume the robot can travel only on the interval - no diagonals!

We can also define directions to make writing the code more comprehensive. We should also save the previous move to avoid undoing the most recent move. At first, our previous move was not moving at ( 0 , 0 ).

These definitions will also help us guide the robot in an intuitive manner. We will add a nested function in navigate() called go() which takes a move, orients the robot's direction and moves it for us:

We also need to be able to check whether next move has an obstacle. It returns true if the given move lands on a 0. Because this function is nested within navigate(), it has access to all other variables and doesn’t need them as inputs:

To determine direction to go, we need to know where the desired location is relative to the robot’s current position. The previous move will be saved in a tuple to make sure that the robot doesn't end up moving back to where it was. Initially, there are no previous moves:

x = goal_loc(0) - robo_loc(0)
y = goal_loc(1) - robo_loc(1)

So long as the robot is not at its goal, we want to loop over logic to move it. The logic will check what y-direction it wants to go based on its relative position, then check whether it is a legal and non-previous move and finally carry out the move and update its position:

The robot can now navigate arbitrary environments! This method is based on the mountain-climber approach: you only move if it immediately gets you closer to the goal. There is little planning ahead except for keeping track of your previous move. For simple floor layouts this algorithm works well, and navigate() is scalable for any map size.