BaseFlight Sensor2 - FabLabSeoul/WingProject GitHub Wiki

이 글은 Multiwii Baseflight μ˜€ν”ˆμ†ŒμŠ€λ₯Ό λΆ„μ„ν•œ 글이닀.

Stm32 value line discovery λ³΄λ“œμ— 맞게 μ½”λ“œλ₯Ό μž¬μž‘μ„± ν–ˆλ‹€.

이 글은 Baseflight의 Sensor 라이브러리λ₯Ό λΆ„μ„ν•œ 글이닀. Sensor μ΄ˆκΈ°ν™”μ— κ΄€λ ¨λœ 글은 Sensor 첫 번째 λ¬Έμ„œ BaseFlight Sensor λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•˜μž.

Sensor에 κ΄€ν•œ 글이 κΈΈμ–΄μ Έ 두 λ¬Έμ„œλ‘œ λ‚˜λˆ„μ—ˆλ‹€. μ—¬κΈ°μ„œλŠ” μ„Όμ„œ 값을 μ½μ–΄μ˜€λŠ” μ½”λ“œλ₯Ό 주둜 λΆ„μ„ν•œλ‹€.

Sensor 라이브러리(sensors.c) μ—μ„œ μ„Όμ„œ 값을 κ°€μ Έμ˜€λŠ” ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • ACC_getADC()
    • 가속도 μ„Όμ„œλ‘œλΆ€ν„° 값을 μ½μ–΄μ˜¨ ν›„, ACC_Common()ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.
  • ACC_Common()
    • 가속도 μ„Όμ„œ 값을 필터링해 x,y,zμΆ• 가속도(g) μ΅œμ’… 값을 accADC[]에 μ €μž₯ν•œλ‹€.
  • Gyro_getADC()
    • μžμ΄λ‘œμŠ€μ½”ν”„ 값을 μ½μ–΄μ˜¨ ν›„, GYRO_Common()ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.
  • GYRO_Common()
    • μžμ΄λ‘œμŠ€μ½”ν”„ 값을 필터링해 x,y,zμΆ• 각속도(degree/seconds) μ΅œμ’… 값을 gyroADC[]에 μ €μž₯ν•œλ‹€.
  • Mag_getADC()
    • μ§€μžκΈ° μ„Όμ„œ 값을 필터링해 x,y,zμΆ• 자기μž₯(gauss) μ΅œμ’… 값을 magADC[]에 μ €μž₯ν•œλ‹€.

ACC_Common() ν•¨μˆ˜

Baseflight Sensor::ACC_Common()ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

#define CALIBRATING_ACC_CYCLES              400

int16_t accADC[3];
uint16_t calibratingA = CALIBRATING_ACC_CYCLES;      // the calibration is done is the main loop. Calibrating decreases at each cycle down to 0, then we enter in a normal mode.
uint16_t acc_1G = 256;          // this is the 1G measured acceleration.

static void ACC_Common(void)
{
    static int32_t a[3];
    int axis;

    if (calibratingA > 0) {
        for (axis = 0; axis < 3; axis++) {
            // Reset a[axis] at start of calibration
            if (calibratingA == CALIBRATING_ACC_CYCLES)
                a[axis] = 0;
            // Sum up CALIBRATING_ACC_CYCLES readings
            a[axis] += accADC[axis];
            // Clear global variables for next reading
            accADC[axis] = 0;
            mcfg.accZero[axis] = 0;
        }
        // Calculate average, shift Z down by acc_1G and store values in EEPROM at end of calibration
        if (calibratingA == 1) {
            mcfg.accZero[ROLL] = (a[ROLL] + (CALIBRATING_ACC_CYCLES / 2)) / CALIBRATING_ACC_CYCLES;
            mcfg.accZero[PITCH] = (a[PITCH] + (CALIBRATING_ACC_CYCLES / 2)) / CALIBRATING_ACC_CYCLES;
            mcfg.accZero[YAW] = (a[YAW] + (CALIBRATING_ACC_CYCLES / 2)) / CALIBRATING_ACC_CYCLES - acc_1G;
            cfg.angleTrim[ROLL] = 0;
            cfg.angleTrim[PITCH] = 0;
        }
        calibratingA--;
    }

    accADC[ROLL] -= mcfg.accZero[ROLL];
    accADC[PITCH] -= mcfg.accZero[PITCH];
    accADC[YAW] -= mcfg.accZero[YAW];
}

ν”„λ‘œκ·Έλž¨μ΄ μ‹œμž‘λ˜λ©΄, 400 μ‚¬μ΄ν΄λ™μ•ˆ 가속도 값을 μΈ‘μ •ν•΄ 평균을 λ‚΄μ–΄ mcfg.accZero[] 에 μ €μž₯ν•œλ‹€. 그리고 평균 값을 λ°˜μ˜¬λ¦Όν•˜κΈ° μœ„ν•΄μ„œ 평균 값에 1/2λ₯Ό λ”ν•œ 값을 μ €μž₯ν•œλ‹€. Z좕에 - acc_1Gλ₯Ό ν•˜λŠ” μ΄μœ λŠ” κΈ°λ³Έ 쀑λ ₯ 가속도가 이미 반영된 μƒνƒœμ΄λ―€λ‘œ κ·Έ 값을 μ œκ±°ν•˜κΈ° μœ„ν•œ 계산이닀.

Calibration, 즉 ꡐ정단계가 λλ‚˜λ©΄, 본격적으둜 가속도 μ„Όμ„œ 값을 읽어 μ˜¨λ‹€. μ΅œμ’… 값은 accADC[] μ „μ—­ λ³€μˆ˜μ— μ €μž₯λœλ‹€.

accADC[ROLL] -= mcfg.accZero[ROLL];
accADC[PITCH] -= mcfg.accZero[PITCH];
accADC[YAW] -= mcfg.accZero[YAW];

처음 가속도 μ„Όμ„œ κ΅μ •μ‹œ μΈ‘μ •λ˜μ—ˆλ˜ 값이 mcfg.accZero[]에 μ €μž₯λ˜μ–΄ 있기 λ•Œλ¬Έμ—, 이 값을 μ‹€μ œ μ„Όμ„œκ°’μ—μ„œ λΉΌμ„œ μ €μž₯ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λŠ” μ΄μœ λŠ”, 초기 μ„Όμ„œμ˜ μžμ„Έλ₯Ό μ›μ μœΌλ‘œ, 초기 μžμ„Έμ—μ„œ λ³€ν™”λœ 가속도 값을 μ–»κΈ° μœ„ν•΄μ„œλ‹€. ν”„λ‘œκ·Έλž¨μ΄ μ‹œμž‘λ  λ•Œ, μ„Όμ„œκ°€ κΈ°μšΈμ–΄μ Έ μžˆλŠ” κ²½μš°κ°€ 있고, μ²˜μŒλΆ€ν„° κΈ°μšΈμ–΄μ§„ μƒνƒœλ‘œ μ„€κ³„λ˜μ—ˆλ”λΌλ„, μ›μ μ—μ„œ λ³€ν™”λœ κ°’λ§Œ μ½μ–΄μ˜€κΈ° λ•Œλ¬Έμ—, λ¬Έμ œμ—†μ΄ 가속도 값을 μ–»μ–΄μ˜¬ 수 있게 λœλ‹€.

GYRO_Common() ν•¨μˆ˜

Baseflight Sensor::GYRO_Common() ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

#define CALIBRATING_GYRO_CYCLES             1000
uint16_t calibratingG = CALIBRATING_GYRO_CYCLES;
int16_t gyroADC[3], accADC[3], accSmooth[3];
int16_t gyroZero[3] = { 0, 0, 0 };

mcfg.moron_threshold = 32;

static void GYRO_Common(void)
{
    int axis;
    static int32_t g[3];
    static stdev_t var[3];

    if (calibratingG > 0) {
        for (axis = 0; axis < 3; axis++) {
            // Reset g[axis] at start of calibration
            if (calibratingG == CALIBRATING_GYRO_CYCLES) {
                g[axis] = 0;
                devClear(&var[axis]);
            }
            // Sum up 1000 readings
            g[axis] += gyroADC[axis];
            devPush(&var[axis], gyroADC[axis]);
            // Clear global variables for next reading
            gyroADC[axis] = 0;
            gyroZero[axis] = 0;
            if (calibratingG == 1) {
                float dev = devStandardDeviation(&var[axis]);
                // check deviation and startover if idiot was moving the model
                if (mcfg.moron_threshold && dev > mcfg.moron_threshold) {
                    calibratingG = CALIBRATING_GYRO_CYCLES;
                    devClear(&var[0]);
                    devClear(&var[1]);
                    devClear(&var[2]);
                    g[0] = g[1] = g[2] = 0;
                    continue;
                }
                gyroZero[axis] = (g[axis] + (CALIBRATING_GYRO_CYCLES / 2)) / CALIBRATING_GYRO_CYCLES;
                blinkLED(10, 15, 1);
            }
        }
        calibratingG--;
    }
    for (axis = 0; axis < 3; axis++)
        gyroADC[axis] -= gyroZero[axis];
}

μžμ΄λ‘œμŠ€μ½”ν”„ κ°’μ˜ ν‘œμ€€ 편차λ₯Ό κ΅¬ν•˜κΈ° μœ„ν•œ ν•¨μˆ˜λ“€

  • stdev_t
    • ν‘œμ€€ 편차λ₯Ό κ΅¬ν•˜κΈ°μœ„ν•œ λ³€μˆ˜λ“€ μ €μž₯
  • devClear()
  • devPush()
    • 평균, 편차 μ €μž₯
  • devVariance()
    • λΆ„μ‚° 계산
  • devStandardDeviation()
    • ν‘œμ€€ 편차 계산

ν”„λ‘œκ·Έλž¨μ΄ μ‹œμž‘λ˜λ©΄ 1000 사이클 λ™μ•ˆ μžμ΄λ‘œμŠ€μ½”ν”„ ꡐ정(Calibration)단계λ₯Ό μ§„ν–‰ν•œλ‹€. 1000 μ‚¬μ΄ν΄λ™μ•ˆ μ„Όμ„œμ˜ ν‘œμ€€νŽΈμ°¨κ°€ mcfg.moron_threshold 보닀 크닀면, ꡐ정단계λ₯Ό λ‹€μ‹œ κ±°μΉœλ‹€. mcfg.moron_threshold 값은 32둜 μ„€μ •λ˜μ–΄ μžˆλ‹€. 이 μ˜λ―ΈλŠ”, 초기 μ„Όμ„œ μžμ„Έκ°€ μ›€μ§μ΄μ§€μ•Šκ³ , κ³ μ •λ˜μ–΄ μžˆμ–΄μ•Ό ν•œλ‹€λŠ” λœ»μ΄λ‹€. ꡐ정 단계가 문제 없이 끝났닀면, 1000 사이클 λ™μ•ˆμ˜ μžμ΄λ‘œμ„Όμ„œ κ°’μ˜ 평균을 gyroZero[] λ³€μˆ˜μ— μ €μž₯ν•œλ‹€. gyroZero[]λŠ” μžμ΄λ‘œμŠ€μ½”ν”„μ˜ 원점이 λœλ‹€.

    for (axis = 0; axis < 3; axis++)
        gyroADC[axis] -= gyroZero[axis];

μžμ΄λ‘œμŠ€μ½”ν”„ Calibration과정이 λͺ¨λ‘ 끝났닀면, μ½μ–΄μ˜¨ 자이둜 μ„Όμ„œ 값에 gyroZero[] 값을 λΉΌμ„œ μ΅œμ’… 값을 gyroADC[]에 μ €μž₯ν•œλ‹€. 이 μ˜λ―ΈλŠ” ν”„λ‘œκ·Έλž¨μ΄ μ‹œμž‘λ  λ•Œ, μ„Όμ„œμ˜ μžμ„Έλ₯Ό κΈ°λ³Έ μžμ„Έλ‘œν•΄μ„œ κ·Έ λ³€ν™” 값을 μ΅œμ’… κ°λ„λ‘œ κ²°μ •ν•˜κ² λ‹€λŠ” λœ»μ΄λ‹€.

Mag_getADC() ν•¨μˆ˜

Baseflight Sensor::Mag_getADC() ν•¨μˆ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

int16_t magADC[3];

int Mag_getADC(void)
{
    static uint32_t t, tCal = 0;
    static int16_t magZeroTempMin[3];
    static int16_t magZeroTempMax[3];
    uint32_t axis;

    if ((int32_t)(currentTime - t) < 0)
        return 0;                 //each read is spaced by 100ms
    t = currentTime + 100000;

    // Read mag sensor
    mag.read(magADC);

    if (f.CALIBRATE_MAG) {
        tCal = t;
       for (axis = 0; axis < 3; axis++) {
            mcfg.magZero[axis] = 0;
            magZeroTempMin[axis] = magADC[axis];
            magZeroTempMax[axis] = magADC[axis];
        }
        f.CALIBRATE_MAG = 0;
    }

    if (magInit) {              // we apply offset only once mag calibration is done
        magADC[X] -= mcfg.magZero[X];
        magADC[Y] -= mcfg.magZero[Y];
        magADC[Z] -= mcfg.magZero[Z];
    }

    if (tCal != 0) {
        if ((t - tCal) < 30000000) {    // 30s: you have 30s to turn the multi in all directions
            LED0_TOGGLE;
            for (axis = 0; axis < 3; axis++) {
                if (magADC[axis] < magZeroTempMin[axis])
                    magZeroTempMin[axis] = magADC[axis];
                if (magADC[axis] > magZeroTempMax[axis])
                    magZeroTempMax[axis] = magADC[axis];
            }
        } else {
            tCal = 0;
            for (axis = 0; axis < 3; axis++)
                mcfg.magZero[axis] = (magZeroTempMin[axis] + magZeroTempMax[axis]) / 2; // Calculate offsets
            //writeEEPROM(1, true);
        }
    }

    return 1;
}

Mag_getADC()ν•¨μˆ˜λŠ” 1μ΄ˆμ— 10번만 κ³„μ‚°ν•˜κ²Œ λ˜μ–΄ μžˆλ‹€. μ§€μžκΈ° μ„Όμ„œκ°€ μ •ν™•ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 그런게 μ•„λ‹Œκ°€ μƒκ°ν•œλ‹€. μ§€μžκΈ° μ„Όμ„œ 값은 크게 κ°€κ³΅ν•˜μ§€ μ•Šκ³  κ·ΈλŒ€λ‘œ μ΅œμ’… 값이 λœλ‹€.

μ§€μžκΈ° μ„Όμ„œμ˜ ꡐ정(calibration)λ‹¨κ³„λŠ” 기본적으둜 κ±°μΉ˜μ§€ μ•ŠλŠ”λ‹€. ꡐ정 μž‘μ—…μ€ f.CALIBRATE_MAG이 ν™œμ„±ν™” λ˜μ—ˆμ„ λ•Œ λΆ€ν„° μ‹œμž‘ν•˜κ³ , ꡐ정이 λλ‚œ ν›„, μ΅œμ’… 원점은 (tCal != 0) && ((t - tCal) > 30000000) 쑰건이 μ„±λ¦½ν–ˆμ„ λ•Œ, κ³„μ‚°λœλ‹€.

ν˜„μž¬ μ½”λ“œμ—μ„œ tCal이 μ—…λ°μ΄νŠΈ λ˜λŠ” κ²½μš°λŠ” f.CALIBRATE_MAG 이 1이상일 λ•Œ, μ„€μ •λœλ‹€. 이 ν”Œλž˜κ·ΈλŠ” μ™ΈλΆ€ μž…λ ₯으둜 ν™œμ„±ν™” λœλ‹€. μ§€μžκΈ° μ„Όμ„œκ°€ Calibration을 μ‹œμž‘ν•˜κ²Œ 되면, 30초 λ™μ•ˆ 3μΆ•μ˜ μ„Όμ„œ κ°’μ˜ μ΅œλŒ€, μ΅œμ†Œκ°’μ„ κ΅¬ν•œ ν›„, 쀑간 값을 원점 κ°’μœΌλ‘œ κ²°μ •μ§“λŠ”λ‹€. μ§€μžκΈ° μ„Όμ„œ 원점 값은 mcfg.magZero[]에 μ €μž₯λœλ‹€. ꡐ정 과정을 κ±°μΉ˜μ§€ μ•ŠμœΌλ©΄, μ„Όμ„œμ˜ ν˜„μž¬ μžμ„Έκ°€ κΈ°λ³Έ 값이 λœλ‹€. 이 μ˜λ―ΈλŠ”, μ§€μžκΈ° μ„Όμ„œμ˜ N λ°©ν–₯이 μ•žμ„ λ‚˜νƒ€λ‚΄λ―€λ‘œ, μΏΌλ“œμ½₯ν„°μ˜ 정면이 뢁μͺ½μ„ ν–₯ν•΄μžˆλ‹€κ³  κ°€μ •ν•œλ‹€λŠ” λœ»μ΄λ‹€.

μ§€μžκΈ° μ„Όμ„œμ˜ μ΅œμ’… 필터링 된 값은 magADC[]에 μ €μž₯λœλ‹€.