Introducing Robotics and Industrial Electronics

Robotics and Industrial Electronics is a core module for final-year students of the level 7 Electrical & Control Engineering programme (DT009) in the Dublin Institute of Technology. The learning outcomes of the module are:

  1. List, outline and discuss comparative features of robot technology
  2. Design, build and test robot actuator and control subsystems
  3. Design, build and test robot environment sensing subsystems
  4. Build, test and fault find an industrial communications network of controllers, actuators, sensors and/or data loggers
  5. Design, build and test computer-based robot systems
  6. Design and implement a control algorithm for a robotic system
  7. Document the development of a robotic system, including discussion of ethical concerns and
  8. the potential impact of such a system on society
  9. Apply forward and inverse kinematics to solve problems in robotic control
  10. Analyse and select DC power supply systems
  11. Select motor and drive system technologies for industrial applications

The module is very practically focused and involves implementing various robot subsystems. Each student also undertakes a mini-project in which he or she designs, builds and tests a small robotic system.

The revision material presented here is divided into the following sections:

  1. Introduction to robotic technology
  2. Fundamental electrical, mechanical and control principles
  3. The dsPIC microcontroller
  4. Actuators and power electronics

The specific components and materials used by each student will depend on what he/she decides to build. However, the following are all available from the school stores and are frequently used in the Robotics & Industrial Electronics laboratory.

  • Breadboard
  • Arduino Nano (with ATmega328 microcontroller)
  • 1 USB to mini USB cable
  • Geared DC motor
  • Stepper motor
  • Servo motor
  • SN754410NE quad half H-bridge IC
  • IRFZ44N power MOSFET transistor
  • BC237 NPN transistor (or similar)
  • TCRT5000 reflective infrared optical sensor
  • Header pins
  • LEDs – various colours
  • Standard resistor
  • Standard capacitor
  • Potentiometer
  • 4xAA battery holder
Posted in Uncategorized | Leave a comment

State machine example from class – “Shoo Robot”

//
// State Machine Example for Arduino Nano
// Written by Ted Burke - 14-11-2019
//

// Variables for sensor readings
int fls, frs, bls, brs; // colour sensors
float dl, dr, dc;       // left , right and centre distance

int state = 1;

void setup()
{
  pinMode(2, OUTPUT); // LM forward
  pinMode(3, OUTPUT); // LM reverse
  pinMode(4, OUTPUT); // RM forward
  pinMode(5, OUTPUT); // RM reverse
}

void loop()
{
  // Read all colour sensors
  fls = analogRead(0);
  frs = analogRead(1);
  bls = analogRead(2);
  brs = analogRead(3);

  // Read distance sensors
  dl = readLeftDistance();
  dr = readRightDistance();
  dc = readCentreDistance();

  // Implement the current state
  if (state == 1) // forward to line
  {
    // actuators
    motors(1, 1); // both motors forward

    // state transitions
    if (fls > 800) state = 2;
    if (frs > 800) state = 3;
    if (dl < 6.0) state = 11;
    if (dr < 6.0) state = 12;
    if (dc < 6.0) state = 9;
  }
  else if (state == 2) // straighten up right side
  {
    // actuators
    motors(0, 1); // LM stop, RM forward

    // state transitions
    if (frs > 800) state = 4;
  }
  else if (state == 3) // straighten up left side
  {
    // actuators
    motors(1, 0); // LM forward, RM stop

    // state transitions
    if (fls > 800) state = 4;
  }
  else if (state == 4) // turn 90 degrees anti-clockwise
  {
    // actuators
    motors(-1, 1); // LM reverse, RM forward

    // state transitions
    delay(1200);
    state = 5;
  }
  else if (state == 5) // forward to corner
  {
    // actuators
    motors(1, 1); // LM forward, RM forward

    // state transitions
    if (fls > 800) state = 6;
  }
  // etc etc
}

void motors(int left, int right)
{
  // Set left motor direction
  if (left < 0)
  {
    digitalWrite(2, LOW);
    digitalWrite(3, HIGH); // LM reverse
  }
  else if (left > 0)
  {
    digitalWrite(2, HIGH); // LM forward
    digitalWrite(3, LOW);
  }
  else
  {
    digitalWrite(2, LOW); // LM stop
    digitalWrite(3, LOW);
  }

  // Set right motor direction
  if (right < 0)
  {
    digitalWrite(4, LOW);
    digitalWrite(5, HIGH); // RM reverse
  }
  else if (right > 0)
  {
    digitalWrite(4, HIGH); // RM forward
    digitalWrite(5, LOW);
  }
  else
  {
    digitalWrite(4, LOW); // RM stop
    digitalWrite(5, LOW);
  }
}
Posted in Uncategorized | Leave a comment

State machine example – Golf ball collector

State table whiteboard snapshots:

Code:

//
// State table example for Arduino Nano - Golf Ball Collector
// Written by Ted Burke - last updated 22-11-2018
//
 
int state = 1;
int step_delay = 10;
 
// Remember which winding is active on each stepper
int active_winding_left = 1;
int active_winding_right = 1;
 
// Function prototypes
void left_step_forward();
void right_step_forward();
double distance_front();
double distance_left();
double distance_right();
 
void setup()
{
    // Actuator control pins
    pinMode(2, OUTPUT); // LM winding 1
    pinMode(3, OUTPUT); // LM winding 2
    pinMode(4, OUTPUT); // LM winding 3
    pinMode(5, OUTPUT); // LM winding 4
     
    pinMode(6, OUTPUT); // RM winding 1
    pinMode(7, OUTPUT); // RM winding 2
    pinMode(8, OUTPUT); // RM winding 3
    pinMode(9, OUTPUT); // RM winding 4
     
    // Sensor pins
    pinMode(10, OUTPUT); // trigger pin for front rangefinder
    pinMode(11, INPUT);  // echo pin for front rangefinder
     
    //
    // FILL IN PINS FOR LEFT AND RIGHT RANGEFINDERS
    //
}
 
void loop()
{
    int steps;
    int rff, rfl, rfr;
     
    while(state == 1) // Forward (up the field)
    {
        // Actuators
        left_step_forward();
        right_step_forward();
         
        // Read sensors
        rff = distance_front();
        rfl = distance_left();
        rfr = distance_right();
         
        // Change state?
        if (rff < 2.0 && rfr > 1.5) state = 2;
        if (rff < 2.0 && rfr <= 1.5) state = 4;
    }
     
    steps = 0;
    while(state == 2) // Turn right
    {
        // Actuators
        left_step_forward(); // left motor forward, right motor stop
        delay(step_delay);
         
        // Count steps
        steps = steps + 1;
         
        // Change state?
        if (steps == 805) state = 3;
    }
     
    while(state == 3) // Return (down the field)
    {
        // Actuators
        left_step_forward();
        right_step_forward();
         
        // Read sensors
        rff = distance_front();
        rfl = distance_left();
        rfr = distance_right();
         
        // Change state?
        if (rff < 2.0 && rfl > 1.5) state = 4;
        if (rff < 2.0 && rfl <= 1.5) state = 2;
    }
     
    steps = 0;
    while(state == 4) // Turn left
    {
        // Actuators
        right_step_forward(); // left motor forward, right motor stop
        delay(step_delay);
         
        // Count steps
        steps = steps + 1;
         
        // Change state?
        if (steps == 805) state = 1;
    }   
}
 
void set_left_windings(int a, int b, int c, int d)
{
    digitalWrite(2, a);
    digitalWrite(3, b);
    digitalWrite(4, c);
    digitalWrite(5, d);
}
 
void left_step_forward()
{
    // Select next winding
    active_winding_left = active_winding_left + 1;
    if (active_winding_left == 5) active_winding_left = 1;
     
    // Set the state of all four windings
    if (active_winding_left == 1) set_left_windings(1, 0, 0, 0);
    if (active_winding_left == 2) set_left_windings(0, 1, 0, 0);
    if (active_winding_left == 3) set_left_windings(0, 0, 1, 0);
    if (active_winding_left == 4) set_left_windings(0, 0, 0, 1);
}
 
void set_right_windings(int a, int b, int c, int d)
{
    digitalWrite(6, a);
    digitalWrite(7, b);
    digitalWrite(8, c);
    digitalWrite(9, d);
}
 
void right_step_forward()
{
    // Select next winding
    active_winding_right = active_winding_right + 1;
    if (active_winding_right == 5) active_winding_right = 1;
     
    // Set the state of all four windings
    if (active_winding_right == 1) set_right_windings(1, 0, 0, 0);
    if (active_winding_right == 2) set_right_windings(0, 1, 0, 0);
    if (active_winding_right == 3) set_right_windings(0, 0, 1, 0);
    if (active_winding_right == 4) set_right_windings(0, 0, 0, 1);
}
 
double distance_front()
{
    // Declare variables
    double d;
    unsigned long t1, t2, duration;
     
    // Trigger pulse to front rangefinder
    digitalWrite(10, HIGH);
    delayMicroseconds(20);
    digitalWrite(10, LOW);
     
    // Measure the duration of the echo pulse
    while(digitalRead(11) == 0); // Wait for start of echo pulse
    t1 = micros();               // Remember start time of pulse
    while(digitalRead(11) == 1); // Wait while echo remains high
    t2 = micros();
    duration = t2 - t1;
     
    // Convert duration into distance in metres
    d = (duration * 1e-6 * 340.0) / 2.0;
     
    return d;
}
 
double distance_left()
{
    // Fill these in yourself
}
 
double distance_right()
{
    // Fill these in yourself
}
Posted in Uncategorized | Leave a comment

SCARA kinematics – example M-file and whiteboard snapshot

%
% SCARA inverse kinematics calculation
% Written by Ted Burke - 15-11-2018
%

% Dimensions of manipulator's rigid segments
L1 = 0.4
L2 = 0.3

% Target position for end effector
x = 0.2
y = 0.5

% Calculate distance of end effector from origin
L3 = sqrt(x^2 + y^2);

% Calculate elbow angle
E = acos((L3^2 - L1^2 - L2^2)/(2*L1*L2));

% Calculate shoulder angle
R = atan2(y,x);
Q = acos((L1^2 + L3^2 - L2^2)/(2*L1*L3));
S = R - Q;

% Convert from radians to degrees
Edeg = 180.0 * E / pi
Sdeg = 180.0 * S / pi

Posted in Uncategorized | Leave a comment

Kinematics and mini-project ideas – whiteboard snapshots

Posted in Uncategorized | Leave a comment

HC-SR04 ultrasonic rangefinder example code for dsPIC30F4011

The following example program for a dsPIC30F40011 microcontroller takes a reading from a HC-SR04 ultrasonic rangefinder once every 200ms.

  • The dsPIC 30F4011 is running at 30 MIPS (using the internal fast RC oscillator with 16x PLL multiplier).
  • RC13 is a digital output and is connected to the “trigger” pin on the HC-SR04.
  • RC14 is a digital input and is connected to the “echo” pin on the HC-SR04.
  • The HC-SR04 requires trigger pulses of 10 us duration (or longer). In this example, the trigger pulses are 20 us in duration.
  • Timer 1 is used to measure the duration of the echo pulses. Its prescaler is set to 1:8, so that each timer tick is equal to 8 instruction cycles (266.66 ns).
  • The UART is configured at 38400 baud to print the measured distance (in metres).
//
// dsPIC30F4011 Ultrasonic rangefinder example
// Written by Ted Burke
// Last updated 25-10-2017
//
// RC13 is the trigger pin (digital output)
// RC14 is the echo pin (digital input)
//
  
#include <xc.h>
#include <stdio.h>
#include <libpic30.h>
  
// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin
  
void main()
{
    // Configure RC13 as a digital output to send the trigger pulse
    _TRISC13 = 0;
    
    // Setup UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART
  
    // We'll use Timer 1 to measure the duration of the echo pulse
    // We set the Timer 1 prescaler to 1:8, which means that each tick
    // of the timer is equal to 8 instruction cycles - i.e. 266.66 ns
    PR1 = 65535;          // Set the Timer 1 period (max 65535)
    T1CONbits.TCKPS = 1;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    
    double distance;
    
    while(1)
    {
        // Send trigger pulse
        _LATC13 = 1;                        // begin trigger pulse
        __delay32(600);                     // 20 us
        _LATC13 = 0;                        // end trigger pulse
        
        // Measure duration of echo pulse
        while (_RC14 == 0);                 // wait for beginning of echo pulse
        TMR1 = 0;                           // reset Timer 1 counter
        T1CONbits.TON = 1;                  // start Timer 1
        while (_RC14 == 1 && TMR1 < 50000); // wait for end of echo pulse (timeout after 13.33 ms)
        T1CONbits.TON = 0;                  // stop Timer 1
        
        // Calculate the distance from duration of echo pulse
        distance = TMR1 * 4.5333e-5;
        printf("Distance: %f metres\r\n", distance);
        
        // Delay (200 ms) before next reading
        __delay32(6000000);
    }
}
Posted in Uncategorized | Leave a comment

Slaughterbots video

This thought-provoking video explores some of the dangers of so-called autonomous weapons. I think it’s important for all of our futures that more people become aware of the debate about autonomous weapons. Videos like this seem like a very effective way of making people understand the threat.

Warning: This video contains some violent scenes

Posted in Uncategorized | Leave a comment

Two alternative state machine structures with Timer 1 millisecond counter

These are the two versions of the dsPIC30F state machine code we looked at in today’s class, both of which use a Timer 1 interrupt service routine to increment a millisecond counter that is used to trigger timeout state transitions. The two versions are functionally more or less equivalent, but one may produce clearer code than the other in certain situations.

The first version is what I call “if inside while”. A single while(1) loop drives the state machine. Each time around the loop, all sensors are read at the start of the loop and then an if-else-if statement is used to select the code for the current state.

//
// dsPIC30F4011 state machine example with timer
// Version 1 - "if inside while"
//
// Written by Ted Burke, 13-11-2017
//

#include <xc.h>
#include <libpic30.h>

#define M_PI 3.141592653589793

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);

// Millisecond counter and state variable
int millis = 0;
int state = 1;  

void main()
{
    // Make every pin in Port D an output
    TRISD = 0;

    // Configure Timer 1 to generate interrupt every 1ms
    PR1 = 30000;          // Set the Timer 1 period (max 65535)
    TMR1 = 0;             // Reset Timer 1 counter
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    T1CONbits.TON = 1;    // Turn on Timer 1
    
    // State loop (if inside while)
    while(1)
    {
        // Read sensors
        //colour = analogRead(0);
        //distance = analogRead(1);
        
        if (state == 1)
        {
            // Set actuators
            LATD = 0b0001;
            
            // Change state?
            if (millis > 500) gotoState(2);
        }
        else if (state == 2)
        {
            // Set actuators
            LATD = 0b0010;
            
            // Change state?
            if (millis > 1000) gotoState(3);
        }
        else if (state == 3)
        {
            // Set actuators
            LATD = 0b0100;
            
            // Change state?
            if (millis > 1500) gotoState(1);
        }
    }
}

// Reset millisecond counter and change state
void gotoState(int newState)
{
    millis = 0;
    state = newState;
}

// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
  
    millis = millis + 1;
}

The second version is what I call “whiles inside while”. In this structure, each state has its own dedicated while loop, which must include reading any relevant sensors as well as setting actuators and checking for state transitions. Each state’s loop can be preceded by initialisation code if required (e.g. resetting the millisecond timer). If initialisation code should only be executed when actually entering the state, then it can be enclosed in an if statement.

//
// dsPIC30F4011 state machine example with timer
// Version 2 - "whiles inside while"
//
// Written by Ted Burke, 13-11-2017
//

#include <xc.h>
#include <libpic30.h>

#define M_PI 3.141592653589793

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);

// Millisecond counter and state variable
int millis = 0;
int state = 1;  

void main()
{
    // Make every pin in Port D an output
    TRISD = 0;

    // Configure Timer 1
    PR1 = 30000;          // Set the Timer 1 period (max 65535)
    TMR1 = 0;             // Reset Timer 1 counter
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TCKPS = 0;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    T1CONbits.TON = 1;    // Turn on Timer 1
    
    // State loop (whiles inside while)
    while(1)
    {
        millis = 0;
        while (state == 1)
        {
            // Set actuators
            LATD = 0b0001;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 500) state = 2;
        }
        
        millis = 0;
        while (state == 2)
        {
            // Set actuators
            LATD = 0b0010;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 1000) state = 3;
        }
        
        millis = 0;
        while (state == 3)
        {
            // Set actuators
            LATD = 0b0100;
            
            // Read relevant sensors
            // ?????
            
            // Change state?
            if (millis > 1500) state = 1;
        }
    }
}

// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
  
    millis = millis + 1;
}
Posted in Uncategorized | Leave a comment