Library for the PsiSwarm Robot - Version 0.7

Dependents:   PsiSwarm_V7_Blank

Fork of PsiSwarmLibrary by James Hilder

sensors.cpp

Committer:
jah128
Date:
2016-10-15
Revision:
5:3cdd1a37cdd7
Parent:
2:c6986ee3c7c5
Child:
6:b340a527add9

File content as of revision 5:3cdd1a37cdd7:

/* University of York Robotics Laboratory PsiSwarm Library: Sensor Functions Source File
 *
 * File: sensors.cpp
 *
 * (C) Dept. Electronics & Computer Science, University of York
 * James Hilder, Alan Millard, Alexander Horsfield, Homero Elizondo, Jon Timmis
 *
 * PsiSwarm Library Version: 0.7
 *
 * October 2016
 *
 *
 */

#include "psiswarm.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Ultrasonic Sensor (SRF02) Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// The ultrasonic sensor needs a start command to be sent: this is done by calling update_ultrasonic_measure().
// It can be set to automatically refresh at 10Hz by called enable_ultrasonic_ticker and disable with disabled_ultrasonic_ticker

void enable_ultrasonic_ticker()
{
    ultrasonic_ticker.attach_us(update_ultrasonic_measure,100000);
}

void disable_ultrasonic_ticker()
{
    ultrasonic_ticker.detach();
}

void update_ultrasonic_measure()
{
    if(waiting_for_ultrasonic == 0) {
        waiting_for_ultrasonic = 1;
        if(has_ultrasonic_sensor) {
            char command[2];
            command[0] = 0x00;                              // Set the command register
            command[1] = 0x51;                          // Get result is centimeters
            primary_i2c.write(ULTRASONIC_ADDRESS, command, 2);              // Send the command to start a ranging burst
        }
        ultrasonic_timeout.attach_us(IF_read_ultrasonic_measure,70000);
    } else {
        debug("WARNING:  Ultrasonic sensor called too frequently.\n");
    }
}

void IF_read_ultrasonic_measure()
{
    if(has_ultrasonic_sensor) {
        char command[1];
        char result[2];
        command[0] = 0x02;                           // The start address of measure result
        primary_i2c.write(ULTRASONIC_ADDRESS, command, 1, 1);           // Send address to read a measure
        primary_i2c.read(ULTRASONIC_ADDRESS, result, 2);                // Read two byte of measure
        ultrasonic_distance = (result[0]<<8)+result[1];
    } else ultrasonic_distance = 0;
    ultrasonic_distance_updated = 1;
    waiting_for_ultrasonic = 0;
    //debug("US:%d cm\n",ultrasonic_distance);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Additional Sensing Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

float get_temperature()
{   
    if(has_temperature_sensor)return IF_read_from_temperature_sensor();
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Voltage Sensing Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

float get_battery_voltage ()
{
    float raw_value = vin_battery.read();
    return raw_value * 4.95;
}

float get_current ()
{
    // Device uses a INA211 current sense monitor measuring voltage drop across a 2mOhm resistor
    // Device gain = 500
    float raw_value = vin_current.read();
    return raw_value * 3.3;
}

float get_dc_voltage ()
{
    float raw_value = vin_dc.read();
    return raw_value * 6.6;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// IR Sensor Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Estimates the distance to an obstacle from one of the IR sensors, defined by index (range 0-7).
// The value is converted to an approximate distance in millimetres, or 100.0 if no obstacle found.
// NB This function is preserved for code-compatability and cases where only a single reading is needed
// In many cases it is preferable to call store_reflected_ir_distances() to save all 8 values then read using get_reflected_ir_distance()
float read_reflected_ir_distance ( char index )
{
    // Sanity check
    if(index>7) return 0.0;

    //1.  Read the ADC value without IR emitter on; store in the background_ir_values[] array
    background_ir_values [index] = IF_read_IR_adc_value(1, index );

    //2.  Enable the relevant IR emitter by turning on its pulse output
    switch(index) {
        case 0:
        case 1:
        case 6:
        case 7:
            IF_set_IR_emitter_output(0, 1);
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            IF_set_IR_emitter_output(1, 1);
            break;
    }
    wait_us(ir_pulse_delay);

    //3.  Read the ADC value now IR emitter is on; store in the illuminated_ir_values[] array
    illuminated_ir_values [index] = IF_read_IR_adc_value (1, index );

    //4.  Turn off IR emitter
    switch(index) {
        case 0:
        case 1:
        case 6:
        case 7:
            IF_set_IR_emitter_output(0, 0);
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            IF_set_IR_emitter_output(1, 0);
            break;
    }

    //5.  Estimate distance based on 2 values using calculate_reflected_distances(); store in reflected_ir_distances()
    reflected_ir_distances [index] = calculate_reflected_distance( background_ir_values [index], illuminated_ir_values [index]);
    ir_values_stored = 1;
    return reflected_ir_distances [index];
}


// Returns the stored value of the reflected obstacle based on last call of either read_reflected_ir_distance or store_reflected_distances
float get_reflected_ir_distance ( char index )
{
    // Sanity check: check range of index and that values have been stored
    if (index>7 || ir_values_stored == 0) return 0.0;
    return reflected_ir_distances[index];
}

// Returns the stored value of the non-illuminated sensor based on last call of store_background_raw_ir_values
unsigned short get_background_raw_ir_value ( char index )
{
    // Sanity check: check range of index and that values have been stored
    if (index>7 || ir_values_stored == 0) return 0.0;
    return background_ir_values[index];
}

// Returns the stored value of the illuminated sensor based on last call of store_illuminated_raw_ir_values
unsigned short get_illuminated_raw_ir_value ( char index )
{
    // Sanity check: check range of index and that values have been stored
    if (index>7 || ir_values_stored == 0) return 0.0;
    return illuminated_ir_values[index];
}

// Stores the reflected distances for all 8 IR sensors
void store_reflected_ir_distances ( void )
{
    store_ir_values();
    for(int i=0; i<8; i++) {
        reflected_ir_distances [i] = calculate_reflected_distance( background_ir_values [i], illuminated_ir_values [i]);
    }
}

// Stores the rbackground and illuminated values for all 8 IR sensors
void store_ir_values ( void )
{
    store_background_raw_ir_values();
    store_illuminated_raw_ir_values();
}

// Stores the raw ADC values for all 8 IR sensors without enabling IR emitters
void store_background_raw_ir_values ( void )
{
    ir_values_stored = 1;
    for(int i=0; i<8; i++) {
        background_ir_values [i] = IF_read_IR_adc_value(1,i);
    }
}

// Stores the raw ADC values for all 8 IR sensors with a 500us emitter pulse
void store_illuminated_raw_ir_values ( void )
{
    //1.  Enable the front IR emitters and store the values
    IF_set_IR_emitter_output(0, 1);
    wait_us(ir_pulse_delay);
    illuminated_ir_values [0] = IF_read_IR_adc_value(1,0);
    illuminated_ir_values [1] = IF_read_IR_adc_value(1,1);
    illuminated_ir_values [6] = IF_read_IR_adc_value(1,6);
    illuminated_ir_values [7] = IF_read_IR_adc_value(1,7);
    IF_set_IR_emitter_output(0, 0);

    //2.  Enable the rear+side IR emitters and store the values
    IF_set_IR_emitter_output(1, 1);
    wait_us(ir_pulse_delay);
    illuminated_ir_values [2] = IF_read_IR_adc_value(1,2);
    illuminated_ir_values [3] = IF_read_IR_adc_value(1,3);
    illuminated_ir_values [4] = IF_read_IR_adc_value(1,4);
    illuminated_ir_values [5] = IF_read_IR_adc_value(1,5);
    IF_set_IR_emitter_output(1, 0);
}

// Converts a background and illuminated value into a distance
float calculate_reflected_distance ( unsigned short background_value, unsigned short illuminated_value )
{
    float approximate_distance = 4000 + background_value - illuminated_value;
    if(approximate_distance < 0) approximate_distance = 0;

    // Very approximate: root value, divide by 2, approx distance in mm
    approximate_distance = sqrt (approximate_distance) / 2.0;

    // Then add adjustment value if value >27
    if(approximate_distance > 27) {
        float shift = pow(approximate_distance - 27,3);
        approximate_distance += shift;
    }
    if(approximate_distance > 90) approximate_distance = 100.0;
    return approximate_distance;
}

// Returns the illuminated raw sensor value for the IR sensor defined by index (range 0-7); turns on the emitters for a 500us pulse
unsigned short read_illuminated_raw_ir_value ( char index )
{
    //This function reads the IR strength when illuminated - used for PC system debugging purposes
    //1.  Enable the relevant IR emitter by turning on its pulse output
    switch(index) {
        case 0:
        case 1:
        case 6:
        case 7:
            IF_set_IR_emitter_output(0, 1);
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            IF_set_IR_emitter_output(1, 1);
            break;
    }
    wait_us(ir_pulse_delay);
    //2.  Read the ADC value now IR emitter is on
    unsigned short strong_value = IF_read_IR_adc_value( 1,index );
    //3.  Turn off IR emitter
    switch(index) {
        case 0:
        case 1:
        case 6:
        case 7:
            IF_set_IR_emitter_output(0, 0);
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            IF_set_IR_emitter_output(1, 0);
            break;
    }
    return strong_value;
}

// Base IR sensor functions


// Returns the stored value of the non-illuminated sensor based on last call of store_background_base_ir_values
unsigned short get_background_base_ir_value ( char index )
{
    // Sanity check: check range of index and that values have been stored
    if (index>4 || base_ir_values_stored == 0) return 0.0;
    return background_base_ir_values[index];
}

// Returns the stored value of the illuminated sensor based on last call of store_illuminated_base_ir_values
unsigned short get_illuminated_base_ir_value ( char index )
{
    // Sanity check: check range of index and that values have been stored
    if (index>4 || base_ir_values_stored == 0) return 0.0;
    return illuminated_base_ir_values[index];
}

// Stores the reflected distances for all 5 base IR sensors
void store_base_ir_values ( void )
{
    store_background_base_ir_values();
    store_illuminated_base_ir_values();
    //for(int i=0;i<5;i++){
    //  reflected_ir_distances [i] = calculate_reflected_distance( background_base_ir_values [i], illuminated_base_ir_values [i]);
    //}
}

// Stores the raw ADC values for all 5 base IR sensors without enabling IR emitters
void store_background_base_ir_values ( void )
{
    base_ir_values_stored = 1;
    for(int i=0; i<5; i++) {
        background_base_ir_values [i] = IF_read_IR_adc_value(2,i);
    }
}

// Stores the raw ADC values for all 5 base IR sensors with a 500us emitter pulse
void store_illuminated_base_ir_values ( void )
{
    //1.  Enable the base IR emitters and store the values
    IF_set_IR_emitter_output(2, 1);
    wait_us(base_ir_pulse_delay);
    for(int i=0; i<5; i++) {
        illuminated_base_ir_values [i] = IF_read_IR_adc_value(2,i);
    }

    IF_set_IR_emitter_output(2, 0);
}

// Routine to store detected line position in a similar format to the used on 3Pi\m3Pi\PiSwarm
void store_line_position ( )
{
    // Store background and reflected base IR values
    store_base_ir_values();
    int h_value[5];
    int line_threshold = 1000;
    int line_threshold_hi = 2000;
    char count = 0;
    line_found = 0;
    line_position = 0;
    for(int i=0; i<5; i++) {
        if(get_background_base_ir_value(i) > get_illuminated_base_ir_value(i)) h_value[i]=0;
        else h_value[i] = get_illuminated_base_ir_value(i) - get_background_base_ir_value(i);
        if(h_value[i] < line_threshold) count++;
    }
    if(count == 1) {
        line_found = 1;
        if(h_value[0] < line_threshold) {
            line_position = -1;
            if(h_value[1] < line_threshold_hi) line_position = -0.8;
        }

        if (h_value[1] < line_threshold) {
            line_position = -0.5 + (0.00005 * h_value[0]) - (0.0001 * h_value[2]);;
        }
        if(h_value[2] < line_threshold) {
            line_position = (0.00005 * h_value[1]) - (0.0001 * h_value[3]);
        }
        if(h_value[3] < line_threshold) {
            line_position = 0.5 + (0.00005 * h_value[2]) - (0.0001 * h_value[4]);;
        }
        if(h_value[4] < line_threshold) {
            line_position = 1;
            if(h_value[3] < line_threshold_hi) line_position = 0.8;
        }
    }
    if(count == 2) {
        if(h_value[0] && h_value[1] < line_threshold) {
            line_found = 1;
            line_position = -0.6;
        }

        if(h_value[1] && h_value[2] < line_threshold) {
            line_found = 1;
            line_position = -0.4;
        }

        if(h_value[2] && h_value[3] < line_threshold) {
            line_found = 1;
            line_position = 0.4;
        }

        if(h_value[3] && h_value[4] < line_threshold) {
            line_found = 1;
            line_position = 0.6;
        }
    }
}

// Returns the subtraction of the background base IR value from the reflection based on last call of store_illuminated_base_ir_values
unsigned short calculate_base_ir_value ( char index ){
    // If the index is not in the correct range or the base IR values have not been stored, return zero
    if (index>4 || base_ir_values_stored == 0) return 0.0;
    // If the reflection value is greater than the background value, return the subtraction 
    if(illuminated_base_ir_values[index] > background_base_ir_values[index]){
        return illuminated_base_ir_values[index] - background_base_ir_values[index];
    //Otherwise return zero
    } else {
        return 0.0;   
    }
}

// Returns the subtraction of the background side IR value from the reflection based on last call of store_illuminated_base_ir_values
unsigned short calculate_side_ir_value ( char index ){
    // If the index is not in the correct range or the base IR values have not been stored, return zero
    if (index>7 || ir_values_stored == 0) return 0.0;
    // If the reflection value is greater than the background value, return the subtraction
    if(illuminated_ir_values[index] > background_ir_values[index]){
        return illuminated_ir_values[index] - background_ir_values[index];
    //Otherwise return zero
    } else {
        return 0.0;   
    }
}

void calibrate_base_ir_sensors (void)
{
    short white_background[5];
    short white_active[5];
    short black_background[5];
    short black_active[5];
    for(int k=0; k<5; k++) {
        white_background[k]=0;
        black_background[k]=0;
        white_active[k]=0;
        black_active[k]=0;
    }
    pc.printf("Base IR Calibration\n");
    display.clear_display();
    display.write_string("Calibrating base");
    display.set_position(1,0);
    display.write_string("IR sensor");
    wait(0.5);
    display.clear_display();
    display.write_string("Place robot on");
    display.set_position(1,0);
    display.write_string("white surface");
    wait(3);
    display.clear_display();
    display.write_string("Calibrating base");
    display.set_position(1,0);
    display.write_string("IR sensor");
    wait(0.5);
    pc.printf("\nWhite Background Results:\n");

    for(int i=0; i<5; i++) {
        wait(0.2);
        store_background_base_ir_values();

        display.set_position(1,9);
        display.write_string(".");
        wait(0.2);
        store_illuminated_base_ir_values();
        for(int k=0; k<5; k++) {
            white_background[k]+=  get_background_base_ir_value(k);
            white_active[k] += get_illuminated_base_ir_value(k);
        }
        pc.printf("Sample %d     1: %04d-%04d  2: %04d-%04d  3: %04d-%04d  4: %04d-%04d  5: %04d-%04d\n", (i+1),
                  get_background_base_ir_value(0),            get_illuminated_base_ir_value(0),
                  get_background_base_ir_value(1),            get_illuminated_base_ir_value(1),
                  get_background_base_ir_value(2),            get_illuminated_base_ir_value(2),
                  get_background_base_ir_value(3),            get_illuminated_base_ir_value(3),
                  get_background_base_ir_value(4),            get_illuminated_base_ir_value(4));
    }
    for(int k=0; k<5; k++) {
        white_background[k]/=5;
        white_active[k]/=5;
    }
    pc.printf("Mean results 1: %04d-%04d  2: %04d-%04d  3: %04d-%04d  4: %04d-%04d  5: %04d-%04d\n",
              white_background[0],          white_active[0],
              white_background[1],          white_active[1],
              white_background[2],          white_active[2],
              white_background[3],          white_active[3],
              white_background[4],          white_active[4]);

    display.clear_display();
    display.write_string("Place robot on");
    display.set_position(1,0);
    display.write_string("black surface");
    wait(3);

    display.clear_display();
    display.write_string("Calibrating base");
    display.set_position(1,0);
    display.write_string("IR sensor");
    wait(0.5);
    pc.printf("\nBlack Background Results:\n");

    for(int i=0; i<5; i++) {
        wait(0.2);

        store_background_base_ir_values();
        display.set_position(1,9);
        display.write_string(".");
        wait(0.2);
        store_illuminated_base_ir_values();
        for(int k=0; k<5; k++) {
            black_background[k]+=  get_background_base_ir_value(k);
            black_active[k] += get_illuminated_base_ir_value(k);
        }
        pc.printf("Sample %d     1: %04d-%04d  2: %04d-%04d  3: %04d-%04d  4: %04d-%04d  5: %04d-%04d\n", (i+1),
                  get_background_base_ir_value(0),            get_illuminated_base_ir_value(0),
                  get_background_base_ir_value(1),            get_illuminated_base_ir_value(1),
                  get_background_base_ir_value(2),            get_illuminated_base_ir_value(2),
                  get_background_base_ir_value(3),            get_illuminated_base_ir_value(3),
                  get_background_base_ir_value(4),            get_illuminated_base_ir_value(4));
    }
    for(int k=0; k<5; k++) {
        black_background[k]/=5;
        black_active[k]/=5;
    }
    pc.printf("Mean results 1: %04d-%04d  2: %04d-%04d  3: %04d-%04d  4: %04d-%04d  5: %04d-%04d\n",
              black_background[0],          black_active[0],
              black_background[1],          black_active[1],
              black_background[2],          black_active[2],
              black_background[3],          black_active[3],
              black_background[4],          black_active[4]);

}


int get_bearing_from_ir_array (unsigned short * ir_sensor_readings)
{
    //out("Getting bearing from array: [%d][%d][%d][%d][%d][%d][%d][%d]\n",ir_sensor_readings[0],ir_sensor_readings[1],ir_sensor_readings[2],ir_sensor_readings[3],ir_sensor_readings[4],ir_sensor_readings[5],ir_sensor_readings[6],ir_sensor_readings[7]);

    float degrees_per_radian = 57.295779513;

    // sin(IR sensor angle) and cos(IR sensor angle) LUT, for all 8 sensors
    float ir_sensor_sin[8] = {0.382683432, 0.923879533, 0.923879533, 0.382683432, -0.382683432, -0.923879533, -0.923879533, -0.382683432};
    float ir_sensor_cos[8] = {0.923879533, 0.382683432, -0.382683432, -0.923879533, -0.923879533, -0.382683432, 0.382683432, 0.923879533};

    float sin_sum = 0;
    float cos_sum = 0;

    for(int i = 0; i < 8; i++) {
        // Use IR sensor reading to weight bearing vector
        sin_sum += ir_sensor_sin[i] * ir_sensor_readings[i];
        cos_sum += ir_sensor_cos[i] * ir_sensor_readings[i];
    }

    float bearing = atan2(sin_sum, cos_sum); // Calculate vector towards IR light source
    bearing *= degrees_per_radian; // Convert to degrees

    //out("Sin sum:%f Cos sum:%f Bearing:%f\n",sin_sum,cos_sum,bearing);

    return (int) bearing;
}