Port of http://dev.qu.tu-berlin.de/projects/sf-razor-9dof-ahrs to an mbed, tested with a 9DOF Sensor Stick, SEN-10724

Dependencies:   mbed

Razor_AHRS.cpp

Committer:
lpetre
Date:
2011-12-27
Revision:
0:9a72d42c0da3
Child:
2:5aa75c3d8cc3

File content as of revision 0:9a72d42c0da3:

/* This file is part of the Razor AHRS Firmware */
#include "Razor_AHRS.h"
#include <math.h>

void IMU::read_sensors() {
    Read_Gyro(); // Read gyroscope
    Read_Accel(); // Read accelerometer
    Read_Magn(); // Read magnetometer
}

// Read every sensor and record a time stamp
// Init DCM with unfiltered orientation
// TODO re-init global vars?
void IMU::reset_sensor_fusion() {
    float temp0[3] = { accel[0], accel[1], accel[2] };
    float temp1[3];
    float temp2[3];
    float xAxis[] = {1.0f, 0.0f, 0.0f};

    read_sensors();
    timestamp = timer.read_ms();

    // GET PITCH
    // Using y-z-plane-component/x-component of gravity vector
    pitch = -atan2(accel[0], sqrt((double)(accel[1] * accel[1] + accel[2] * accel[2])));

    // GET ROLL
    // Compensate pitch of gravity vector
        
    Vector_Cross_Product(temp1, temp0, xAxis);
    Vector_Cross_Product(temp2, xAxis, temp1);
    // Normally using x-z-plane-component/y-component of compensated gravity vector
    // roll = atan2(temp2[1], sqrt(temp2[0] * temp2[0] + temp2[2] * temp2[2]));
    // Since we compensated for pitch, x-z-plane-component equals z-component:
    roll = atan2(temp2[1], temp2[2]);

    // GET YAW
    Compass_Heading();
    yaw = MAG_Heading;

    // Init rotation matrix
    init_rotation_matrix(DCM_Matrix, yaw, pitch, roll);
}

// Apply calibration to raw sensor readings
void IMU::compensate_sensor_errors() {
    // Compensate accelerometer error
    accel[0] = (accel[0] - ACCEL_X_OFFSET) * ACCEL_X_SCALE;
    accel[1] = (accel[1] - ACCEL_Y_OFFSET) * ACCEL_Y_SCALE;
    accel[2] = (accel[2] - ACCEL_Z_OFFSET) * ACCEL_Z_SCALE;

    // Compensate magnetometer error
    magnetom[0] = (magnetom[0] - MAGN_X_OFFSET) * MAGN_X_SCALE;
    magnetom[1] = (magnetom[1] - MAGN_Y_OFFSET) * MAGN_Y_SCALE;
    magnetom[2] = (magnetom[2] - MAGN_Z_OFFSET) * MAGN_Z_SCALE;

    // Compensate gyroscope error
    gyro[0] -= GYRO_AVERAGE_OFFSET_X;
    gyro[1] -= GYRO_AVERAGE_OFFSET_Y;
    gyro[2] -= GYRO_AVERAGE_OFFSET_Z;
}

// Reset calibration session if reset_calibration_session_flag is set
void IMU::check_reset_calibration_session() {
    // Raw sensor values have to be read already, but no error compensation applied

    // Reset this calibration session?
    if (!reset_calibration_session_flag) return;

    // Reset acc and mag calibration variables
    for (int i = 0; i < 3; i++) {
        accel_min[i] = accel_max[i] = accel[i];
        magnetom_min[i] = magnetom_max[i] = magnetom[i];
    }

    // Reset gyro calibration variables
    gyro_num_samples = 0;  // Reset gyro calibration averaging
    gyro_average[0] = gyro_average[1] = gyro_average[2] = 0;

    reset_calibration_session_flag = false;
}

void IMU::turn_output_stream_on() {
    output_stream_on = true;
    statusLed = 1;
}

void IMU::turn_output_stream_off() {
    output_stream_on = false;
    statusLed = 0;
}

// Blocks until another byte is available on serial port
char IMU::readChar() {
    while (pc.rxBufferGetCount() < 1) { } // Block
    return pc.getc();
}

void IMU::setup() {
    timer.start();
    // Init serial output
    pc.baud(OUTPUT__BAUD_RATE);

    // Init status LED
    statusLed = 0;

    // Init sensors
    wait_ms(50);  // Give sensors enough time to start
    I2C_Init();
    Accel_Init();
    Magn_Init();
    Gyro_Init();

    // Read sensors, init DCM algorithm
    wait_ms(20);  // Give sensors enough time to collect data
    reset_sensor_fusion();

    // Init output
#if (OUTPUT__HAS_RN_BLUETOOTH == true) || (OUTPUT__STARTUP_STREAM_ON == false)
    turn_output_stream_off();
#else
    turn_output_stream_on();
#endif
}

// Main loop
void IMU::loop() {

    // Read incoming control messages
    if (pc.rxBufferGetCount() >= 2) {
        if (pc.getc() == '#') { // Start of new control message
            int command = pc.getc(); // Commands
            if (command == 'f') // request one output _f_rame
                output_single_on = true;
            else if (command == 's') { // _s_ynch request
                // Read ID
                char id[2];
                id[0] = readChar();
                id[1] = readChar();

                // Reply with synch message
                pc.printf("#SYNCH");
                pc.putc(id[0]);
                pc.putc(id[1]);
                pc.printf(NEW_LINE);
            } else if (command == 'o') { // Set _o_utput mode
                char output_param = readChar();
                if (output_param == 'n') { // Calibrate _n_ext sensor
                    curr_calibration_sensor = (curr_calibration_sensor + 1) % 3;
                    reset_calibration_session_flag = true;
                } else if (output_param == 't') // Output angles as _t_ext
                    output_mode = OUTPUT__MODE_ANGLES_TEXT;
                else if (output_param == 'b') // Output angles in _b_inary form
                    output_mode = OUTPUT__MODE_ANGLES_BINARY;
                else if (output_param == 'c') { // Go to _c_alibration mode
                    output_mode = OUTPUT__MODE_CALIBRATE_SENSORS;
                    reset_calibration_session_flag = true;
                } else if (output_param == 's') // Output _s_ensor values as text
                    output_mode = OUTPUT__MODE_SENSORS_TEXT;
                else if (output_param == '0') { // Disable continuous streaming output
                    turn_output_stream_off();
                    reset_calibration_session_flag = true;
                } else if (output_param == '1') { // Enable continuous streaming output
                    reset_calibration_session_flag = true;
                    turn_output_stream_on();
                } else if (output_param == 'e') { // _e_rror output settings
                    char error_param = readChar();
                    if (error_param == '0') output_errors = false;
                    else if (error_param == '1') output_errors = true;
                    else if (error_param == 'c') { // get error count
                        pc.printf("#AMG-ERR:%d,%d,%d" NEW_LINE,num_accel_errors,num_magn_errors,num_gyro_errors);
                    }
                }
            }
#if OUTPUT__HAS_RN_BLUETOOTH == true
            // Read messages from bluetooth module
            // For this to work, the connect/disconnect message prefix of the module has to be set to "#".
            else if (command == 'C') // Bluetooth "#CONNECT" message (does the same as "#o1")
                turn_output_stream_on();
            else if (command == 'D') // Bluetooth "#DISCONNECT" message (does the same as "#o0")
                turn_output_stream_off();
#endif // OUTPUT__HAS_RN_BLUETOOTH == true
        } else { } // Skip character
    }

    // Time to read the sensors again?
    int now = timer.read_ms();
    if ((now - timestamp) >= OUTPUT__DATA_INTERVAL) {
        timestamp_old = timestamp;
        timestamp = now;
        if (timestamp > timestamp_old)
            G_Dt = (float) (timestamp - timestamp_old) / 1000.0f; // Real time of loop run. We use this on the DCM algorithm (gyro integration time)
        else
            G_Dt = 0;

        // Update sensor readings
        read_sensors();

        if (output_mode == OUTPUT__MODE_CALIBRATE_SENSORS) { // We're in calibration mode
            check_reset_calibration_session();  // Check if this session needs a reset
            if (output_stream_on || output_single_on)
                output_calibration(curr_calibration_sensor);
        } else if (output_mode == OUTPUT__MODE_SENSORS_TEXT) {
            // Apply sensor calibration
            compensate_sensor_errors();

            if (output_stream_on || output_single_on)
                output_sensors();
        } else if (output_mode == OUTPUT__MODE_ANGLES_TEXT || output_mode == OUTPUT__MODE_ANGLES_BINARY) {
            // Apply sensor calibration
            compensate_sensor_errors();

            // Run DCM algorithm
            Compass_Heading(); // Calculate magnetic heading
            Matrix_update();
            Normalize();
            Drift_correction();
            Euler_angles();

            if (output_stream_on || output_single_on) 
                output_angles();
        }

        output_single_on = false;

#if DEBUG__PRINT_LOOP_TIME == true
        Serial.print("loop time (ms) = ");
        Serial.println(millis() - timestamp);
#endif
    }
#if DEBUG__PRINT_LOOP_TIME == true
    else {
        Serial.println("waiting...");
    }
#endif
}