fbpx

Line-following robot – part 2

Hmmkay – time to make our ‘bot move!

But first, a short detour:

Whilst our ‘bot definitely was skipping leg day at the gym, its an absolute beast in the brains department – so why not put those brains to use in this project?! So in this build I’m going to include some optional features that you can add to your project to give insights into what the bot is doing and so boost our learning. These features would be hard or complicated to implement on other bots, but are pretty easy to do on our bad bay.

Robotics is an interesting field because it fuses together quite a few other disciplines like electronics, mechanics and software engineering. So in these optional features I will also put some more detailed info than is necessary for the project, along with some links to further learning, in case there are topics that spark your interest and you’d like to learn more. I might move these optional topics into a separate page in future to make this section seem less ‘hectic’ – we’ll see how it goes ….

Lets jump in.

Optional feature the first: battery monitoring

Motors consume a lot of electrical energy – and when all that energy is coming from the little built-in battery on the Cube, what you will find is that your ‘bot runs for only a few minutes before it runs out of juice and stops. This can be annoying when you want to demo your project to someone else, but you’re unknowingly starting with an almost dead battery from previous runs of the bot. So lets see how to show the battery status on that gorgeous big screen of the Cube!

So firstly, some (simplified) theory about batteries (more over here):

Batteries are containers inside of which there are different types of chemicals interacting with each other to generate electrical energy. Different mixes of chemical elements give us batteries with very different characteristics. For example the single-use AA or AAA batteries that we use in TV remotes, wireless keyboards, etc sometimes use Zinc and Carbon as their chemical elements, whose interaction produces an electrical Voltage (Voltage is like the ‘pushing’ force that drives electricity around a circuit) of 1.5V and is generally not rechargeable. As an interesting aside, one reason Duracell can claim to have batteries that last up to 6X longer than other batteries, is that they use Alkaline chemistry instead of Zinc Carbon – and not many manufacturers use the old Zinc Carbon chemistry anymore, so its not exactly an apples-to-apples comparison! More on this topic over here. Anyway …

The battery inside our Cube is what’s called a Lithium-ion Polymer (or LiPo for short) battery, that uses the highly reactive metal Lithium inside a polymer gel to produce a voltage of roughly 3.7V. LiPo batteries have a much higher energy density than Zinc Carbon batteries (meaning they can store much more energy for the same sized volume), and they are rechargeable. This means you can pack a lot of re-useable energy storage inside a far smaller space which has made them super popular for portable electronics like smartphones, radiocontrol devices, and of course, our Cube!

Lithium is a highly reactive metal, so if the battery gets overcharged or short-circuited, the Lithium will burn in quite the intense fire and is why there are restrictions on transporting lithium batteries in airplanes! This is also why almost all Lithium batteries have a protection circuit (you can see it under the yellow tape in the picture) to prevent over-charging and short-circuiting.

An interesting thing about batteries is how their voltage changes based on their charge state. When we previously spoke of AA and AAA batteries being 1.5 Volts – they actually start life at over 1.6 Volts when freshly charge, and reduce their voltage as they discharge in a non-linear way (non-linear means that the graph of voltage vs charge level is not a straight line showing a constant change with charge level, but is far more curvy).

Below is a graph from Adafruit showing how the Voltage of a LiPo battery – like the one inside our Cube – varies according to charge level (they show Discharge Capacity on the x Axis – 0% Discharged means 100% charged, right?), as well as how ‘hard’ the battery is being loaded according to the C rating. I won’t go into C ratings (you can read a bit more over here), but our motors will drive our little battery quite hard, so we’ll work with the green curve.

When fully charged, our Battery will measure somewhere in the region of 4.2V. As we start using the battery (but simply having the Cube powered on), the battery Voltage of our Cube will drop very quickly down to about 3.8V, and then gradually decrease until about 3.5V once we’ve used 60% of the capacity, at which point it kind of falls off a cliff and the Voltage starts dropping at an accelerating rate. The so-what of all of that, is that once the Voltage of the battery has dropped below 3.5V, there’s only 40% total charge left and substantially less ‘useable charge’ left due to the chemistry of batteries and how you should try not drain a Lithium battery to 0% as it reduces its life. So you can actually measure the voltage in software, and using this graph show a battery capacitor indicator on your Cube’s screen! This in fact, is how many early cellphones worked – they measure battery voltage and convert it to a percentage capacity – although nowadays the charging circuits have chips with ‘coulomb counters’ that can ‘count’ the energy as the battery charges, and measure the energy being discharged to give a far more accurate idea of real battery capacity than the Voltage curves that tend to vary with temperature, load and battery age.

A final quick word on battery capacity. The battery in our Cube has a rated capacity of 500mAh. What does this mean? Firstly, if we expand out the units, mAh stands for milli-Ampere hours, and what this is saying is that the battery stores enough energy to supply a current of 500 milli-Amperes (ie: half an Ampere) for one hour. This also means that if you only draw 100 milliAmperes of current in your circuit (ie: one fifth of the 500), then you should be able to get about 5 times the duration, so roughly 5 hours. Equivalently if you draw one Ampere of current (1000 milliAmps) then you can only do so for half the time, or roughly 30 minutes.

Enough theory! Lets build already!

UIFlow provides some handy-dandy blocks for measuring the Voltage of the battery, as well as how much current the battery is coming from or going into the battery. The current block gives negatives numbers when the battery is discharging – ie: the battery is delivering energy to the circuit to power it, and positive numbers while the Cube is plugged in to a power-source and the battery is being charged up.

Because our Cube is actually running Micropython under the hood, we have access to some of the convenient and powerful features that Micropython offers such as Timers; which UIFlow handily exposes to us as Timer blocks.

Timers are a super useful way of running a block of code at some point in the future (a one-shot timer), or repeatedly at a defined interval (a periodic timer).

We are going to create a Timer than runs every second, and which shows us the Battery Voltage and Current on our Cube’s screen so we can keep an eye on the battery! Super cool, super useful, and super easy to do on the Cube!

Drag two labels onto the Cube’s screen where we can display the battery Voltage and Current status. I called mine lblVoltage and lblCurrent.

From the Event menu, first drag in a timer_callback block, and inside this block drag in the blocks from the UI->Label menu that will update lblVoltage and lblCurrent with the real-time battery voltage and current as shown.

You’ll notice that the timer_callback block doesn’t have a little dent on its top border, meaning it can’t clip into any other block. Instead this block ‘floats’ on the side, and is called everytime timer1 gets triggered. You start this triggering process by dragging in the ‘Start timer1’ block from Events, setting the type to PERIODIC (meaning the timer will trigger repeatedly, not only the once like a ONE-SHOT timer does), with a period of 1000 milliseconds, or 1 second.

What all of this has done is given a neat and convenient way of updating the display with your battery status!

Of course, you could update the Voltage and Current display from inside the main loop of your code, but I think this is a cleaner way of doing it as it will leave your main loop uncluttered with extra blocks that may cause confusion when debugging.

A reminder that this entire section is totally optional for you to implement, but hopefully you found it an interesting detour? Check out some of the sites that I linked in to continue exploring the fascinating world of battery technology that is at the heart of so many real-world products.

Moving Motors

We saw from Part 1, that our wheels are being driven by servo motors connected to Ports B and C of our Cube. If you look closely at the servo motor cable where it enters the servo motor casing, there are three wires coloured red, brown, and yellow. The red and brown wires are how power is supplied to the motor from the Cube (not, as you might assume, directly from the battery, but actually from a power-management chip that converts the battery voltage to 5 Volts. This power-chip gets its energy from the battery though) – the red wire carries the positive voltage and the brown wire the negative or ground voltage. Inside the motor is a tiny little circuit board with some electronics, and its these components along with the motor itself that get powered by these two voltage wires. That leaves the yellow wire – what is that for?

The Cube needs to signal to the motor in which direction it must turn and how fast, right? The Cube does so by signalling over the yellow wire. Exactly how the Cube does this signalling is an interesting topic – the electronics and hardware side of the topic is entirely optional (but fascinating! you can learn more than you ever thought you’d need to know about servo motors over here), but you need a rough idea of what’s going on when you program the motor in software due to the rather low-level way that UIFlow makes these blocks available.

Lets take a brief look …

Imagine you’re the Cube and you have this yellow wire that you can use to send signals to the motor with. One way to think of this in the more physical world is to imagine that you are the Cube, a buddy of yours is the motor controller, you’re standing 100 metres apart and between you is stretched a tight rope (the yellow wire) as shown in the classy sketch below. All you can do is lift the rope up and down to try signal to your buddy what to do as the motor controller (the equivalent of raising or lowering the Voltage on the yellow wire). How would you signal speed and direction to your buddy/the motor controller?

One way you might think of is to adjust the height of the rope (or the ‘level’ of the voltage on the yellow wire), like, say that holding the rope all the way up should be interpreted as full speed clockwise, all the way down is full speed anti-clockwise, in the middle is stop, and values in-between are different speeds in each direction. For the Cube and its yellow wire, this could translate into signalling, say, 5 Volts for max clockwise, 0 Volts for max counter-clockwise, and 2.5Volts for stop with varying levels in-beween. This is a reasonable first stab, but if you were to build this in the Cube you’d encounter some real world problems like the length of the motor cable influencing the Voltage the motor received (the cable slightly resists the current flowing through it and reduces the Voltage a little), plus if the cable gets unplugged by accident the motor controller would see a 0 Voltage level and go full-speed anti-clockwise which is not what you want and possibly even dangerous!

This problem of having one device communicate with another device is a surprisingly deep field and one into which an enormous amount of research has gone, and which is evolving and improving rapidly – particularly with wireless communication such as new WiFi standards and mobile telephones. Most introductory material to the topic is usually University level and will often go by the name of ‘Digital Communication Fundamentals’ or similar names – a simplified YouTube video introduction to the field is over here.

But we’re not trying to send boatloads of data from the Cube to the motor controller – which is what a lot of the fancy communications protocols would let you do – we just want to send two pieces of information: desired speed and direction, right?

So the way this is commonly done on simple devices such as Servo Motors is a simple method with a fancy name: Pulse Width Modulation (PWM). If we look at the diagram from ServoCity below, a pulse is periodically put out on the wire, and the length of the pulse communicates the desired speed and direction to the motor controller. In the rope example, you might agree with your buddy that every 10 seconds you will lift the rope high to indicate you’re about to signal them something, and you’ll only hold the rope up for one second for full counter-clockwise, up for 5 seconds would be full clockwise and up for 3 seconds would be full stop. You would hold the rope down the rest of the time, so that your buddy would know how to recognise a new pulse.

So it is with pulse width modulation. The sender – the Cube in our case – puts out a maximum positive voltage on the yellow wire (usually 5V or 3.3V depending) every 20 milliseconds (ie: 50 times a second). The voltage would be kept high for 1.5 milliseconds (ie: the ‘width’ of the pulse when viewed over time per the picture below would be 1.5 milliseconds wide) when the servo must be in the zero position, with a shorter pulse of 1 millisecond being full counter-clockwise and a pulse of 2ms being full clockwise, with values in-between being different speeds. If you look carefully at the diagram below, what is indicated is angles of 0, 90 and 180 degrees – 180 degree servos and 360 degrees are signalled the same way using PWM, where in the case of a 180 servo we are setting the angle that the servo must move to, and in the case of a 360 servo we are setting the direction and the speed.

So lets program our servo in UIFlow code

You add servo motors to your project the same way as you did in the Dino project: Under the Units screen element, click the big + plus sign, select the servo motor Unit, and importantly: choose Port B or Port C to indicate which port this servo is connected to. You will do this twice so that you project has two servo motors called servo0 and servo1 (or something similar if you add/remove servos a few times) that you can program separately as shown in the screenshot below.

Adding Servo Units to your program gives you the Servo blocks under the Units menu as shown below. Its a little unfortunate that UIFlow gives a nice block for 180 degree servos to rotate the servo to a given angle, but doesn’t have an equivalently convenient block for the 360 servo. Instead UIFlow lets you set the width of the pulse in the Pulse Width Modulation wave – in microseconds (millionths of a second!!). So if you want to spin the servo fully in the one direction, that pulse width needs to be 2 milliseconds, and 2 milliseconds is 2000 microseconds (there are 1000 microseconds in 1 millisecond), so you’d drag in the servo write block and enter 2000 as the value (note the funny looking u is the Greek letter Mu which is often used to represent micro or millionths of a unit).

To rotate the servo the other direction, set a value of 1000 for a pulse width of 1 millisecond.

To stop the servo, put a value of 1500 or 1.5 milliseconds.

Now I don’t know about you, but I don’t find it intuitive to use 1000 to 2000 and the numbers in-between as values for forwards and backwards! So lets create a little move_motor function that will make life a little bit more convenient for us in controlling the servos.

What is more intuitive to me is to thinking of setting the motor speed as a percentage, where, say 100% means fully forward, -100% means fully backward, and 0% means stop, with numbers in between being different speeds in-between. So if we create a function that accepts a speed value as a percentage, the challenge becomes converting this percentage value into a number from 1000 to 2000 that the servo write block wants.

So maybe stop reading at this point, and see if you can work out on a piece of paper, the little bit of maths we need to convert a speed value ranging from -100 to 100 (the percentage we want to use) into a pulse width of 1000 to 2000 microseconds.

Go on, I’ll wait ….

*checks watch*

You done? Good!

So there’s more than one way to skin this cat (and like, seriously, what sick individual came up with that expression?!), but one way to do this conversion is to realize that the range from -100 to 100 is 200 ‘steps’ (assuming whole numbers), but that the range from 1000 to 2000 that the pulse width must be is 1000 ‘steps’. So if we convert (or map) the 200 percentage steps to the 1000 pulse width steps we can see that each percentage step equates 5 pulse width steps.

So if we’re given a number between -100 to 100, say, -56

  • We add 100 to it to convert it into the range 0 to 200. (right? Adding 100 to any number between -100 to 100 shifts the number up by 100 to always be a positive number between 0 and 200) – so in our example -56 + 100 = 44.
  • We multiply this number by 5 to get the equivalent pulse steps (because each percentage step is the equivalent of 5 pulse width microseconds), so in our case 44 x 5 = 220 pulse steps.
  • We now add the resulting pulse steps to the smallest value in our pulse step range of 1000. So in our example we add 220 to the minimum pulse width value of 1000 to get 1220.

So 1220 microseconds (or 1.22 milliseconds) is the value we will give to the servo motor block to make it spin at -56% speed. This 1220 number kind of makes sense, because -100% or maximum negative would give a value of 1000, and full stop is a value of 1500 as shown before. So -56% or 1220 microseconds is a little bit over the halfway value of 1250 microseconds.

Does that make sense? If not drop a question in the forum for this discussion and maybe we can give you some different explanations or examples that help make it click for you!

Do you want the good news now?

You don’t have to do any of this Maths! It is such a common thing to convert/scale/map from one number range to another, that UIFlow provides a block called ‘map’ that does it all for you! 😍

The bad news is that for reasons known only to themselves, this incredibly useful block is buried away in the Advanced->Easy I/O menu 🤷‍♂️.

Anyways. The way you use this block is you give the number that you want to map (or convert or rescale) as the first input. In our example this would be -56.

You then set what the low and high values of the range of this number are. In our example this would be -100 and 100.

Finally you set the low and high values of the range of numbers you are converting to. In our case this would be 1000 and 2000 for the Pulse Width Modulation pulse width.

The output of this block is the input number mapped/converted/rescaled into the new number range. In our example this block would spit out 1220.

Pretty nifty hey?

The move_motor function

Phew! Finally! Let’s put all that we’ve learned into a function that will spin the servo motor in the direction we want, at the speed that we want.

Similarly to what we learned about functions in the Applied Technology course, we’ll start by expanding the purple Functions menu in UIFlow, and dragging the top ‘to dosomething’ block onto the canvas. Lets rename this function to move_motor. This is the block that we want to call from the rest of our code whenever we want the servo motor to change its movement in some way.

When we call this function, we want to be able to give the percentage value for the motor speed and direction right? So lets give the function an input value (or parameter) that lets us set this percentage value.

Drag the ‘input name x’ block on the left and drop it inside the Inputs block, and rename the resulting input to percentage as shown.

We will now be able to give a percentage value to the move_motor block whenever we call it.

We now want to convert our percentage value into the microseconds that the servo block will want, so from its cunningly-hidden spot in the Advanced->Easy I/O menu drag in a map block. The map block will be grey until we connect it to another block in a second.

We need to set the first input value to the map block as the number we want to convert. This number is available from the percentage input value or parameter that we created a few seconds ago. So expand the Variables main menu, drag the block called ‘percentage’ into the canvas and clip it into the first input to map. Your canvas should look like the screenshot below.

Now we can drag in the Servo ‘write’ block from the Units->Servo menu, and clip our map block into the microsecond number input …. but we have a small snag. The servo motor ‘write’ block also lets us set which servo motor we’re controlling, and in this project we have two servos – one for the left-hand wheel and one for the right-hand wheel of our ‘bot. How do we now decide which servo to deal with?

Well, one way is to add another input value or parameter to the move_motor function that lets you indicate if this is a move command for the left or the right motor, and include an If block in your function to check this value and change which servo it uses. I’ll call this option A, with an example of how it could look below.

Personally I prefer what I’ll call option B – where I’ll rename the move_motor function to move_motor_left and specify the left hand servo, then clone this function into a move_motor_right and change which servo gets controlled. My main program code will then called move_motor_left or move_motor_right rather than passing the motor to move as a parameter. This option is probably less efficient than option A, but I think its easier to understand and debug what’s going on.

Option A: Using an If block to check the new motor_to_move parameter
Option B: Cloning and re-naming the function for each motor

Let’s test it out!

You could now write a simple program to simply call your move_motor block once as a quick ‘n dirty way to test your code out, but lets try something a bit cooler that makes use of our Cube’s big brain and fancy-schmancy touchscreen that those other ‘bots can only dream of! Lets add a touch slide to our Cube that lets us change the motor speed and direction on the fly!

Drag in a slider block onto the screen designer. I like to set the width to 300 and the heigh to 20 to make it easier to use the slider, but you do you. Set minValue to -100 and maxValue to 100 to set the range of values this slider controls (the -100 to 100 will become the percentage value for the motor – do you see where this is going?!). A sidenote: some version of UIFlow have a bug in the slider control where this minValue and maxValue from the screen designer get ignored. If this affects you, the workaround is to also drag in a ‘set slider range min x max y’ block from the UI->Slider menu and run it right as your program starts to over-ride the values from the screen designer.

So we now have a touch control that we can adjust from -100 to 100. Lets slap that puppy into a loop, where each time around the loop we give the touch control value to our motor! Here’s how the whole program looks for me:

You might notice that my servos are called 4 and 5, whereas yours may be 0 and 1. In addition, my servo 4 drives the left wheel, whereas your first servo might drive the right wheel depending on how you wired up your both and the order you dragged the blocks in. No problem – simply rename your functions or change the servos you write to to match up your left and right wheels in the real world.

Also note that I’m not running the optional feature to show battery voltage here.

Of course, the code above only drives one wheel at a time, but it is trivial to add another block to add the other servo to your code. Drag in the move_motor_right block as well as, or instead of the move_motor_left and give it a twirl.

Do you notice a problem?

If you’ve been following my code, you will notice something wrong – the right hand wheel looks like its spinning the wrong direction?! The issue here is not a programming error – the motor is spinning in the direction we told it to, however when we mount the motor on the ‘bot chassis in the real-world the motor mirrors the left-hand motor so that the same anti-clockwise spinning of the motor reverses the bot instead of moving it forward! The simplest solution to this problem is inside the move_motor_right function to simply ‘invert’ or ‘reverse’ the percentage value to swap around the positive and negative percentage values by multiplying the percentage value by -1 (negative one). Here is what the final code is that correctly moves the wheels in the same direction.

Wrapping up

So we can now move our motors in different directions! There’s lots you can do to enhance this basic driving robot:

You could add another touch slider to drive the wheels independently.

You can now start writing some cool programs to drive your bot in different directions similarly to a real-world version of the Logo or Turtle graphics language often use to teach coding. Tape a pen to your bot and create some straight, right_turn, left_turn and stop functions that use the Wait blocks from the Time menu and appropriate motor controls to have your bot drive around a page and draw crazy graphics!

Maybe you also want to light the left or the right sidebar lights to indicate which servo is being driven?

Maybe you also want to use the five LEDs on each sidebar to indicate whether the servo is stopped, moving forward or backward, and by how far? You could, for example, use middle LED 3 on the right hand led bar to indicate stopped and the Map block to convert percentage into a value from 1 to 5 to light LEDs 1 to 5 on the lightbar to indicate which direction the wheel is spinning. Do the same for the left side (this one is a bit trickier!)

How about creating some forward/left/right/stop arrows and displaying them on your bot’s screen to show directions its taking?

Once you’re done playing, we can move onto Part 3 where we use a light sensor to guide the ‘bots steering and follow a line.