Dive into robotics with the ActivityBot 360° robot and BlocklyProp visual programming! This tutorial will show you how.
This tutorial set will show you how to:
Along the way, you will learn some robotics, engineering, electronics, and programming concepts that you can apply to your inventions.
If you have an original ActivityBot with external encoders (#32500) or a classroom that uses both original and ActivityBot 360° robots, follow the Legacy Edition [4] of this tutorial instead.
Follow these short tutorials to get comfortable with BlocklyProp programming basics.
Once you have completed this ActivityBot 60° tutorial, you will be able to make your robot navigate on its own with different kinds of sensors. You will also be ready to create new challenges for your robot and to add a variety of sensors and accessories to help with those challenges.
If your ActivityBot 360° belongs to a school, it may have been put together already.
Replacement Parts: Most of the pieces in the kit are available for purchase individually online from www.parallax.com [9]. Or, email sales@parallax.com or call 888-512-1024.
(2) Feedback 360 servos (#900-00360)
If your ActivityBot servos do NOT have a yellow wire, and are labeled "High Speed Continuous Rotation Servos" you have an older kit. You will need to use the Legacy Version [4] of this tutorial instead.
You will have parts left over after assembly.
Parts needed for this step:
Parts needed for this step:
All done:
In the next few steps you will mount the right and left servos on the chassis. But which side is right and which side is left?
Parts needed for this step:
You can use regular nuts or lock-nuts to mount the servos. Lock-nuts have white Nylon inserts that make a tight grip on screws, so they don't loosen easily with vibration. But, they take a little more force to install.
If you want to use the lock-nuts, press the closed end of the little black wrench over each lock-nut to hold it in place while attaching the screws.
Parts needed for this step:
Parts needed for this step:
Parts needed for this step:
CAUTION - the ends of the cotter pin may be sharp! If you have needle-nose pliers, you can bend the cotter pin ends into a circle to tuck away the points. But be aware that this makes the cotter pin more difficult to remove in the future if you ever want to do that.
Parts needed for this step:
When you finish, you can turn your robot chassis right-side up.
Parts needed for this step:
For this construction step, you will need:
Battery Type Matters!
The Feedback 360° servos do best 6 to 8 volts. Five alkaline batteries provide 5 x 1.5 V = 7.5 V. Five NiMH batteries provide 5 x 1.2 V = 6 V. NiMH batteries perform well, but Ni-Cad rechargeable batteries do not perform well in this application and are not recommended.
Each pair of 3-pin servo ports along the top of the Propeller Activity Board (original or WX version) has a shunt jumper on power-select pins to its immediate left.
IMPORTANT! Make sure there are no cables plugged into the board. Moving jumpers with power connected could damage your board.
Missing a Voltage Selection Jumper? Replacements are available - contact our sales department [10] to order part #452-00043; there are three per board.
Each servo has a 3-wire cables: white = signal, red = power, black = ground. When plugging these cables into servo headers, be sure to align white wires with the P12 & P13 labels along the edge of the PCB, and the black wires along the row labeled GND (see the picture below).
The Feedback 360° servo also has a single yellow feedback wire, which plugs into a separate servo header's I/O pin.
It's time to program your ActivityBot 360°! Make sure you are ready. If you have not done the prerquisite tutorials yet, now is the time!
BlocklyProp is a graphical online programming tool for the Propeller microcontroller.
It's a good idea to become familiar with the basics of BlocklyProp programming before trying to program your ActivityBot robot.
This tutorial shows you how to build functions—reusable chunks of code—with blocks. Then, it explains what multicore means, and how to launch these functions into different Propeller chip cores so they can run at the same time. It's a powerful tool for robotics, one that is beneficial to understand before you go further.
It's tempting to dive right into robot navigation. We understand! But, as with any vehicle, it's wise to get familiar with the controls before attempting to drive.
In this section you will:
If you want to learn more, read the product guide:
32912 Product Guide [13]
The breadboard lets you build your own circuits with common electronic components. It's a great way to learn about electricity, and to experiment with making your own inventions. Building experimental circuits to design your own projects is called prototyping, and it is a real-world engineering skill.
The breadboard is surrounded on three sides by black sockets. These make it convenient to connect circuits on the breadboard to power, ground, and the Propeller I/O pins. There are also sockets to connect to a digital-to-analog converter and an analog-to-digital converter.
If you have never built circuits on a breadboard before, watch this video about breadboards and how to build circuits on them. History can be pretty interesting!
There are two built-in light-emitting diodes (LEDs) near the bottom-right corner of the Activity Board WX. They are already electrically connected to Propeller I/O pins P26 and P27.
These LEDs are helpful when developing applications that use sensors. The idea is to build a program that turns on an LED when a sensor is activated. Then you, the roboticist, can get a quick visual cue that the sensor is actually detecting something.
The Propeller microcontroller has 32 input/output pins, or I/O pins, labeled P0 through P31. The Propeller can interact with other circuits connected to these I/O pins, through programs that use these labels. A Propeller I/O pin can do three things:
We'll experiment with the "O" (output) feature of an I/O pin by programming the Propeller to turn a light on and off. The light circuit, which includes a small LED and resistor, is already built into the Propeller Activity Board (original or WX) or the Propeller Board of Education.
In BlocklyProp, you can turn an LED on or off with a single block: the Pin states category's make PIN block.
This program uses four blocks enclosed by a repeat forever loop.
The first block in the loop is make PIN (26) high. This sets the Propeller chip's P26 I/O pin to output-high, which connects the pin to its 3.3 V supply. The pin applies 3.3 V of electrical pressure to the LED circuit, causing electric current to pass through it and the light to turn on. After that, a pause(500) block makes the program do nothing for 500 ms, which keeps the light on for half of a second.
Next, make make PIN (26) low sets P26 to output-low, which connects the pin to its 0 V ground supply voltage instead. This takes away the electrical pressure, so the current stops flowing through the circuit and the light turns off. Another pause(500) makes the light stay off for half of a second.
After the second 500 ms pause, code execution starts over at the first block in the loop, and repeats these four steps forever (or until you turn the power off!)
The picture below is called a timing diagram. It is a graph with voltage on the vertical axis, and time on the horizontal axis. Timing diagrams are often used to describe the electrical interaction between microcontrollers and circuits or other devices. This timing diagram shows the output of I/O pin P26 from the test code above.
You can make the light blink faster or slower by changing the value in the pause block. You can also make the on-time different from the off-time by using different values in the two pause blocks.
Try controlling the P27 light along with the P26 light, according to the timing diagrams below.
The first breadboard circuit we'll build is a simple piezo speaker. Many devices in our daily lives use beeps to communicate with us, such as microwave ovens, car locks, smoke detectors, forklifts driving in reverse—can you think of more examples? Likewise, it is useful to equip your ActivityBot to communicate with you via sound.
Inside the piezo speaker is a thin piece of material called a piezoelectric element that deforms when voltage is applied to it, and returns to its original shape afterwards. When voltage is switched on/off rapidly, the vibration emits a tone. The faster the switching frequency, the higher the tone produced. A Propeller I/O pin can provide this rapidly toggling voltage at specific rates.
(1) Piezo speaker (#900-00001)
(2) Jumper wires
The Audio > Frequency out block can make piezospeaker beep.
The Frequency out block has three fields to set. PIN specifies the Propeller I/O pin connected to the piezospeaker, 4 in our circuit. The duration field sets how long the beep will last, 1000 ms (1 second) in this example. The frequency (Hz) field sets the pitch of the beep, 3000 Hz in this example. To generate this frequency, the Propeller sets P4 to an output and rapidy toggles it between 0 V to 3.3 V, causing the piezoelectric element inside the speaker to vibrate 3000 times per second. The result is a high-pitched tone.
Know when you are low — A brownout indicator signals to you, the roboticist, when your ActivityBot's batteries are running low. If the battery voltage drops too low, the Propeller microcontroller will reset and restart the program. This can look like a programming or circuit error if you don't know it is happening! So, you will see this block at the beginning of any ActivityBot example program that is meant to make the robot move. A low-battery beep will help you troubleshoot your ActivityBot projects by ruling out low battery power as the culprit.
What is a brownout? - You may be familiar with the word blackout; a blackout is when power dies comepletely, like when the power goes out in your home. A brownout means that there is still power, just not enough to make everything work.
The Frequency out block's duration (ms) and frequency (Hz) fields can accept use variable blocks and even Operators > Numbers category blocks, as well as the default number value blocks. Sometimes it is fun or useful to have a tone vary in correlation to some sensor's input values, so you can know what the ActivityBot is sensing. For now let's just play with the idea using a variable that's counting loops in a repeat item block.
This section shows you how to make your ActivityBot 360° move. It covers:
From here forward, we will be talking about the robot's right and left sides, and its front and back. These terms are from the perspective of a tiny person sitting on the white breadboard, with a hand on each post and feet dangling past the edge of the board.
It's time to test your servos' feedback signal connections. If all is well, the example program below will cause the built-in LED on the Activity Board WX to blink on and off when you gently turn a wheel by hand.
Here are some symptoms and causes:
Calibration is optional, but it will improve the accuracy of driving maneuvers. This one-time calibration aids the Robot blocks in measuring and correcting distances and speeds, using information from the servo's feedback connection.
The calibration code takes about one minute to collect all of its data. You will need a smooth, and obstacle-free, floor area. While the calibration program is running, the ActivityBot 360° will pivot forward and backward, using only one wheel at a time. It will let you know when it's done by turning off its P26 and P27 lights (below the breadboard). This video shows an ActivityBot correctly performing the calibration.
IMPORTANT! USE FRESH BATTERIES FOR THIS STEP!
What if it didn't work?
If your robot did not move when you started the calibration program, or it started going backward first instead of forward, or if it started and stopped right away or just twitched, then double-check your electrical connections. You may have a servo cable or feedback signal wire plugged in incorrectly.
What if I need to restart or repeat the calibration?
To restart the calibration process, push the reset button any time while the P26 and P27 lights are on.
To repeat the calibration process once it has fully completed, you will need to use BlocklyProp to reload the program. That is because the program modifies itself at the end so that it cannot run a second time, which keeps your ActivityBot from trying to recalibrate the next time you turn power on to load a new program.
Notice that the Robot calibrate block does not have any sockets or plugs for connecting it to other blocks. It is meant to be used by itself without any other blocks in a program, and it takes care of the entire calibration process automatically.
The Robot calibrate block collects requested speed vs. measured speed data and stores it in a part of the Activity Board’s EEPROM memory, where it can retain data even after you turn the power off. That way, the calibration data is available every time you turn the robot back on.
Later, when your project includes Robot blocks that set a certain wheel speed, the underlying code will use the calibration data from EEPROM to start driving the motors at speeds close to what your program asks. That way the code doesn’t have to make large corrections, just small ones, which improves overall accuracy.
Encoders - An encoder converts information from one format to another. The ActivityBot's encoders convert wheel rotation or position into digital information that the Propeller microcontroller can use.
Encoder Ticks are not insects - Throughout this tutorial, you will see the term ticks when used to describe the ActivityBot's wheels turning. In this context, a tick means 1/64th of a wheel revolution. When the wheel makes one complete revolution, it has rotated 64 ticks. We use ticks per second to note how fast the wheels are turning. Ticks can also refer to distance traveled when considering the circumference of the ActivityBot wheel.
Feedback 360° Servo's Internal Encoders - Each Feedback 360° servo has a tiny magnet inside that is attached to the motor shaft. Also inside the servo's case is a Hall-effect sensor, which can detect changes in the position of the magnetic field as the motor shaft rotates. A tiny processor monitors the Hall-effect sensor and sends position information to the Propeller I/O pin. (For more details, see the Feedback 360° Servo product guide [14].)
There is a block that can check the ActivityBot's calibration data and tell you if there are any mistakes or problems with the ActivityBot's servo, encoder, and battery connections. If this test says the calibration was successful, your ActivityBot will be ready to run navigation programs. If not, then it will tell you what problem(s) it detects. After finding and fixing the problem, make sure to run both the calibration and this test again. Your ActivityBot will not be ready for navigation until it is calibrated AND it passes this test!
Now that you have successfully calibrated your ActivityBot 360°, it is time to run a simple test program to make it drive straight forward. The test code below makes both wheels rotate forward at 64 encoder ticks per second for 5 seconds, which should make your ActivityBot roll forward about 1 meter (3 ft.).
Your robot should travel forward for about 1 meter (3 ft.) in a straight line. You may see slight shifts as the robot adjusts its course based on encoder feedback.
The first block is Robot initialize, with the drop-down menu set to ActivityBot 360°. Place this block at the beginning of all projects that use other Robot set or Robot drive blocks.
Next is a Robot drive speed block with the number value 64 attached to both its left and right sockets. This makes both the left wheel and right wheel rotate forward at a rate of 64 ticks per second. The pause 5000 block allows the ActivityBot to drive forward at this rate for 5000 ms (5 seconds). After that, a second Robot drive speed block stops the right and left wheels with the number values 0, 0. We will explore this next.
The Robot drive distance block tells the ActivityBot 360° how far each wheel should turn. It is intended for projects where the goal is to drive specific distances and turn specific angles. You can use positive values for forward distances and negative values for backward ones.
The default distance unit for Robot drive distance is 3.25 mm ticks. Inches and centimeters are also options in the drop-down menu. The example below shows how to make the ActivityBot roll forward 208 mm by setting the left and right distances for 64 ticks. We've used this value because 64 x 3.25 mm = 208 mm.
The Robot drive distance block will make the ActivityBot move:
Here are some examples – look carefully at the sign (+/-) of each value:
The ActivityBot 360° wheel circumference is 208 mm, so a full turn makes the wheel travel that far. A single tick is equal to 1/64th of a full turn, and 1/64th of 208 mm works out to 3.25 mm.
If you know how far you want your ActivityBot 360° to roll, you could divide the distance by 3.25 mm (or 0.325 cm or 0.00325 m) to find out how many ticks your program needs to tell the ActivityBot to travel.
ticks = distance mm ÷ 3.25 mm/tick
The ActivityBot 360° pivot turning radius is the distance between the wheels. Ideally, it's 105.8 mm, but that will vary slightly with the shape of each aluminum chassis and also with how the servos are mounted. The left wheel will have to turn 2 × π × r to make the ActivityBot pivot by a full circle while holding the right wheel still. In this case, the r is the turning radius, so that’s 2 × π × 105.8 mm ≈ 664.76 mm.
Keep in mind that the turning radius will be slightly different for each ActivityBot 360°, so you may have to tune the number of ticks for a 1/4 turn. For example, your robot's turning radius might need 50, 52, or 53 ticks to get as close as possible to a 1/4 turn.
This test code makes your robot go forward 128 ticks, and then make a 51-tick, 1/4 turn, to face 90- degrees to the right.
First comes the comment block noting the project name. Next, the frequency out block for the piezospeaker on P4 acts as a low-battery brown-out indicator.
After the required Robot initialize block, there are two Robot drive distance blocks, set to measure distance in ticks.
The first Robot drive distance block moves both wheels forward with left 128 and right 128. Since 128 is enough ticks for two full wheel revolutions, the forward distance traveled is 3.25 x 128 = 416 mm, or about 16 inches.
The second Robot drive distance block uses left 26 and right -25 to move the wheels in opposite directions. As explained above, that is a combined 51 ticks, causing the ActivityBot 360° to rotate in place, turning 90 degrees to the right.
Drive the distance, no distractions — The Robot drive distance block must complete the wheel turns for its target distance before it stops the motors. Only then will it allow the cog's code execution to continue to the next block. This behavior is called blocking, meaning that it makes the cog wait until it is complete before allowing the program the cog is running to continue. It's a lot like pause and frequency out, except that we know precisely how long each of those blocks will take. With Robot drive distance, we don't necessarily know the exact amount of time a given maneuver will take.
Set your speed limit! — The Robot drive speed block is introduced on another page. Unlike the Robot drive distance block, the Robot drive speed block is non-blocking. It sets the speed and then immediately moves to the next block as it maintains the wheel speeds you have set.
Unit Options — The Robot drive distance block's inches and centimeters options work only with the wheels that come with the ActivityBot robot. If you use wheels with a different circumference, the distance traveled per tick will change.
Line, arc, pivot, rotate in place — The values you use affects the path the ActivityBot takes. Use the same values for a straight line. For example 64, 64 makes the ActivityBot go forward. -64, -64 makes it go backward. Use different values for arcs, like 128, 64. To pivot on one wheel, set one of the two values to 0. To make the ActivityBot rotate in place, use positive values, like 26, -26.
Let's try making the ActivityBot 360° navigate some basic shapes:
By repeating the forward and 90-degree turn maneuver 4 times, the program can make the ActivityBot draw a square.
Repeat x times need to change from 4 to 3, and -26 and 25 values need to change to make 120 degree turns instead of 90-degree turns.
Here is some arc code.
An example course is shown below. To run this particular course, the ActivityBot must perform the following maneuvers:
The Robot drive distance block was good for fixed courses and maneuvers. The Robot drive speed block lends itself to navigation with sensors. The simplest way to use it is to attach a number value block to its left and right fields. Each number indicates how fast each wheel should turn in units of ticks per second. Remember, each tick is 1/64th of a wheel turn and moves the ActivityBot's wheel by 3.25 mm.
After setting the wheel speeds, the Robot drive speed block allows the Propeller to execute whatever block comes next immediately. So, if you want your ActivityBot 360° to keep going at a specific speed for a certain amount of time, add a pause (ms) block between Robot drive speed and the next block that sets wheel speeds.
Here are some other tips to remember:
Wheel direction in the rest of this tutorial refers ONLY to turning forward with positive speed values, or backward with negative speed values. It does NOT refer to clockwise or counterclockwise rotation of the individual wheel. A positive value will cause a wheel to turn in the robot's forward direction. A negative value will make it turn in the robot's backward direction.
This short example makes the ActivityBot 360° go straight forward at medium speed (64 ticks/s) for 3 seconds. After that, it makes the robot go backward faster (-96 ticks/s) for 2 s. After that, it stops.
Your ActivityBot 360° should drive forward for 3 seconds, then backward more quickly for 2 seconds.
Did you expect your robot to return to its exact starting position? It stands to reason since 64 x 3 = 192 and -96 x 2 = -192. In a perfect world, that would be 192 ticks forward followed by 192 ticks backward.
In reality, it takes time to speed up during the 3 seconds. During the next 2 seconds, the ActivityBot has to slow down, then speed back up in reverse. So, its reverse distance traveled will be a little less. As was stated previously, when the goal is to travel a certain distance, you should use the Robot drive distance block. It accounts for speeding up, cruising speed, and slowing down. The Robot drive speed block only works to maintain wheel speeds.
The comment block holds the project's name. Next, the frequency out block makes a beep on the P4 piezospeaker to let you know the program has started running. The Robot initialize block is required before using other robot navigation blocks. These three blocks will appear together at the top of most programs that make the ActivityBot move.
The next five blocks define the ActivityBot's maneuvers.
Enforcing the speed limit — No matter what values you attach to Robot drive speed left and right, the C code underlying BlocklyProp will use a maximum value of +/- 128. For most applications, +/- 96 is a better maximum value. If you want to lower the maximum value, you can put a Robot set max speed block at the top of your project, which can be useful if you attach variables or equations for the left and right speed values.
Just keep on rolling — Robot drive speed is a "set-it-and-forget-it" block. It starts the wheels turning at the chosen speed, and then code execution immediately continues with the next block that follows. The wheels will keep turning at that speed until another Robot drive block changes or stops them.
Speed vs. velocity — Velocity is like speed, but it can be either positive (forward motion) or negative (backward motion). Speed itself is just velocity without the sign that indicates direction; velocity's absolute value.
Little things add up — A small variation between what you want your ActivityBot to do and what it does may go unnoticed once or twice, such as going just beyond a target distance or stopping short of a rotation angle. But when these small differences happen one after another, their accumulated effect can be noticeable. Sometimes referred to as error propagation, it is an important concept in robotics, programming, and engineering. And, you may run into it sooner than you think!
Later, your ActivityBot will have to do things such as executing turns until its obstacle sensor gives the all clear. In that kind of situation, the Robot drive speed block will set the servo speeds to make the ActivityBot turn in place. Since Robot drive speed is "set-it-and-forget-it," the program can immediately move on to blocks that monitor sensors. After the sensor detects an opening, the program can go back to a Robot drive speed block that makes the ActivityBot drive forward again.
So, let's try making the ActivityBot turn in place after going forward by using left 52 and right -52 in the second Robot drive speed block. The left 52 will cause the left wheel to slow slightly, from 64 to 52 ticks per second. The right -52 will cause the left wheel turn backward at 52 ticks per second, making the robot turn right. In this example, we are using a pause (ms) 500 block to make the robot turn in place for a fixed amount of time. Just keep in mind that later, this turning in place time will be determined other things, for example when a sensor detects an opening.
Without any stops and pauses, the ActivityBot's motions seem to blend. If you ever need motions to be more distinct while using the Robot drive speed block, you can add a Robot drive stop and a short pause (ms) block after each maneuver.
Doing this will give the ActivityBot time to stop after going forward for 3 seconds, and before making the turn.
Another example of something your ActivityBot might do with sensors is to go down a corridor, check for openings to the left and right, and then return. You can make the ActivityBot do this by combining portions of the two example programs on this page.
The example program on the previous page moved the ActivityBot's wheels at the same speed but opposite directions - one forward and one backward - to make the robot turn. Other wheel speed/direction combinations perform other kinds of turning maneuvers. Turning maneuvers can be grouped into three general categories:
Wheel speeds are equal, but velocity is opposite to make the ActivityBot turn about its center. This rotation is great for making the robot face a new direction in a tight place and without changing its position. Imagine a pen going through the center of the ActivityBot's "axle." A rotating turn would not draw any curved lines; it would just rotate on this point making the dot darker! You could think of this as an arc with a radius of zero.
One wheel turns forward or backward while the other stays still. Now the center of the circle is the wheel that is not moving, and the ActivityBot pivots about this point. An imaginary pen through the center of the robot would draw a circle with a radius equal to half of the ActivityBot's wheelbase of about 106 mm.
With arcs, both wheels are turning but at different speeds. They may be moving in the same or opposite directions.
Opposite-direction tight arcs: If the wheels are going in opposite directions, the arc created by our imaginary centroid-pen would have a radius between zero (if the speeds were almost the same like a rotating turn) and 53 mm (if one wheel speed is set to almost zero, like a pivot turn).
Same-direction wide arcs: If the wheels are turning different speeds but the same direction. Going back to the imaginary centroid-pen, the radius of such arcs can be anything from about 53 mm to theoretically infinite. The closer the speeds are to each other, the larger the arc radius will be. (Of course, if the speeds are equal, the trajectory is straight.)
This program progresses through the four types of turns described above. The ActivityBot will come to a stop and beep between each type of turn so you can more easily see the relationship between the blocks and the maneuvers.
After the required Robot initialize block, the rest of the code is divided into four sections of five blocks each.
Variable Speeds — The Robot drive speed block will accept any block for its left or right fields that resolves into a number value, such as variables and equations. Using a variable for a driving speed can allow the maneuvers to change during runtime without additional blocks. Remember also that the block's underlying C code libraries automatically limit the range to +/- 128, and that you can limit the range further with the Robot set max speed block.
The following example project keeps one wheel's speed steady while gradually reducing the other with a variable. Can you predict what the maneuver will be like?
Did your ActivityBot do what you predicted?
To thoroughly understand turning maneuvers, make copies of the Taking Turns project under new names, and try the following variations:
Acceleration is a measurement of how quickly something speeds up or slows down. The ActivityBot's acceleration can be adjusted, which will be important in some robotic applications. Some examples:
The ActivityBot robot's acceleration can be adjusted with the Robot set acceleration block.
Choose the speed option for use with Robot drive speed blocks, choose distance for use with Robot drive distance blocks. The acceleration setting for distance maneuvers is typically slower to help improve accuracy. The acceleration setting for speed is usually a little faster to give the robot more peppy responses in sensor navigation.
You can also change the maximum speed limits for Robot drive speed and Robot drive distance maneuvers.
The default speed limit for Robot drive speed is 128 ticks per second. Even though your code can change the speed at any time, there are some cases when sensors and variables might work together to ask for some unreasonably high speeds. Limiting the speed with this block can prevent some "runaway" conditions.
The default speed limit for Robot drive distance maneuvers is 64 ticks per second. That's the cruising speed the ActivityBot reaches while on its way to its distance goal, unless you use this block to change it. For transporting a drink, it might be safer to pick a slower cruising speed. In a timed dead reckoning navigation contest, you might want to experiment with higher cruising speeds.
This example sets the acceleration and cruising speed for Robot drive distance blocks to high values while it drives forward, then resets to low values while it drives backwards. The overall traveled for both maneuvers is the same, so the ActivityBot returns to where it starts. As you watch your robot go, pay close attention to two things:
The first block in the program is a comment noting the project name. The second block is frequency out, which makes a piezospeaker tone. If that tone plays in the middle of the program instead of at the beginning, it could indicate that the batteries are low. The third block is the required ActivityBot initialize. These first three blocks are used in all example programs that make the ActivityBot move.
Next, Robot set acceleration block is configured for distance at 600 ticks/s2. The Robot set max speed block is set for distance, at 128. That determines the Robot drive distance block's target cruising speed, which is 2 full revolutions per second. After these settings, the first Robot drive distance block makes the ActivityBot go 256 ticks forward. The high acceleration setting causes the ActivityBot to speed up and slow down abruptly, while the high Robot set speed setting directs the ActivityBot to use a high cruising speed to travel the distance.
When your robot completes the first maneuver, the next set of Robot set acceleration and Robot set max speed blocks have much lower values. Then, a Robot drive distance block tells the ActivityBot to back up the same distance it just went forward, 256 ticks. You will see that with the new acceleration setting of just just, 50 ticks/s2, the ActivityBot only gradually speeds up and slows down. With the new max speed of just 64, the cruising speed is slower.
What's in a name? — The acceleration setting in the Robot set acceleration block is in units of ticks per second squared. It does not matter if your Robot drive distance blocks are also using ticks, or centimeters, or inches. The result will be the same.
Setting the acceleration Robot drive speed blocks is about the same as it is for distance maneuvers. The next project shows the effect that different acceleration settings have, when speed and drive time are the same.
This time, the robot does not make it back to where it started. The robot drive speeds were the same in both directions (+/- 128) and the drive time was the same, pause (ms) 2000. So what happened? Going forward, the acceleration setting was 600 ticks/s2 but going backwards, the setting was only 300 ticks/s2. The slower acceleration rate makes it take longer for the robot to reach full cruising speed, so it wasn't able to travel the same distance in the same amount of time.
Now, let's add the effect of constraining the max speed in the driving-backward maneuver.
After driving forward the same distance as last time, how far did it make it back? It should stop even further from the starting point. Why?
Whisker sensors allow your robot to detect obstacles by bumping into them. When the robot's whisker bumps into an object, it makes an electrical connection between the whisker and a circuit on the breadboard. In this lesson, you will program your robot’s Propeller microcontroller to detect those contacts and make the robot avoid obstacles.
The next picture shows whisker wiring. Along with the whisker circuits, it has the same piezospeaker we've been using.
It is essential to test the whisker switches before programming your robot to navigate with them. That way, you can be sure the whiskers are sending the right messages. Otherwise, they might be telling the Propeller that they detect something even though they didn't, or vice-versa.
Without pressing any whiskers, the Terminal should display Left = 1 and Right = 1. If either of the numbers is 0, or if either of them flickers between 1 and 0, there is probably a circuit error.
Next, let's check to make sure the Propeller chip detects when the right whisker is pressed or makes contact with an obstacle.
The project starts with a Terminal clear screen block, followed by our "reset indicator" (frequency out) block that beeps the piezospeaker.
After that, the program enters a repeat forever loop. Inside the loop, the first task is to check the state of each I/O pin connected to a whisker circuit - P7 and P8. The value of each state is stored with a Set variable block, wL for the left whisker connected to P7, and wR or the whisker on P8.
The Propeller I/O pin reads a 1—a "high" signal when the circuit is inactive, that is when the whisker is not being pressed. When the circuit is active as the whisker is being pressed, the Propeller I/O pin reads a 0—a "low" signal.
The next four blocks display the whisker states. A Terminal print text block followed by a Terminal print number block to show each variable and its value. These blocks are followed by a pause (ms) 150, which slows down the loop just enough to let you see the data in the Terminal. A Terminal clear screen block comes last which allows fresh data to be displayed on the next trip through the loop.
Each robot whisker forms a normally-open switch in an active-low circuit with a 10 kΩ pull-up resistor. In its normal, unpressed, inactive state (left), the whisker switch is open - the whisker wire does not touch the 3-pin header post - keeping GND (0 V) out of the circuit. There is no path from GND to P8. So, all we have is 3.3 V on one end of a pair of resistors, and P8 set to input on the other end, sensing 3.3 V and so reporting a high signal (1).
In its active state (left), the pressed whisker switch closes when the wire touches the 3-pin header post. Now P8, through the 220 Ω resistor, 3-pin header, whisker, mounting post, and the mounting hole in the Activity Board, is connected to GND (0 V). P8 set to input senses 0 V, and so reports a low signal (0).
The 10 kΩ resistor in this circuit is called a pull-up resistor. It pulls the voltage at P8 up to 3.3 V when the whisker is not pressed. A pull-up or pull-down resistor applies a voltage that is opposite of the voltage the I/O pin will detect when the switch/button contact is made. Without it, the I/O pin set to input will be more like an antenna, allowing nearby electric fields to affect whether it detects 1 or 0, possibly supplying false data to your project.
Right now, the Terminal provides visible feedback when each whisker is pressed. That's not very useful for a robot that you want to disconnect from the computer! Let's use the Propeller Activity Board's built-in P26 and P27 LED circuits as whisker indicator lights.
Now let's try a program that makes the robot back up while you push and hold a whisker up against its breadboard post.
The first two blocks may look familiar now and are going to be at the start of all the navigation programs from here on out. First, the frequency out block makes a 3000 Hz, 1-second beep on the piezospeaker connected to P4. Remember, that's the brownout indicator that lets you know if the program restarts due to low batteries. The second is the Robot initialize block, which must be at the top of the program come before any Robot drive... blocks.
The rest of the code is inside a repeat forever loop. Two variables are declared next, each with a check PIN block attached. The variable left wheel stores the input state of the left whisker circuit connected to P7, and right wheel stores the input state of the right whisker circuit connected to P8. Remember, these whisker circuits provide active low outputs. If a whisker is pressed, the I/O pin connected to that circuit will sense 0 volts so that the check PIN block would provide a 0. If a whisker is NOT pressed, check PIN would provide a 1.
First thing in the loop, an If...do...else block checks to see if either whisker is pressed. The attached if condition blocks could be read as "if left wheel equals zero or right wheel equals zero, do..." The condition is built using a boolean comparison block set to or. Inside that are two compare value blocks, each individually checks whether a variable (left wheel or right wheel) equals zero.
If either whisker is pressed, the if condition is true, and the block enclosed by if...do gets executed. In this case, that's the Robot drive speed, with both wheels turning at -64 ticks per second causing the ActivitBot to back up.
If neither whisker variable stores a 0, the program execution skips the block enclosed by if...do and moves on to the block enclosed by else...if. There, a Robot drive speed left 0 right 0 block makes both wheels stop turning.
Check early, check often! — Notice there is no pause block in the repeat forever loop above which allows the loop to execute very rapidly, monitoring the sensors and therefore updating which drive speed block is in use frequently. This allows the robot to quickly respond to sensor input with the appropriate motion. It's something to keep in mind when navigating with sensors.
...and don't block me! — Any code inside a loop that is blocking - code that does not let any other process proceed until complete - will slow down that loop. Examples of "blocks that block" are pause (ms) and frequency out. But, you can use this to your advantage by placing pause or frequency out right after robot drive speed to control how long the robot performs an evasive maneuver inside of a larger loop.
Here is a modified loop for your main function. It replaces the if...else statements with code that allows you to push one whisker at a time to make it turn away to one side or the other, or both whiskers to make it move straight backward.
Your ActivityBot should turn to the side away from whichever whisker is pressed. If you press both whiskers at once, it should back up.
Right now, if you press both of your robot's whiskers at the same time, it will awkwardly wiggle as it backs up with pivoting turns. Can you fix that?
Now that you've learned how to make the robot react to its whiskers it's time to expand on that. Let's free your ActivityBot to roam around on its own, and navigate away from obstacles using its whiskers.
To roam, the ActivityBot needs to go forward until it bumps into something. When that happens, it needs to back up, turn away from the obstacle, and then go forward again.
While the robot is going forward, it should repeatedly check its whiskers with minimal delays between checks. That way, it can know right away when it has encountered an obstacle.
After the frequency out block that serves as a brownout indicator, and the required Robot initialize block, the rest of the code is inside a single repeat forever loop.
First comes a Robot drive speed block that sets left and right wheels to 64 ticks per second, which is approximately 50% of the ActivityBot's top controlled speed. After that, check PIN blocks store the input states of the whisker circuits in the variables left whisker and right whisker. Immediately after that, the if...do...else...if block evaluates those variables to see if there is a detected obstacle. Since there are no pauses between these blocks, the ActivityBot can respond very quickly to sensor input.
The if...do...else...if block first checks to see if the left whisker equals zero, which indicates the left whisker circuit is closed by hitting an obstacle. If this is true, Robot drive speed -64, -64 makes the ActivityBot drive backward while the pause (ms) 600 block executes. Then the next Robot drive speed block uses left 64 and right -64 to turn the wheels in opposite directions at the same speed. This setting makes the ActivityBot rotate right, away from the side which detected the obstacle. The rotation maneuver lasts as long as it takes for pause (ms) 300 to execute.
But, if left whisker = 0 is false, those maneuvers are skipped and the else...if condition checks to see if right whisker = 0 is true. If it is, then an obstacle on the right has been detected, and the code executes the Robot drive speed and pause (ms) blocks right below, this time backing up and using left -64 and right 64 to rotate left, away from the obstacle on the right.
If neither variable is equal to zero, then no evasive actions inside the if...do...else...if loop is executed. The code will return to the top of the repeat forever loop, where Robot drive speed 64, 64 propels the ActivityBot forward a bit more before checking the whisker states all over again.
Iteration — According to Merriam Webster [15], one definition of iteration is "a procedure in which repetition of a sequence of operations yields results successively closer to a desired result." Iterative testing and tuning is an integral part of developing robotics applications.
The example program uses the default Robot drive speed value of 64 (and also -64). That's at 50% of the speed the ActivityBot can go when using encoders. Since there is no Robot set acceleration used, the default value of 600 ticks per second squared is in use. These moderate default values work together well. They are an excellent choice while developing a basic sensor navigation routine, so the ActivityBot doesn't zip away unexpectedly. Now that your whisker system is working let's speed things up a little at a time and tune the maneuvers along the way.
How far does the ActivityBot back up after detecting an object? And then how many degrees does it rotate?
You have probably noticed that as you increased the ActivityBot's speed, its response seems increasingly sluggish. At top speed, the ActivityBot might even hit the wall before it responds to the whisker switches and backs up. It may have also knocked the 3-pin header out of its sockets a bit, preventing further whisker-switch contact. What do you think is causing that?
The Ping))) Ultrasonic Distance Sensor allows your ActivityBot to detect obstacles and measure the distance to them. Even though the PING))) sensor looks a bit like eyes, it is more like a mouth and an ear.
Much like a bat, the PING))) sensor emits an ultrasonic chirp when triggered, then listens for the chirp's echo and signals its return. The Propeller microcontroller can mark the time between triggering the PING))) sensor and getting a return signal. This echo return time can then be used to calculate the distance to an object.
This ability makes different ActivityBot navigation strategies possible, such as avoiding obstacles while roaming, or maintaining a set distance to an object that is moving.
In this activity, we will measure the distance to a variety of objects to get familiar with what the PING))) does, and does not, detect. The PING))) sensor needs power, ground, and one signal connection for the Propeller to get distance measurements.
PING))) Sensor and Servo Ports—Do not connect to VIN. If you want to connect the PING))) sensor to a servo port (such as P16 or P17) with a 3-pin extension cable, be sure to set the port's power jumper to 5V first, or you could damage your sensor.
Each servo port includes a 3.9 k-ohm resistor in series with its Propeller I/O pin, so you would not need to include the 2.2 k-ohm resistor shown in the schematic for the breadboard circuit.
The PING))) sensor emits a very high-pitched chirp and then listens for an echo, much like a bat. By measuring the echo's return time, and using the speed of sound in the air, the Propeller microcontroller can determine the distance between the PING))) sensor and an object reflecting the chirp sound, up to 3.3 meters away. This is how your ActivityBot can navigate using sound.
Echo based navigation only works with objects that can reflect the sensor's chirps effectively. Small objects, or objects too far away, don't reflect enough sound. Soft items, such as curtains and stuffed animals, may muffle the sound and not bounce back an echo. Walls approached at a glancing angle will reflect the sound further away instead of back at the PING))) sensor. Hard-surfaced cylinders, ball-shaped obstacles, and walls faced directly reflect sound well.
Air temperature can affect the accuracy of the PING))) sensor's distance measurements. Check out our reference article [17] about the speed of sound vs. air temperature to learn more about why this happens.
This program displays the PING))) distance measurements in the BlocklyProp Terminal. You can use it to test how well the PING))) sensor can detect various objects and walls at various angles. This will give you an idea of what your ActivityBot can and cannot do with this sensor.
The PING))) sensor gets a brief high/low signal called a start pulse from the Propeller chip to start the measurement. The sensor then makes its ultrasonic click and sets its SIG pin high. When the echo comes back, it sets its SIG pin low. The Propeller chip then can measure the duration of the PING))) sensor SIG pin's high signal, which corresponds to the echo return time.
The Ping distance block takes care of the math for converting the echo return time measurement to distance. In this program, the Ping distance block is plugged into a Terminal print number block, to display the measured value. The Terminal print number block comes just after a Terminal print text block, to add a label and measurement units for the displayed value. These two blocks are in a repeat forever block, along with a pause (ms) and Terminal clear screen block. The pause (ms) block slows down the repeat loop enough to get a smooth display, and the Terminal clear screen block keeps the value updated.
Sound is measured in hertz (Hz). The PING))) sensor emits chirps at 40 kHz, well above human hearing. Most young humans can hear frequencies between 20 Hz and 20 kHz, while some bats can hear frequencies up to 100 kHz. So, while you cannot audibly hear the PING))) sensor's chirps, a bat most likely could.
Even though the PING))) sensor uses a sound that is a higher frequency than a human can hear, lower frequency sounds can bounce off of objects, too. Watch this video about an incredible person who is blind, but can "see" using echolocation: BBC Earth: Blind man uses echolocation like a dolphin [18]. Although he was able to develop that sense himself, sensors like the PING))) can be used to help others with disabilities. Read this article showing a product that could help others: CNN: sonar sticks [19]. Can you think of other ways sensors can be used to help?
Since there is a piezospeaker on the board, let's use tones instead of the Terminal to express the distance measured by the PING))) sensor. The next program saves the block's value in a variable and then uses math to scale the measured distance to a frequency range played on the piezospeaker.
Take a closer look at the project above. Can you see how the Distance variable controls the Hz variable, with the help of two operators and two constants?
Now that you can measure centimeter distances with the PING))) sensor, and know which kinds of objects it can sense, let’s combine the sensor's input with the servo motor's output.
Our goal is for the ActivityBot to roam and avoid objects using sound with the PING))) sensor. In one way, this is an improvement over whisker-switch navigation, because the ActivityBot does not have to bump into objects to detect them.
But, there is a key difference: there were two whisker switches. With whiskers, the ActivityBot could tell whether an obstacle was on the left or the right, and then turn away in the opposite direction. The PING))) sensor returns a distance value but does not indicate whether the obstacle is more to the left or the right.
Let's start with test code that makes the ActivityBot turn right or left at random when an object comes within 20 cm. Once that is working and tuned, then you'll add blocks for roaming.
This example program makes the ActivityBot stay still until the PING))) measured distance is 20 cm or less. Then, it turns in place until it measures more than 20 cm, which means the robot turned until the object is no longer visible.
Since this code makes the robot drive, it begins with a frequency out block for the brownout indicator piezospeaker on P4. Next comes the required Robot ActivityBot initialize block. The Robot set acceleration block is set to a moderate 600 ticks/s2 to start.
The rest of the code is in a repeat forever loop. It begins with a repeat while loop. The while condition is a compare values block, checking to see if the value provided by the Ping distance block is greater than or equal to 20. If the answer is true, the code executes the pause 5 (ms) block and then checks the PING))) sensor again. This action will keep repeating, effectively putting code execution on standby until the while condition is no longer true.
When that happens, code execution drops down to the next block. The set variable block makes turn equal to a random number from 1 to 2. That's the BlocklyProp way of flipping a coin to decide which way the ActivityBot should turn. The "coin flip" is checked in the if...do...else...if block. If turn = 1 is true, then the block Robot drive speed left 64, right -64 gets executed, and the ActivityBot turns right. If turn = 1 is not true, then the block next to else is executed, and Robot drive speed left -64, right 64 makes the ActivityBot turn left.
The if...then...else...if block is followed by another repeat while loop. This time, the while condition checks if the Ping distance measurement is less than 20. The code execution stays in standby mode as long as this is true. Staying in standby mode allows the ActivityBot to continue its rotation maneuver until the PING))) sensor reports a measurement greater than 20, meaning that it has turned past the obstacle. Then, code execution continues to the Robot drive stop block which makes the ActivityBot quit turning.
Finally, the code execution returns to the top of the repeat forever loop, where it once again will sit still and check the PING))) distance every 5 ms until an object comes closer than 20 cm.
Redundant subsystems - Whiskers switches rely on touch, and the PING))) sensor relies on ultrasound. As you may have noticed, each one has advantages and disadvantages. In real-world control systems, different types of sensors that depend on various physical properties are often used together, which can help make the system more reliable under a wider variety of conditions. Where risk is low, redundant sub-systems can seem silly, like wearing a belt with a pair of suspenders. But where risk is high, redundant subsystems are critically important. Can you think of any examples?
By adding just two blocks, the example code above can free the ActivityBot to roam. Right now, the ActivityBot stays still unless it detects an object within 20 cm. All that is needed is a block that allows it to drive forward unless an object is detected within 20 cm. If an object is detected that close, it must stop driving forward before performing the turn-away maneuver.
Instead of finding and avoiding obstacles, this next project makes the ActivityBot follow a target. The same sensor system, using different code. The code checks for the desired distance, and if the object is further away than that, the ActivityBot moves forward. If the object is closer than the desired distance, the AcitivityBot moves backward. If the object’s distance is equal to the desired distance, the ActivityBot will maintain its position.
This example makes the ActivityBot try to keep a 32 cm distance between itself and an object in front of it. With no object detected, the robot will go forward until it finds one, then maintain that 32 cm distance. If you pull the object away from the ActivityBot, it will go forward again to catch up. Likewise, if you push the object toward it, it will back up to restore that 32 cm distance.
The program declares five variables: distance, setPoint, errorVal, kp, and speed.
The program starts off with the familiar comment block for the project title and frequency out block for the brownout indicator piezospeaker on P4.
After the required Robot ActivityBot initialize, two more blocks contribute to the robot's behavior. Robot set acceleration is set to 400 ticks/s2 which will help cushion sudden changes in speed. Robot drive max speed 96 makes sure that the Robot drive speed block caps its left and right inputs at +/-96, even if the attached speed variable exceeds 96 during runtime.
Two set variable blocks with number values attached come next: setPoint = 32 and kp = -10. These values do not change during runtime.
The rest of the code is inside a repeat forever block. This routine makes the ActivityBot maintain a distance between itself and the target object by repeating four steps very rapidly:
An automated system that maintains a desired physical output level is called a control system. In the case of our ActivityBot, maintained level is the distance between itself and the object in front of it. Many control systems use a feedback loop to maintain the level, and our ActivityBot is a prime example of this. The measured distance is fed back into the system and compared against the set point for calculating the next speed, and this happens over and over again in a process called a control loop.
Engineers use drawings like this block diagram to think through a control loop’s design and also to convey how it works to technicians and other engineers. If the set point (distance the ActivityBot tries to maintain) is 32 and the measured distance is 38, there is an error value. This error value is the set point – measured distance. That’s 32 – 38 = -6. This error is multiplied by a proportionality constant (kp) for an output that’s the speed to correct the error this time through the loop. That’s speed = kp x error value, or 60 = -10 x -6.
The circle is called a summing junction (for addition or subtraction), and the rectangular blocks designate operations that can vary. In this example, the top one is a proportional control block that multiplies error by a proportionality constant. The System block represents things we do not necessarily have control over, like if you move the object toward or away from the ActivityBot. Other common control blocks that were not used in this example are Integral, which allows corrections to build up over loop repetitions, and Derivative, which responds to sudden system changes.
It’s instructive to watch the relationship between errorVal and speed as the value of distance changes. We can use the Terminal to do this.
This modified project is for looking at data, not for navigation! Go back to Push Ping, or comment out the Terminal and pause blocks, if you want to return to the navigation activity.
There are lots of values to experiment with here. For example, if you change the setPoint variable from 32 to 20, the ActivityBot will try to maintain a distance of 20 cm between itself and an object in front of it. The value of kp can be increased to make it go faster to correct with smaller error distances, or slower to correct over larger error distances.
If you decrease the Robot set acceleration value, it will cushion the changes but could cause it to respond so slowly that it runs into the object. If you increase it, it will respond more abruptly. Too large, and it could change direction so fast that it could eject the Ping))) sensor from the breadboard.
Would increasing the Robot drive max speed help the ActivityBot keep up with a retreating object? Could decreasing Robot drive max speed too much hamper its response?
Light sensors are used in all kinds of robotics, industrial, and commercial applications. Next, let's teach your ActivityBot to navigate by visible light. Using a pair of small light sensors, called phototransistors, your ActivityBot can measure and compare the light levels on its right and its left. It can then turn toward the brighter side as it navigates. This way, the ActivityBot can be programmed to follow a flashlight, or find its way out of a dark room to a bright doorway.
Phototransistors are a common type of analog light sensor – the kind that might help a porch light automatically turn on at night. Think of the phototransistor as a light-controlled current valve. When light strikes the base of the device (B), more current will conduct into its collector lead (C) and flow out of its emitter lead (E).
Now, it's time to build and test two phototransistor circuits to give your ActivityBot phototransistor "eyes." Leave the piezo speaker circuit in place.
Build the light sensor circuits shown below.
This test will display raw light sensor readings in the Terminal window. You will need a flashlight or lamp that is brighter than the overall light level in the room.
The phototransistor circuits are designed to work well indoors, with fluorescent or incandescent lighting. Make sure to avoid direct sunlight and direct halogen lights; they flood the phototransistors with too much infrared light.
The values shown above were taken in a room with overhead fluorescent lighting. Notice how covering the left phototransistor causes the lightLeft measurement to be larger.
Mismatched Measurements
It is unlikely that your two phototransistor measurements will match exactly. Each sensor will vary slightly as a natural part of the manufacturing process, and the brightness levels are rarely precisely even in any environment. However, if one gives a measurement about 100 times larger than the other when exposed to the same level of light, that phototransistor is probably plugged in backward. In this case, check your circuit and try again.
Take a peek back at the circuit schematic, for P9. Think of the 0.01 µF capacitor in the circuit as a tiny battery, and the phototransistor as a light-controlled current valve.
The first block in the repeat forever loop is make PIN 9 high. This block supplies current to the capacitor battery, which only takes a pause 1 (ms) to charge. Immediately after that, the block RC discharge PIN 9 changes I/O pin P9 from output-high to input. As an input, P9 will switch from sensing a high signal to a low signal as the capacitor battery drains its current through the phototransistor valve. The Propeller is measuring how long it takes for this logic transition to happen, and the RC block stores this discharge time in the lightLeft variable block.
The next three blocks in the repeat forever loop go through the same process with the P5 phototransistor circuit and storing another discharge time measurement in the lightRight variable.
When bright light is shining on a phototransistor, its valve is wide open and current drains through it very quickly, so the measured discharge time is small. When the phototransistor is covered up, its valve opens only a little, and the current will drain more slowly, resulting in a higher discharge time value. The remaining blocks in the loop display the discharge time of both variables in the Terminal. The Terminal screen capture above was taken when the left phototransistor was covered up, so the value of lightLeft is larger than the value of lightRight.
QT is short for Charge Transfer: In this activity, each photoresistor is being used in a charge transfer or QT circuit. This circuit lets the robot sense a much wider range of light levels than an analog-to-digital conversion circuit you might typically see with this sensor. This QT circuit is a simple option for using a variety of analog sensors with a digital I/O pin.
Visible and Invisible Light: Light travels in waves so small that the distance between adjacent peaks is measured in nanometers (nm), which are billionths of meters. The figure below shows the wavelengths for colors of light we are familiar with, along with some the human eye cannot detect, such as ultraviolet and infrared. The phototransistor in your ActivityBot kit detects visible light but is most sensitive to 850 nm wavelengths, which is in the infrared range.
Just for fun, let's see if the phototransistor can detect different colors.
From your observation, do you think a clear phototransistor alone, like this one, could be used as a color sensor? Why or why not?
For ActivityBot navigation, the raw sensor values aren't directly useful. What matters is the difference between the light levels detected by each sensor so that the ActivityBot can turn to the side of the one sensing brighter light. It is also nice to have values that will fall into a predictable range that can add or subtract them from the speeds sent to drive the servo motors.
Accomplishing this takes just a few steps - after building the equation you need, you'll then figure out how best to use blocks that follow this equation.
The concept is simple - of all of the light detected by both sensors, which portion of it is coming from the right sensor (and if you need to, you can tell which portion is coming from the right sensor). First, divide one sensor measurement into the sum of both. The result will always be in the 0 to 1 range. This technique is an example of a normalized differential measurement. Here’s what it looks like as an equation:
A range from 0 to 1 still isn't useful - especially in Blockly, since we can't use decimal or fractional numbers. The goal is to have a range from -100 to +100 (a range with 200 possible values). Multiplying the normalized differential measurement by 200 will give a result between 0 and 200. Subtracting 100 from that result will provide a range from -100 to +100. If the phototransistors sense the same level of light, regardless of how bright or dim, the final whole number will be zero. This is an example of a zero-justified normalized differential measurement, let's call it "ndiff" for short. Now our equation looks like this:
We have to be careful in BlocklyProp. Because all math is done with integers, it is possible to create an equation that always results in zero.
Preserving values in integer math
A good habit is to make your value bigger before you make it smaller. If you can use decimals or fractional values, it doesn't matter if you divide then multiply or multiply then divide. When doing integer math, it DOES matter. If we do the multiplication first, we can preserve the value of our result:
To turn this into blocks, you'll create a few different quantities. The top part of the fraction (the numerator) becomes:
The bottom part of the fraction (the denominator) becomes:
Which means nDiff is:
The example code below will display the lightRight and lightLeft measurements, and also show the value of nDiff as an asterisk. This makes it easier to visualize how the ActivityBot navigation program uses the sensor values to aim for a new trajectory towards the brighter light.
When the Terminal opens, you should see something like this:
The graphical display was generated using Terminal print number and character value blocks.
This program builds on Test Light Sensors from the previous page. Two more variables are declared in addition to lightLeft and lightRight: nDiff and position. nDiff holds the result of the fancy-named equation, and position is used to print a graphical display that represents the value of nDiff.
After calculating nDiff, its result needs to be turned into a position in the terminal. nDiff can be any number from -100 to +100, and we need positions from 0 to 50 (that's about how many characters wide the Terminal window is).
To get from (-100 to +100) to (0 to 50), we first add 100 (bringing it to 0 to 200), then divide by 4 (bringing it to 0 to 50).
Then, using a repeat x times block, we use the Terminal print number and character value blocks to print enough spaces to push out the asterisk (*) we are planning to print it where it needs to be on the Terminal screen.
Integer math, nesting, and order of operations all matter. Just like with normal mathematical operations in real life, multiplication and division operations are performed before addition and subtraction. However, you may have already learned that Blockly evaluates each block from "inside-out," and you have to plan accordingly to get the correct result.
Dealing with integer math can be tricky, too. Most modern microcontrollers can do floating-point math, including the Propeller Multicore microcontroller. BlocklyProp doesn't support it, however, because the blocks themselves are not able to tell what kind of number - integer or floating-point - your variable might be. In addition, floating-point operations are slower and take up more memory than integer operations.
Once you know a few tricks, it's easy to overcome this limitation.
There may be times where you want to turn the value of a sensor into a percentage. To change a sensor's value to a percentage, we have to know what it's minimum and maximum values are. The difference in those values is the sensor's range. Then, you can use the following formula to convert the sensor's value to a percentage:
In real life, because multiplication and division have the same operator precedence, either of these calculation steps will get you the same result:
To see what difference your order of operations can have on the result in integer math, try the following example:
Here's why this happens:
What percentage of 200 is 150? To find out, you might take 150 divided by 200 and get 0.75, then multiply that by 100 to get 75.
But what is 0.75 as an integer? Here's a hint: it's not 1. In integer math, the decimal is truncated. This means that the number is cut off at the decimal, leaving only 0. If you're trying to convert to a percentage by multiplying by 100, 100 times 0 is still 0.
If we use the same numbers, but this time multiply first by 100 before dividing by 200, we get 150 times 100 = 15000, then 15000 divided by 200 = 75.
Now, let's make the ActivityBot follow light! The following code should allow your robot to follow a flashlight beam, or navigate out of a dark room through a brightly lit doorway.
This program uses the Robot blocks for the ActivityBot. After initializing the ActivityBot, the program consists of a repeat forever loop which receives values from the phototransistors and uses them to adjust the servos' speeds to navigate toward whichever side detects brighter light.
First, the now familiar variables lightLeft, lightRight, and nDiff are setup for taking and using the sensor measurements. Then, the variables speedLeft and speedRight are set up for use with the Robot drive speed block later on.
The first two parts inside of the repeat forever loop are the same as our previous test program. They take the light sensor measurements and store the values in lightLeft and lightRight.
The next line is the same nDiff equation used in Test Light Sensor Graphical project. It calculates nDiff, which will have a value in the range of -100 and 100. Positive nDiff values mean the left photoresistor is sensing brighter light, and negative nDiff values mean the right photoresistor is sensing brighter light. The farther the value is from zero, the greater the difference in the amount of light that the two phototransistors are sensing.
The variables speedLeft and speedRight are initialized to 64. Keep this in mind when looking at the if...else block that comes next.
The first condition translates to "if nDiff is greater than or equal to zero (brighter light on the the left), make speedLeft equal to 64 minus nDiff × 4." The new value for speedLeft would be something less than 64, while speedRight remains 64. These variables are then used in the Robot drive speed block after the if...else block. For example, if nDiff = 32, this statement is the same as speedLeft = 64 - (32 × 4), which equals -64. The result is Robot drive speed (left = -64, right = 64) which makes the ActivityBot rotate to the left toward the light.
However, if nDiff is NOT positive, the code drops to the else part of the if...else block, making speedRight equal to (nDiff × 4). This translates to "make speedRight equal to 64 plus nDiff × 4." This is still a number smaller than 64; remember that nDiff is negative here. For example, if nDiff = -16, this statement is the same as speedRight equals 64 + (-16 × 4) so speedRight ends up equal to zero, while speedLeft is still 64. This gives the Robot drive speed (left = 64, right = 0), causing the ActivityBot to pivot right towards the brighter light.
Phototaxis — This is the term that describes an organism moving its whole body in direct response to light stimulus. Moving towards brighter light is called positive phototaxis, and moving away from brighter light would then be negative phototaxis. While the Navigate by Light program makes the ActivityBot mimic positive phototaxis, in nature, it seems this behavior is usually found in microscopic organisms. Moths are attracted to light, but their varied and circling flight paths around a streetlamp demonstrate that their navigation systems are far more complex.
Would you like to make your ActivityBot mimic negative phototaxis? It can be done by replacing one single variable in the Navigate by Light project.
What other behaviors can you give your ActivityBot with light sensors?
Many TVs and other equipment are controlled by infrared remotes. When you point the remote at the TV and press the channel-up button, it changes the channel for you. Pressing the button makes the remote flash a pattern of infrared light at the TV. Infrared light is not visible to humans, but the TV’s infrared receiver detects the flashing light pattern. The TV’s microcontroller decodes this pattern and uses that information to change the channel for you.
The infrared light (IR LED) and receiver work great as "flashlights" and "eyes" for your ActivityBot. The IR LEDs shine forward, and the IR receivers detect reflections off of nearby obstacles.
The Propeller microcontroller receives reflection data from the IR receivers and uses it to make navigation decisions. In this way, your ActivityBot can roam around obstacles without first bumping into them.
In this activity, you will build and test the infrared object sensor system to make sure they detect objects in front of the ActivityBot. Just like the whisker circuits, there is one on the right and one on the left so the ActivityBot can determine which way to turn in relation to the obstacle.
In this case, each side needs two electronic components: an LED that rapidly flashes infrared light, and an infrared receiver to detect a reflection of that infrared light bouncing off of objects nearby.
(2) IR LEDs
(2) IR receivers
(2) 1 k-ohm resistors (brown-black-red)
(2) 220-ohm resistors (red-red-brown)
The infrared LEDs are designed to sit in the standoff tubes a certain way. The flat spot on the LED dome matches a flat ledge inside the tube. Holes on the bottom of the tube are labeled for the anode (+) and cathode (-) leads.
The IR LED’s cathodes are connected to the D/A0 and D/A1 sockets on the Activity Board WX. These two special sockets can output a variable voltage. This voltage can be increased to make the IR LEDs dimmer, for closer range detection. (They can even be tested at different voltages—levels of dimness—to get a rough idea of the object’s distance, but that's for another activity...)
This test code displays the state of each IR detector: 1 = no infrared reflection detected, 0 = yes, infrared reflected from an object is detected.
It is essential to make sure that both IR detectors work correctly before continuing.
What the IR Sensors Can't See
Remember, the IR sensor system is looking for reflected infrared light. Shiny, light-colored objects reflect infrared light well, while dull, dark-colored objects absorb infrared light instead of reflecting it. So, if your IR sensors can't see your black shoes or a black plastic wastebasket, don't worry, that is normal.
First, the program makes I/O pins P26 and P27 output low (0 V). Not only does this turn off the P26 and P27 LEDs, but it also connects the D/A0 and D/A1 sockets to 0 V. The IR LED cathodes are connected to these sockets, using them for a ground connection. (We will explain why in a later activity.)
The rest of the code is inside a repeat forever loop. First, a frequency out block generates a 38000 Hz signal on P11, for 1 millisecond. P11 connects to an IR LED, causing it to strobe on/off very rapidly in a 1 ms burst.
This block is followed immediately by a check PIN block, which stores the input state of P10 in a variable named irLeft. P10 connects to the left IR receiver circuit. These IR receivers detect not just the presence of infrared light but are most sensitive to IR light that is strobing on/off very rapidly, at 38 kHz (38000 Hz). The IR detector sends a low signal (0 V) when a reflected 38 kHz IR signal is detected, and a high signal when no such signal is detected.
The process is duplicated for the right-side IR circuitry with another frequency PIN + check PIN block pair, storing the IR receiver's output in the irRight variable.
The next six lines should look familiar by now. The four Terminal print blocks display labels and values for the irLeft and irRight variables. A pause (ms) 150 block slows down the loop for optimal display. Finally, a Terminal clear screen block erases the old data before the next trip through the loop.
Infrared — It means “below red,” and it refers to the fact that the light’s frequency is below that of red on the color spectrum.
Filtering — The IR receiver has a filter built in that makes it look for infrared that flashes on/off 38000 times per second (38 kHz). This allows it to differentiate between infrared coming from the TV remote and other IR sources such as halogen and incandescent lamps as well as sunlight streaming in through a nearby window.
Sunlight — Even though the IR receiver filters for sunlight, direct sunlight can often swamp the IR LED’s signal. Try to keep it indoors and out of any sunlight streaming in through windows.
The P26 and P27 lines are currently in use providing 0 V to the IR LED cathodes, so you cannot use their LEDs for indicating the IR detector states like you did with whiskers. So, let’s build some LEDs on the breadboard for "object detected" indicators.
Next, you will need to modify the code to turn a given light on if its IR receiver returns 0, or off if it returns 1.
If you have an infrared remote, you can further modify your IR detection code so that it sounds an alarm if it detects IR interference from the environment. This is a useful test to find out if nearby fluorescent lights or other devices are sending out signals that could affect your robot's behavior. The key is not to emit any infrared with the IR LEDs. If the receivers still send a low signal, there must be IR interference coming from another source.
If interference is detected, make the piezospeaker get your attention with a series of six 50 ms, 4 kHz chirps separated by six 50 ms pauses. You can use a TV remote to test this.
Now you can disconnect the robot from the computer and walk around your workspace. Try pointing your robot at different light sources. If you hear the beeping alarm, you know that a 38 kHz infrared signal is detected. The ballast on some styles of fluorescent tube lighting is notorious for causing this kind of IR interference.
With your IR object sensors built and tested, you are ready to make your ActivityBot roam and avoid obstacles without bumping into them.
The IR Roaming code example makes the ActivityBot go forward until it detects an obstacle. If it sees an obstacle on the left, it will turn right. Likewise, if it sees one on the right, it will turn left. And if it sees obstacles on both left and right, it will back up.
Remember, not all obstacles are visible to this infrared system.
Many dark colored objects will absorb infrared light instead of reflecting it. If in doubt, use your LED indicator program from the previous page. The red LEDs on the breadboard will show you if an object is visible to the IR object detectors.
Your ActivityBot should promptly turn away from your hand and roam in a different direction until it detects another obstacle.
The project begins with the now familiar frequency out block for the piezospeaker brownout alarm on P4. Next comes the required Robot ActivityBot initialize block, and a Robot set acceleration block. As we learned from previous projects, 600 ticks/s2 is a moderate acceleration value that works well with the Robot drive speed block's default value 64.
Remember that the make PIN...low blocks for P26 and P27 are needed to connect the IR LED cathodes to ground.
The rest of the code blocks are in a repeat forever loop. The next four blocks are the same as in the IR Detectors project. For each IR LED/Receiver pair, a frequency out block sends a 1 ms pulse of infrared light at 38 kHz; then a check PIN block immediately stores the IR receiver's response in a variable. The variables irLeft and irRight will each store a zero if that receiver detects an infrared reflection off of an object, and 1 if no object is detected. (Remember, the IR receivers are set up as active-low circuits.)
A single if...do block with four conditions and potential actions makes up the rest of the repeat forever loop. Those conditions check the combined state of the irLeft and irRight variables. The possible conditions are:
The conditions are tested one at a time in order. The first condition that is true runs the code in its ...do section, and then the program flow returns to the top of the repeat forever loop.
The first if... condition uses a boolean comparison block to test whether it is true that both irLeft and irRight variables are storing a 1. That means neither IR receiver detected an object, and the ActivityBot can drive forward with Robot drive speed left 64, right 64.
The second if... condition also uses a boolean comparison block to test whether it is true that both irLeft and irRight variables are storing a zero. That means both IR receivers detected an object, and the ActivityBot can back up with Robot drive speed left -64, right -64.
If the first two conditions get skipped, it must mean that the two variables are not storing the same value. So, the next two conditions test each variable alone to see if either one detects an object.
The third if... condition checks whether irLeft is storing a zero. That means an object is detected on the left, and the ActivityBot can turn right to avoid it with Robot drive speed left 64, right -64.
The fourth if... condition checks whether irRight storing a zero. That means the an object was detected on the right, and the ActivityBot can turn left to avoid it with Robot drive speed left -64, right 64.
Conserving Conditions — Since this project has only four possible conditions to check for, your if....do loop did not really need to test for the fourth. Logic tells us, if the first three conditions are false, the fourth condition must be true. So the last else...if in the if...do loop could have just been an else containing the Robot drive speed left -64, right 64 block.
Compact vs. Easy to Read — Using all four conditions is an example of building code so that it is easy to understand, rather than as short as it can possibly be. When you are first developing a project, it can be helpful to make the code as easy to understand as possible. Then save a copy, and work on making it more compact by eliminating blocks that are not necessary. However, if you will be sharing your code, or if it is a very complex project, it may be wiser to keep it readable.
Adding visible light feedback for the IR recivers can help you understand what is happening if your robot seems to behave in unexpected ways.
If your sensor input, light and motion output system logic is now working well, it's time to tune it for performance. Your goal is to get your ActivityBot navigating as rapidly as it can without running into objects or doing nose-wheelies.
Links
[1] http://www.parallax.com/product/32600
[2] https://solo.parallax.com/
[3] https://www.parallax.com/downloads/blocklyprop-solo-launcher-and-faq-schools
[4] https://learn.parallax.com/node/1607
[5] https://learn.parallax.com/tutorials/language/blocklyprop/getting-started-blocklyprop-solo
[6] http://learn.parallax.com/tutorials/language/blocklyprop/simple-blocklyprop-programs-abwx
[7] http://learn.parallax.com/tutorials/language/blocklyprop/blocklyprop-functions-and-multicore
[8] https://learn.parallax.com/node/1965
[9] http://www.parallax.com
[10] https://www.parallax.com/sales/contact-sales/
[11] https://learn.parallax.com/tutorials/language/blocklyprop/simple-blocklyprop-programs-abwx
[12] https://learn.parallax.com/tutorials/language/blocklyprop/blocklyprop-functions-and-multicore
[13] https://www.parallax.com/downloads/propeller-activity-board-wx-product-guide
[14] https://www.parallax.com/downloads/parallax-feedback-360%C2%B0-high-speed-servo-product-guide
[15] http://(https://www.merriam-webster.com/dictionary/iteration)
[16] https://www.parallax.com/product/28015
[17] https://learn.parallax.com/reference/speed-sound-air-vs-temperature
[18] https://www.youtube.com/watch?v=2IKT2akh0Ng
[19] http://www.cnn.com/2014/06/20/tech/innovation/sonar-sticks-use-ultrasound-blind/index.html
[20] https://www.parallax.com/product/350-00029