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
Advertisements
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

A handy “super example” for the dsPIC30F4011

This is a handy “super example” for the dsPIC30F4011, which uses several features of the microcontroller:

  • Sets all Port D pins as outputs (I have LEDs connected to all four).
  • Toggles an LED on RD0 every 200ms approx.
  • Configures AN0-AN8 as analog inputs.
  • Enables all three PWM channels in complimentary mode, with period set to 20 ms (suitable for servo control).
  • Configures the UART for printing analog voltage read on AN0
  • Characters are also read via UART to control some of the outputs: “2” toggles RD2, “3” toggles RD3, “s” steps the servo angle on PWM channel 1.
  • Configures the Timer 1 interrupt to call an interrupt service routine every 125ms.

When using this as template code for a new project, just delete the featured you don’t need and edit the ones you do.

//
// dsPIC30F4011 Super Example
// Written by Ted Burke
// Last updated 19-10-2017
//
// Featuring digital i/o, analog input,
// PWM output, UART Tx/Rx, Timer 1 ISR.
//
 
#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
 
// Function prototype for analog read function
unsigned int analogRead(int);
 
// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);
 
int main()
{
    double pw;   // Pulse width for servo (min 1ms, max 2ms)
    int voltage; // Analog voltage read which will be read from AN0
    char c;      // The char data type is basically just an 8-bit int
 
    ///////////////////////////////////////////////////////////////
    // Set up digital i/o, analog input, PWM, UART and interrupt //
    ///////////////////////////////////////////////////////////////
    
    // Configure all 4 port D pins as digital outputs
    TRISD = 0;
 
    // Configure AN0-AN8 as analog inputs
    ADCON3bits.ADCS = 15;  // Tad = 266ns, conversion time is 12*Tad
    ADCON1bits.ADON = 1;   // Turn ADC ON
 
    // Configure PWM for free running mode
    //
    //   PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * PTPER
    //   PWM pulse width = (Tcy/2) * prescale * PDCx
    //
    PWMCON1 = 0x00FF;     // Enable all PWM pairs in complementary mode
    PTCONbits.PTCKPS = 3; // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    PTPER = 9375;         // 20ms PWM period (15-bit period value)
    PDC1 = 937;           // 1.0ms pulse width on PWM channel 1
    PDC2 = 937;           // 1.0ms pulse width on PWM channel 2
    PDC3 = 937;           // 1.0ms pulse width on PWM channel 3
    PTMR = 0;             // Clear 15-bit PWM timer counter
    PTCONbits.PTEN = 1;   // Enable PWM time base
 
    // Setup UART
    U1BRG = 48;            // 38400 baud @ 30 MIPS
    U1MODEbits.UARTEN = 1; // Enable UART
 
    // Configure Timer 1
    // In this example, I'm setting PR1 and TCKPS for 8Hz
    PR1 = 14648;          // Set the Timer 1 period (max 65535)
    TMR1 = 0;             // Reset Timer 1 counter
    IEC0bits.T1IE = 1;    // Enable Timer 1 interrupt
    T1CONbits.TCKPS = 3;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    T1CONbits.TON = 1;    // Turn on Timer 1

    ///////////////////////
    // Main control loop //
    ///////////////////////
    
    while(1)
    {
        // Read a voltage from AN0 and print it via serial port
        voltage = analogRead(0);
        printf("Voltage = %d\r\n", voltage);
 
        // Toggle LED on RD0
        _LATD0 = 1 - _LATD0;
 
        // Check if any characters were received via UART
        if (U1STAbits.URXDA == 1)
        {
            c = U1RXREG;                       // read the character
            if (c == '2') _LATD2 = 1 - _LATD2; // toggle RD2 on/off
            if (c == '3') _LATD3 = 1 - _LATD3; // toggle RD3 on/off
            if (c == 's') pw = pw + 0.2;       // increase the servo angle
        }
        
        // Set servo angle by setting pulse width on PWM channel 1
        if (pw > 2.0) pw = 1.0;
        PDC1 = pw * 937.5; // Note: 937.5 units is equivalent to 1ms
 
        __delay32(6000000); // 200ms delay
    }
 
    return 0;
}
 
// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    IFS0bits.T1IF = 0;
 
    // Toggle LED on RD1
    _LATD1 = 1 - _LATD1;
}
  
// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
unsigned int analogRead(int channel)
{
    ADCHS = channel;          // Select the requested channel
    ADCON1bits.SAMP = 1;      // Start sampling
    __delay32(30);            // 1us delay @ 30 MIPS
    ADCON1bits.SAMP = 0;      // Start Converting
    while (!ADCON1bits.DONE); // Should take 12 * Tad = 3.2us
    return ADCBUF0;
}

This is the build script I’m using:

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out
Posted in Uncategorized | 1 Comment

Millisecond timer for state machine using Timer 1 interrupt

//
// Timer interrupt example for dsPIC30F4011
// Written by Ted Burke - 16-11-2016
//
// This is a simple state machine with four
// states. The current state is indicated
// using two LEDs. All state transitions are
// triggered by time elapsed since the
// previous state transition, as measured
// using a millisecond counter which is
// incremented every 30000 instruction cycles
// by a Timer 1 interrupt service routine.
//

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

_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Clock speed = 7.5MHz x 16, i.e. 30 MIPS
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// These variables must be global because they
// are accessed by the main function and the ISR
int state = 1;        // state variable
int milliseconds = 0; // millisecond counter

// Timer 1 interrupt service routine (ISR)
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    _T1IF = 0;      // Clear Timer 1 interrupt flag
    milliseconds++; // Increment the millisecond counter
}

// This function resets the millisecond counter and changes state
void change_state(int n)
{
    milliseconds = 0;
    state = n;
}

int main()
{
    // Configure digital I/O
    TRISD = 0b1100; // RD0 and RD1 are the two LEDs
    
    // Configure Timer 1
    PR1 = 30000;          // Set the Timer 1 period to 1 ms (30000 * Tcy)
    TMR1 = 0;             // Reset Timer 1 counter
    _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
    
    while(1)
    {
        if (state == 1)
        {
            // Display state on LEDs
            _LATD0 = 0;
            _LATD1 = 0;
            
            // Change state?
            if (milliseconds >= 500) change_state(2);
        }
        else if (state == 2)
        {
            // Display state on LEDs
            _LATD0 = 1;
            _LATD1 = 0;
            
            // Change state?
            if (milliseconds >= 500) change_state(3);
        }
        else if (state == 3)
        {
            // Display state on LEDs
            _LATD0 = 0;
            _LATD1 = 1;
            
            // Change state?
            if (milliseconds >= 2000) change_state(4);
        }
        else if (state == 4)
        {
            // Display state on LEDs
            _LATD0 = 1;
            _LATD1 = 1;
            
            // Change state?
            if (milliseconds >= 3000) change_state(1);
        }
    }
        
    return 0;
}
Posted in Uncategorized | Leave a comment