Skip to main content

Introduction to Robotics

Section 6.3 Lab: Controlled movement

In this lab we’ll learn how to control the bot movement in a finer way, by for example not accelerating too fast.

Subsection 6.3.1 Rescaling and Constraining values

Before moving further we need to set up some groundwork. One of our first goals would be to look at the accelerometer values then convert those into something that can be seen on the screen.
The first thing to do is set up so something happens automatically at fixed intervals. Start by searching in the API for run_every and find its documentation, and in particular look at its use as a decorator (with the at-sign @).
Next, look at the accelerometer API and find how to get the accelerometer value for different directions. Make sure you identify which one of those directions will give you information about the bot moving forward or backward (you may need to use "More" to expand the explanations).

Practice 6.3.1.

The goal here is to make a program that prints the accelerometer value on a fixed interval, using the two pieces above. So first of all make sure you have a while True loop that simply does a sleep. This will make sure your bot is actively working by itself.
Then write a separate function decorated by run_every as in the examples you found above, to execute every 10 milliseconds. Then in that function use print to print the accelerometer value in the direction of movement. The value should be small negative numbers normally, but it can jump to the 400 range if you move the bot forward quickly. Use the abs value to always look at the absolute value.
Now that we have the accelerometer value recorded well, we need to work with it. The idea is this: We would like to connect the accelerometer to the brightness of the LED lights. But here is the problem: The accelerometer values are in a range of possibly hundreds or thousands, while the LED lights only accept an integer from 0 to 9. So our goal: Appropriately rescale a value from one range to the other. The general math we want to do with rescaling can be expressed as follows:
  • We have a value \(x\text{.}\) It comes from a range \([x_{min}, x_{max}]\text{.}\)
  • We want to convert it to a target value \(y\) in a range \([y_{min}, y_{max}]\text{.}\)
  • We want a "linear progress": If \(x\) is 20% of the way from \(x_{min}\) to \(x_{max}\) then we want \(y\) to be 20% of the way from \(y_{min}\) to \(y_{max}\text{.}\)
  • Mathematically this mean we need the following ratios to be equal:
    \begin{equation*} \frac{x-x_{min}}{x_{max} - x_{min}} = \frac{y-y_{min}}{y_{max} - y_{min}} \end{equation*}
  • Solve this equation for \(y\text{:}\)
    \begin{equation*} y = y_{min} + \frac{x-x_{min}}{x_{max} - x_{min}}(y_{max} - y_{min}) \end{equation*}
This seems perhaps a tad complicated, but there is a built-in function that does this for us, called scale. You would use it like this: y = scale(x, from_=(xmin, xmax), to=(ymin, ymax))

Practice 6.3.2.

Add to the reporting of the accelerometer value: In addition to the value itself, print its scaled value, where we want to map the (0,400) range on the accelerometer to the (0,9) range (so that a value of 400 becomes 9, basically.
Notice two things: What happens to values bigger than 400? And also, do the results produce only integers or also floating point numbers?
The next piece of the puzzle is how to handle out-of-range situations. For example an acceleration value of 400 will be too high for us and we’d want that to correspond to 9, but then a bigger value would be invalid. What we want to do is constrain the resulting value: If it is less than 0 we want it to become 0 and if it is more than 9 we want it to become 9. To that end you will create a function that does this.

Practice 6.3.3.

Write a function called constrain(x, range) that takes as input a value and a target range as a tuple (xmin, xmax). (You can read out a tuple’s values with something like (xmin, xmax) = range. The function should then return x as long as it is between the min and max, but otherwise it should return that closest point. So if x is less than xmin then the return value would be xmin and similarly if x is more than xmax.
Use the function in your accelerometer printout to make sure that the resulting scaled value is always in the range from 0 to 9.
Equipped with this functionality, now we can make the lights turn on when we accelerate too much.

Practice 6.3.4.

In addition to the printing of the values, your acceleration reporting function should also set the display as follows: Compute the scaled and constrained number as before, then start with the heart image, divide by 9 and multiply by the constrained number. You should now observe that when you move your bot too fast the heart lights up and otherwise it’s pretty dark.
How does this work: The images have values 0 through 9 and in the heart case that 9 corresponds to the bright parts. Dividing by 9 turns all those 9s into 1s. Then multiplying by the given number scales them back up. For example at half acceleration you’ll get 5s and the heart should be half-visible.
This last practice is the first objective of the lab.

Subsection 6.3.2 Ramp-up and Ramp-down

Now that we have this measure and visualization of the acceleration, let’s work on ramp-up and ramp-down. The idea is simple: You want to reach a certain target speed but "gently". Say the target speed is 160. You can reach it "gently" by increasing the speed by 20, then waiting 100 milliseconds before increasing it another 20, and so on until you reach 160. We get to control two things here: How much to increase each time (20), and for how long to run at that level (100).

Practice 6.3.5.

Your goal for this exercise is to adjust those numbers so that you achieve the following:
  • never exceed an acceleration of 400
  • minimize how long it takes to ramp up and down in total time
  • minimize how much distance is traveled
Store the ramp-up and ramp-down parts as separate functions you can call on, as we’ll be using them. Record somewhere how much distance they take together.
Our next goal now is to be able to program how much the bot moves. We want to essentially write function forward(inches) that takes as input a number of inches, then moves forward for exactly that many inches, factoring in the ramp-up and ramp-down. The first part of that is collecting some data.

Practice 6.3.6.

Create an array of times in milliseconds that we would run the bot at full speed for, starting at 0 and increasing by 500 up to 3 seconds.
For each time have the bot ramp up, then move at top speed for that amount of time, then ramp down, and compute the total distance traveled.
Draw a line plot of those distances against the corresponding times. This should hopefully appear to be pretty close to a straight line.
Now we move on to creating the forward function:

Practice 6.3.7.

Create a function forward(inches) that is being given a value in inches, and it moves the bot forward for exactly that distance. It should operate as follows:
  • First of all if the value is less than your minimum distance (the one with just ramp-up and ramp-down from earlier) then don’t do anything. Otherwise find in your table the two points in-between which the desired inches distance is. For example maybe you are trying to go for 30 inches and in your table you found that in 2000 milliseconds we get to 27 inches and in 2500 milliseconds we get to 32 inches. So that is the range, since 30 is between 27 and 32.
  • Then use scale to find the corresponding amount of milliseconds, by scaling the inches value from the 27-32 range to the 2000-2500 range (or whatever the values are in your case). This tells you the number of milliseconds you should move at your top speed.
  • Extract all this above logic into a separate function. Then use that function to do ramp-up then move at top speed for that amount then ramp-down.
Your function should be able to provide pretty accurate distances. And also your bot should be moving in a straight line.
This exercise is the second and final objective for this lab.