Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • hil-fixes
  • spi-transaction-dma
  • nd015x-dma
  • logger-V2
  • arp-propagator
  • arp-gyro
  • async-fsm
  • chipselect-mux
  • nas-catch-dev
  • parafoil-mavlink-upd
  • mockup-main-software
  • quadspi-flash
  • quadspi-flash2
  • sx1278-resilience
  • units-impl
  • nokia-tm-dev
  • ram-testing-dev
  • spi
  • cc3135
  • ARP-pre-2.7
  • PYXIS_ROCCARASO
  • PYXIS_EUROC
  • lynx-euroc
  • hermes-v1.0
  • hermes-flight-1
26 results

bmx160-calibration-entry.cpp

Blame
  • bmx160-calibration-entry.cpp 13.83 KiB
    /* Copyright (c) 2021 Skyward Experimental Rocketry
     * Authors: Riccardo Musso, Alberto Nidasio
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include <drivers/interrupt/external_interrupts.h>
    #include <drivers/timer/TimestampTimer.h>
    #include <scheduler/TaskScheduler.h>
    #include <sensors/BMX160/BMX160.h>
    #include <sensors/BMX160/BMX160WithCorrection.h>
    #include <sensors/calibration/AxisOrientation.h>
    #include <sensors/calibration/BiasCalibration/BiasCalibration.h>
    #include <sensors/calibration/SixParameterCalibration/SixParameterCalibration.h>
    #include <sensors/calibration/SoftAndHardIronCalibration/SoftAndHardIronCalibration.h>
    #include <utils/Debug.h>
    
    #include <fstream>
    #include <iostream>
    
    using namespace std;
    using namespace miosix;
    using namespace Eigen;
    using namespace Boardcore;
    
    constexpr const char* CORRECTION_PARAMETER_FILE = "/sd/bmx160_params.csv";
    constexpr const char* MAG_CALIBRATION_DATA_FILE =
        "/sd/bmx160_mag_calibration_data.csv";
    
    constexpr int ACC_CALIBRATION_SLEEP_TIME     = 10;  // [s]
    constexpr int ACC_CALIBRATION_N_ORIENTATIONS = 6;
    
    constexpr int MAG_CALIBRATION_DURATION           = 30;  // [s]
    constexpr uint32_t MAG_CALIBRATION_SAMPLE_PERIOD = 20;  // [ms]
    
    constexpr int GYRO_CALIBRATION_DURATION = 10;  // [s]
    
    BMX160* bmx160;
    
    /**
     * @brief Orientations for accelerometer calibration.
     *
     * The BMX160 reference frame view facing the death stack x is:
     *          z   x
     *          ^   ^
     *          |  /
     *          | /
     *   y <----/
     *
     * Each AxisOrthoOrientation values indicates how to change x and y
     */
    AxisOrthoOrientation orientations[ACC_CALIBRATION_N_ORIENTATIONS]{
        {Direction::POSITIVE_X, Direction::POSITIVE_Y},  // Z up
        {Direction::POSITIVE_Z, Direction::POSITIVE_Y},  // X up
        {Direction::POSITIVE_X, Direction::POSITIVE_Z},  // Y up
        {Direction::POSITIVE_X, Direction::NEGATIVE_Y},  // Z down
        {Direction::NEGATIVE_Z, Direction::POSITIVE_Y},  // X down
        {Direction::POSITIVE_X, Direction::NEGATIVE_Z},  // Y down
    };
    
    constexpr const char* testHumanFriendlyDirection[]{
        "Z up", "X up", "Y up", "Z down", "X down", "Y down",
    };
    
    #if defined(_BOARD_STM32F429ZI_SKYWARD_DEATHST_X)
    SPIBus bus(SPI1);
    
    void __attribute__((used)) EXTI5_IRQHandlerImpl()
    {
    #elif defined(_BOARD_STM32F429ZI_SKYWARD_DEATHST_V3)
    SPIBus bus(SPI4);
    
    void __attribute__((used)) EXTI3_IRQHandlerImpl()
    {
    #else
    #error "Board not supported"
    #endif
        if (bmx160)
            bmx160->IRQupdateTimestamp(TimestampTimer::getTimestamp());
    }
    
    int menu();
    void waitForInput();
    
    void calibrateAccelerometer();
    void calibrateMagnetometer();
    void calibrateGyroscope();
    
    int main()
    {
    // Enable interrupt from BMX pin
    #if defined(_BOARD_STM32F429ZI_SKYWARD_DEATHST_X)
        enableExternalInterrupt(miosix::sensors::bmx160::intr::getPin().getPort(),
                                miosix::sensors::bmx160::intr::getPin().getNumber(),
                                InterruptTrigger::FALLING_EDGE);
    #elif defined(_BOARD_STM32F429ZI_SKYWARD_DEATHST_V3)
        enableExternalInterrupt(miosix::sensors::bmx160::intr::getPin().getPort(),
                                miosix::sensors::bmx160::intr::getPin().getNumber(),
                                InterruptTrigger::FALLING_EDGE);
    #else
    #error "Board not supported"
    #endif
    
        // Greet the user
        printf("\nWelcome to the calibration procedure!\n");
    
        // Make the user choose what to do
        switch (menu())
        {
            case 1:
                calibrateAccelerometer();
                break;
            case 2:
                calibrateMagnetometer();
                break;
            case 3:
                calibrateGyroscope();
                break;
    
            default:
                break;
        }
    
        return 0;
    }
    
    int menu()
    {
        int choice;
    
        printf("\nWhat do you want to do?\n");
        printf("1. Calibrate accelerometer\n");
        printf("2. Calibrate magnetometer\n");
        printf("3. Calibrate gyroscope\n");
        printf("\n>> ");
        scanf("%d", &choice);
    
        return choice;
    }
    
    void waitForInput()
    {
        string temp;
        do
        {
            cout << "Write 'c' to continue:\n";
            getline(cin, temp);
        } while (temp != "c");
    }
    
    void calibrateAccelerometer()
    {
        SixParametersCorrector correction;
        correction.fromFile("/sd/bmx160_accelerometer_correction.csv");
    
        SixParameterCalibration calibrationModel({0, 0, 9.8});
    
        BMX160Config bmxConfig;
        bmxConfig.fifoMode      = BMX160Config::FifoMode::HEADER;
        bmxConfig.fifoWatermark = 20;
        bmxConfig.fifoInterrupt = BMX160Config::FifoInterruptPin::PIN_INT1;
    
        bmxConfig.temperatureDivider = 0;
    
        bmxConfig.accelerometerRange = BMX160Config::AccelerometerRange::G_16;
        bmxConfig.gyroscopeRange     = BMX160Config::GyroscopeRange::DEG_2000;
    
        bmxConfig.accelerometerDataRate = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.gyroscopeDataRate     = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.magnetometerRate      = BMX160Config::OutputDataRate::HZ_100;
    
        bmxConfig.gyroscopeUnit = BMX160Config::GyroscopeMeasureUnit::RAD;
    
        bmx160 = new BMX160(bus, miosix::sensors::bmx160::cs::getPin(), bmxConfig);
    
        printf("Initializing BMX160...\n");
    
        if (!bmx160->init())
            printf("Init failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Performing self-test...\n");
    
        if (!bmx160->selfTest())
            printf("Self-test failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Initialization and self-test completed!\n");
    
        // Show the user the current correction values
        printf("Current correction parameters:\n");
        printf("A = |% 2.5f    % 2.5f    % 2.5f|\n\n", correction.getA()(0),
               correction.getA()(1), correction.getA()(2));
        printf("b = |% 2.5f    % 2.5f    % 2.5f|\n\n", correction.getb()(0),
               correction.getb()(1), correction.getb()(2));
    
        waitForInput();
    
        printf(
            "Please note that the BMX axis, viewed facing the ELC bay, are as "
            "follows:\n");
        printf("         z   x\n");
        printf("         ^   ^\n");
        printf("         |  /\n");
        printf("         | /\n");
        printf("  y <----/\n");
    
        for (unsigned i = 0; i < ACC_CALIBRATION_N_ORIENTATIONS; i++)
        {
            printf(
                "Step n.%u/%d, please rotate the death stack x so that the "
                "sensor "
                "is %s\n",
                i + 1, ACC_CALIBRATION_N_ORIENTATIONS,
                testHumanFriendlyDirection[i]);
    
            waitForInput();
    
            printf("Reding data and feeding the model...\n");
    
            TaskScheduler scheduler;
            scheduler.addTask(
                [&]()
                {
                    bmx160->sample();
    
                    uint8_t fifoSize = bmx160->getLastFifoSize();
                    auto& fifo       = bmx160->getLastFifo();
    
                    for (uint8_t ii = 0; ii < fifoSize; ii++)
                    {
                        Logger::getInstance().log(fifo.at(ii));
                        calibrationModel.feed(
                            static_cast<AccelerometerData>(fifo.at(ii)),
                            orientations[i]);
                    }
                },
                200);
            Logger::getInstance().start();
            scheduler.start();
    
            Thread::sleep(ACC_CALIBRATION_SLEEP_TIME * 1000);
    
            scheduler.stop();
            Logger::getInstance().stop();
        }
    
        printf("Computing the result....\n");
        auto newCorrector = calibrationModel.computeResult();
    
        printf("New correction parameters:\n");
        printf("A = |% 2.5f    % 2.5f    % 2.5f|\n\n", newCorrector.getA()(0),
               newCorrector.getA()(1), newCorrector.getA()(2));
        printf("b = |% 2.5f    % 2.5f    % 2.5f|\n\n", newCorrector.getb()(0),
               newCorrector.getb()(1), newCorrector.getb()(2));
    
        newCorrector.toFile("/sd/bmx160_accelerometer_correction.csv");
    }
    
    void calibrateMagnetometer()
    {
        SixParametersCorrector corrector;
        corrector.fromFile("/sd/bmx160_magnetometer_correction.csv");
    
        SoftAndHardIronCalibration calibrationModel;
        Vector3f avgMag{0, 0, 0}, vec;
    
        BMX160Config bmxConfig;
        bmxConfig.fifoMode      = BMX160Config::FifoMode::HEADER;
        bmxConfig.fifoWatermark = 20;
        bmxConfig.fifoInterrupt = BMX160Config::FifoInterruptPin::PIN_INT1;
    
        bmxConfig.temperatureDivider = 0;
    
        bmxConfig.accelerometerRange = BMX160Config::AccelerometerRange::G_16;
        bmxConfig.gyroscopeRange     = BMX160Config::GyroscopeRange::DEG_2000;
    
        bmxConfig.accelerometerDataRate = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.gyroscopeDataRate     = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.magnetometerRate      = BMX160Config::OutputDataRate::HZ_100;
    
        bmxConfig.gyroscopeUnit = BMX160Config::GyroscopeMeasureUnit::RAD;
    
        bmx160 = new BMX160(bus, miosix::sensors::bmx160::cs::getPin(), bmxConfig);
    
        printf("Initializing BMX160...\n");
    
        if (!bmx160->init())
            printf("Init failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Performing self-test...\n");
    
        if (!bmx160->selfTest())
            printf("Self-test failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Initialization and self-test completed!\n");
    
        // Show the user the current correction values
        printf("Current correction parameters:\n");
        printf("A = |% 2.5f    % 2.5f    % 2.5f|\n\n", corrector.getA()(0),
               corrector.getA()(1), corrector.getA()(2));
        printf("b = |% 2.5f    % 2.5f    % 2.5f|\n\n", corrector.getb()(0),
               corrector.getb()(1), corrector.getb()(2));
    
        printf("Now I will calibrate the magnetometer\n");
        printf(
            "Please, after starting the calibration, rotate the gyroscope in "
            "the "
            "most different directions.\n");
        printf("The calibration will run for %d seconds\n",
               MAG_CALIBRATION_DURATION);
    
        waitForInput();
    
        printf("Calibration started, rotate the stack!\n");
    
        TaskScheduler scheduler;
        scheduler.addTask(
            [&]()
            {
                bmx160->sample();
    
                uint8_t fifoSize = bmx160->getLastFifoSize();
                auto& fifo       = bmx160->getLastFifo();
    
                for (uint8_t i = 0; i < fifoSize; i++)
                {
                    Logger::getInstance().log(fifo.at(i));
                    calibrationModel.feed(fifo.at(i));
                }
            },
            200);
        Logger::getInstance().start();
        scheduler.start();
    
        Thread::sleep(MAG_CALIBRATION_DURATION * 1000);
    
        scheduler.stop();
        Logger::getInstance().stop();
    
        printf("Computing the result....\n");
        auto newCorrector = calibrationModel.computeResult();
    
        printf("New correction parameters:\n");
        printf("A = |% 2.5f    % 2.5f    % 2.5f|\n\n", newCorrector.getA()(0),
               newCorrector.getA()(1), newCorrector.getA()(2));
        printf("b = |% 2.5f    % 2.5f    % 2.5f|\n\n", newCorrector.getb()(0),
               newCorrector.getb()(1), newCorrector.getb()(2));
    
        newCorrector.toFile("/sd/bmx160_magnetometer_correction.csv");
    }
    
    void calibrateGyroscope()
    {
        BiasCalibration calibrationModel;
        int count = 0;
    
        BMX160Config bmxConfig;
        bmxConfig.fifoMode      = BMX160Config::FifoMode::HEADER;
        bmxConfig.fifoWatermark = 20;
        bmxConfig.fifoInterrupt = BMX160Config::FifoInterruptPin::PIN_INT1;
    
        bmxConfig.temperatureDivider = 0;
    
        bmxConfig.accelerometerRange = BMX160Config::AccelerometerRange::G_16;
        bmxConfig.gyroscopeRange     = BMX160Config::GyroscopeRange::DEG_2000;
    
        bmxConfig.accelerometerDataRate = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.gyroscopeDataRate     = BMX160Config::OutputDataRate::HZ_100;
        bmxConfig.magnetometerRate      = BMX160Config::OutputDataRate::HZ_100;
    
        bmxConfig.gyroscopeUnit = BMX160Config::GyroscopeMeasureUnit::RAD;
    
        bmx160 = new BMX160(bus, miosix::sensors::bmx160::cs::getPin(), bmxConfig);
    
        printf("Initializing BMX160...\n");
    
        if (!bmx160->init())
            printf("Init failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Performing self-test...\n");
    
        if (!bmx160->selfTest())
            printf("Self-test failed! (code: %d)\n", bmx160->getLastError());
    
        printf("Initialization and self-test completed!\n");
    
        printf(
            "Now starting the gyroscope calibration, leave the stack perfectly "
            "still\n");
    
        TaskScheduler scheduler;
        scheduler.addTask(
            [&]()
            {
                bmx160->sample();
    
                uint8_t fifoSize = bmx160->getLastFifoSize();
                auto& fifo       = bmx160->getLastFifo();
    
                for (uint8_t i = 0; i < fifoSize; i++)
                {
                    auto data = fifo.at(i);
                    Logger::getInstance().log(data);
    
                    calibrationModel.feed({data.angularSpeedX, data.angularSpeedY,
                                           data.angularSpeedZ});
                    count++;
                }
            },
            200);
    
        Logger::getInstance().start();
        scheduler.start();
    
        Thread::sleep(GYRO_CALIBRATION_DURATION * 1000);
    
        scheduler.stop();
        Logger::getInstance().stop();
    
        auto corrector = calibrationModel.computeResult();
    
        printf("New correction parameters:\n");
        printf("b = |% 2.5f    % 2.5f    % 2.5f|\n\n", corrector.getb()(0),
               corrector.getb()(1), corrector.getb()(2));
    }