We have a robot with two wheels attached to motors, one on the left and one on the right

We have code to control the motors for the two wheels (which we call MotorM1 and MotorM2) with a value in the range -1 to 1, where 1 is full speed ahead, 0 is no movement, and -1 is full speed reverse

We have a camera mounted on the robot, facing directly ahead

We have code to get an image from the camera and can detect an object of interest, and find its centre (or centroid) within the image

The objective is:

If the object is in the middle of the image, robot should go straight ahead (M1=1, M2=1)

If the object is to the right, move forward-right (e.g. M1=1, M2=0.7)

Likewise, if the object is to the left of centre, move forward-left (e.g. M1=0.7, M2=1)

If the object is not found, we turn the robot around in a circle to search for it (M1=1, M2=-1)

The first three of these are illustrated below:

Our solution is to treat this as a variant of what we previously worked out before, where we had a joystick input, where the X direction of the joystick controls forward movement and its Y direction controls whether to move left or right. In this case, we are steering left/right while always moving forward. Therefore, X has a fixed value (X=1) and Y is a value that depends on the direction of the object of interest.

The equations we came up with are:

X = 1 (a fixed value)

Y = (W – HWid) * YMax / HWid

Then use X and Y to calculate the motor control values as before:

M1 = X + Y, constrained to being between -1 and +1

M2 = X – Y, constrained to being between -1 and +1

Here:

X is a fixed positive value: X=1 for full speed, or make it smaller to move forward more slowly

Y is calculated from the equation above

W is the distance of object’s centre from left edge of the image, in pixels

HWid is half the width of the image, in pixels (for example, for a basic VGA image, 640×480, HWid is 640/2 = 320)

YMax is a value approximately 0.5, but needs to be calibrated – it depends on how sharply you want your robot to steer towards the object

M1 is the control signal for Motor M1, in the range -1 (full reverse) to +1 (full forward)

M2 is the same for Motor M2

Constrained means: if the calculated value is less than -1, set it to -1; if it is greater than +1, set it to +1.

At our most recent session in the Hackers group in CoderDojo Athenry, we spent out time on practical mathematics, figuring out how, if we want to make a robot that is controlled by a person with a joystick, how exactly do we translate movements of the joystick to signals sent to the motors.

Our assumptions are:

We are controlling robot with 2 drive wheels, one on the left and one on the right, like the one shown in the photo above (which we made last year)

We assume that we have code to control the motors for the two wheels (which we call MotorM1 and MotorM2) with a value in the range -1 to 1, where 1 is full speed ahead, 0 is no movement, and -1 is full speed reverse

To make the robot turn, drive the motors M1 and M2 at different speeds

We assume that we have code to receive signals from the joystick, and get X and Y values in the range -1 to 1, as shown in the diagram below

Our approach was to think about what joystick positions (X and Y) should result in what robot movements (M1 and M2), and then see if we could come up a way of expressing M1 and M2 in terms of X and Y. We filled a whiteboard with satisfying diagrams and calculations; see the bottom of this post. I have re-dawn them for clarity below.

The resulting equations are quite simple:

M1 = X + Y, constrained to being between -1 and +1

M2 = X – Y, constrained to being between -1 and +1

Here:

M1 is the control signal for Motor M1, in the range -1 (full reverse) to +1 (full forward)

M2 is the same for Motor M2

X is the forward position of the joystick from -1 (full back) to +1 (full forward)

Y is the left/right position of the joystick from -1 (full left) to +1 (full right)

Constrained means: if the calculated value is less than -1, set it to -1; if it is greater than +1, set it to +1.

Here is the full set of joystick positions and motor movements that we considered, showing how the equations work:

Each motor needs two control signals in the range 0-255, one for forward and one for reverse, so we will need more code to convert our M1 and M2 to what is needed for the H-bridge, but that is a fairly easy job for a different day.

Handling Interrupts in Arduino

In Arduino, you can set up special functions that are a called depending on the state of some digital pins – these are called Interrupt Service Routines (ISRs). These routines are called separately from the main loop() that is always running, even if the main loop() is in the middle of another operation.

For controlling our robots, our Arduino might receive a signal on a pin, and if we want the robot to react quickly, an ISR can handle it. The ISR can send a signal on a different pin or change the state of a variable.

This code is simple and correctly-working demonstration of how interrupts work. Note that you don’t need to build any circuitry to try this out. A built-in LED on the Arduino, at pin 13, turns on/off depending on whether Pin 2 is connected/disconnected from the the GROUND pin.

There are three main tasks:

Define the ISR function: this is a function declared as void with no arguments: for example, our ISR is called changed and is defined as:
void changed() { … }
In our code, it sets the value of a variable called controlState and it turns the LED on/off. Note that the value of controlState is printed out repeatedly in the main program loop, showing how the ISR can change a variable that is used elsewhere.

In the setup() function, set the pin mode of the control pin to INPUT or INPUT_PULLUP (see below for the difference). For example, we have defined the variable control to have value 2 and are setting its mode like this:
pinMode(controlPin, INPUT_PULLUP);

In setup(), attach the ISR to the pin:
attachInterrupt(digitalPinToInterrupt(controlPin), changed, CHANGE);

One extra note about the above line of code: all pins have numbers and all interrupts have numbers, and these are not (necessarily) the same numbers. The function digitalPinToInterrupt() returns the interrupt number corresponding to a given digital pin.

Some other things to note:

The Interrupt Service Routine (ISR) should be short and run fast: delay() calls are ignored and print() can cause problems.

You can’t attach two interrupts to the one pin: use CHANGE and then test pin state

If you initiate the pin with INPUT_PULLUP, it is triggered by connecting it to GROUND; otherwise, if you initiate it with INPUT, you need a circuit with 10k resistor.

On an Uno, can only attach interrupts to pins 2 and 3.

If you are changing a variable in the ISR and need to use it elsewhere, declare it as volatile.

Here is the full Arduino code:

// Michael Madden, CoderDojo Athenry
//
// Test a pin interrupt to which an interrupt service routine (ISR) is attached.
// When the control pin is connected to GROUND, the LED on the board is turned on.
// Things we have figured out:
// * The interrupt service routine (ISR) is a function declared as void with no arguments.
// * It should be short and run fast: delay() calls are ignored and print() can cause problems.
// * You can't attach two interrupts to the one pin: use CHANGE and then test pin state
// * If you initiate the pin with INPUT_PULLUP, it is triggered by connecting it to GROUND;
// * otherwise, with INPUT, you need a circuit with 10k resistor.
// * On an Uno, can only attach interrupts to pins 2 and 3.
// * If you are changing a variable in the ISR and need to use it elsewhere, declare it as volitile.
const byte controlPin = 2; // Want to react when this pin is triggered
const byte ledPin = 13; // no circuit needed: there is a built in LED on 13
volatile int controlState = 0; // will change this value in ISR
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(controlPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(controlPin), changed, CHANGE);
Serial.begin(9600);
}
void loop() {
// The main loop does not do much, it just prints out the value of the
// control pin's state every 2 seconds.
Serial.print("Current value of control pin = ");
Serial.println(controlState);
delay(2000);
}
void changed()
{
// This is our interrupt service routine.
// It is triggered when the control pin changes,
// so we check its state and turn on/off the LED.
//
controlState = digitalRead(controlPin);
if(controlState > 0) {
digitalWrite(ledPin, HIGH);
}
else {
digitalWrite(ledPin, LOW);
}
}

When you write to a pin on the Arduino, it outputs a voltage. However, you can’t use this directly to drive an electric motor, because they require too much current, and it would damage the Arduino. The solution is to use a 6V battery as an external power supply, and connect it to the motor via a transistor circuit. When you apply a signal with small current to the middle leg of the transistor, a much larger current can flow from the battery to the motor.

While this works, a more elaborate circuit is needed if you want to be able to control two motors, and make them go backwards and forwards. This circuit is called a Dual H-Bridge. The Wikipedia page has technical details: https://en.wikipedia.org/wiki/H_bridge

We are using a pre-built integrated circuit for our H-Bridge, as they are low-cost, small, and work well. Here is the one we are using:

It has several connectors:

[+] and [-] are where the external battery is connected

[IN1] and [IN2] control Motor A (details below)

[IN3] and [IN4] control Motor B

[Motor A] and [Motor B] each have two pins that are connected directly to motors

To control Motor A, connect [IN1] and [IN2] to two pins of the Arduino, such as 6 and 7:

[IN1] HIGH and [IN2] LOW: Motor A goes forward full speed

[IN1] LOW and [IN2] HIGH: Motor A goes backward full speed

Both LOW: Motor A does not turn (no power, it coasts)

Both HIGH: Motor A does not turn (is braked)

To control speed, use a value for the pins connected to [IN1] or [IN2] in the range 0-255 (0=LOW, 255=HIGH)

Here is Arduino code to control a motor with a H-Bridge, written by Luke, one of our Hackers:

Last week, two of our members from the Hackers group were at the BT Young Scientist & Technology Exhibition, where they presented their projects. Luke, who goes to Clarin College in Athenry, presented a project called “Relax and Reward”, … Continue reading →

This week in the Hackers group, Kevin led the way in completing the work on the principles of the soldering iron station. While our relay can switch on/off a 220V device like the soldering iron, we tested it with a … Continue reading →

In Hackers, we started work on a short project for a temperature-controlled soldering station. As shown on the whiteboard above, the basic idea is:

A temperature sensor is connected to an Arduino (analog input)

A soldering iron, with its tip located near the sensor, is wired to the mains via a relay switch

The Arduino can control the relay switch to turn the soldering iron on/off

This is the basis for a simple bang-bang controller: we have a target temperature (e.g. 200 degrees) and a tolerance (e.g. ±10 degrees), then you turn it on if the temperature is below 190 degrees and turn it off if above 210 degrees.

We noted that since there will be space between the soldering iron tip and the temperature sensor, the temperature it will read will be lower than the tip temperature.

As the whiteboard shows, this project also involved discussion of: wiring for the thermocouple; how breadboards are wired; normally-open vs normally-closed relay switches.

One group used a LM35 temperature sensor – here are its specs, and the group found it interesting to see how detailed and useful these data sheets are: http://www.ti.com/product/LM35

The other group used a potentiometer to simulate temperature, so they could test it working over its full range, as shown here:

Below is the code for reading and displaying the temperature. Next steps will be to integrate it with the relay.

// Michael Madden, CoderDojo Athenry
// Reading a temperature sensor.
// The temperature sensor is an LM35 - here are its specs: http://www.ti.com/product/LM35
// THe temperature range is -55 to 150 degrees celcius.
// Its output voltage pin is connected to an analog input on the Arduino (A1),
// from which we read a value in the range 0-1023, so we convert them.
void setup() {
// Just needed for print statements to work
Serial.begin(9600);
}
void loop() {
// Read the temperature sensor: get a value in range 0-1023
int val = analogRead(A1);
// From the analog input, we get a value in the range 0-1023
// and we need to convert it to a temperature in the range -55 to 150 degrees.
// The two lines below achieve this in differnet ways and give the same result.
int temp1 = (val/1023.0)*205 - 55;
int temp2 = map(val, 0, 1023, -55, 150);
// Print out the raw value and converted temperature
Serial.print("Sensor value = ");
Serial.print(val);
Serial.print("\tTemperature 1 = ");
Serial.print(temp1);
Serial.print("\tTemperature 2 = ");
Serial.println(temp2);
// Wait 100 milliseconds before reading the sensor again
delay(1000);
}

We had a small but dedicated team of Hackers for our first week back at CoderDojo Athenry last Saturday.

To begin, we started working on 3D printers. 3D printers are a fantastic technology for turning 3D computer models into physical objects. Here are Kevin’s notes on how to set up a 3D printer: 3d-printer-setup (PDF)

And here also is a diagram Kevin prepared, showing the 3D printing workflow:

In addition to setting up software for the 3D printers on group members’ Windows and Linux machines, we started planning potential projects for this year.

One possible project is to build an 8-bit PC from individual components. It was mentioned that it’s Turing complete, which led to a discussion of some concepts that are named after Alan Turing:

Turing Complete: a computer system is Turing Complete if it has the core features that mean it can run any algorithm. For a modern programming language, this means in practice: memory (variables); decisions (if statements); repetition (loops). However, this does not consider things like how data is input and output (file handling, displays, networking, etc), those secondary capabilities are a lot of what make computers useful.

A nice flip side of Turing Completeness is that if you are learning a new programming language, and you can figure out how to handle variables, decisions and loops in that language, you have mastered all the basics!

The Turing Test is a different concept that Alan Turing came up with, when he was thinking about early concepts of Artificial Intelligence (which was impressive considering how computers barely existed!) Rather than thinking about creating computer intelligence by replicating the functions of the human brain, he imagined an experiment where a human would communicate with either another human or a computer (selected at random); in the conversation, the asker could ask whatever they liked, and the human or computer answerer could try to deceive if they wished. If the experiment is repeated and askers cannot determine accurately whether the answerer is a human or computer, we should conclude that the computer is behaving intelligently.

Incidentally, Google’s recent Duplex demo, where an AI system can phone businesses to make appointments, is arguably an example of an AI system passing the Turing test, asn the people receiving the phone calls thought they were talking to a human: