Создание собственной системы стабилизации

mahowik
Razek:

Саш а у тебя же что-то похожее есть только без ГПС?

да… в дневнике подробности…

RaJa:

если честно, не думаю, что большая разница между ITG3200 и MPU6050

Razek:

у меня рассыпуха в виде ITG3200

из своего опыта… гиры примерно одинаковые… я год налетал на 3205 и не могу сказать 6050 прям лучше… а вот аксель в mpu-ке получше, это да…

Razek:

Реально, кстати вам под AIOP эту прошивку залить самое то, плата прям в стоке поддерживается без бубна

пока не добавили дефайн на АИОП… MULTIWII_PRO это не то, там bmp085… в прошиве пока нет драйвера под ms5611 github.com/bradquick/bradwii_arduino/…/baro.cpp но адаптировать не сложно думаю… день-пять и будет ))

SergDoc:

с гирами беда какая-то - дёргаться даже в акро стал, возможно из-за той же жары плывут? (летал на самом сонцепёке, людей нет) пришлось задействовать и аксель и маг - пошло летать, а на гирах какойто дёрганый…

может вибрации полезли… вруби фильтр на гиру в drv_mpu6000.c на 20hz

static uint8_t mpuLowPassFilter = MPU6000_LPF_20HZ;
Razek
mahowik:

пока не добавили дефайн на АИОП… MULTIWII_PRO это не то, там bmp085…

Точно глянул на кинге совсем другая платка оказывается

SergDoc:

возможно из-за той же жары плывут?

Ну это проверить то не проблема просто прогреть квадрик на солнышке а потом полететь

SergDoc

Неа, я пропы поменял, вибрации бы аксель душили, приподнял чуть P и D отлетал батарейку нормально, гиры да, поставил на 20Гц - ещё по мелкоплате на прошивке Таймкопа знаю - на 42-х дёргается, но это не вибрации это скорей из-за особенности рамы - 12-е пропы для неё великоваты (на трёхе тоже на 11-х лучше себя чувствовала), надо 11-е заказать, а то нету нормальных у меня… и похоже один мотор у меня 750-й, а не 700, как-то уж очень обмотка отличается от трёх остальных, попробую из старого, с битым колокольчиком собрать…

SergDoc

надо, добивать что-бы логи писала, либо телеметрию на комп, а то серьёзно как слепой котёнок блин… наверно логи в первую очередь - флешь - то у меня на плате есть…

RaJa
Razek:

Да, у меня рассыпуха в виде ITG3200 и BMA180, реально они хуже мпушек.

Реально, кстати вам под AIOP эту прошивку залить самое то, плата прям в стоке поддерживается без бубна

Ну как тут уже сказали, разница в гирах невелика, у меня тоже этих платок 10DOF штуки 2-3 лежит на ITG3200 ADXL345 и пр.
Жаль только плата не моя, она у меня на тестах, да и адаптировать под нее код надо.

SergDoc

Вспомнил, я же цикл поменял, вот всё и поплыло, естественно пришлось новые ПИД-ы подбирать:)

Думаю тут надо перестраивать всё, ибо на таком цикле (3500) для проца и для гироакселя по SPI как-то не совсем комфортно? (нечем заняться 😃 )

SergDoc

Заказал наконец-то верхние платы - посмотрим как китайцы справятся с фигурным выпиливанием 😃

так что пока буду заниматься любимой работой - нефиганеделанием, пока ну ни к чему душа не лежит, а на полетать просто уже есть что 😃

Чё-та я задумался, коптер стал более “нервным”, батарейки греть начал! ну и т.д. пропы? что те 12/4.5 что другие 12/4.5 (отбалансированы) всё-же видать время цикла так повлияло… надо поэкпериментировать… там где кино я ставил 1500, а вчера 3500…

rual
oleg70:

это оказывается COMport на компьютере глючит ! (во как)

Управление потоком отключите

SergDoc:

с гирами беда какая-то - дёргаться даже в акро стал

Если раскачка плавная, то делай меньше П и побольше Д. Если же дерганья “нервные”, то делай меньше Д, происходит резкое перерегулирование по угловой скорости, при твоих винтах большой Д = высокие нагрузки на моторы (причем зарежимные, с низким КПД ВМГ),с вытекающими последствиями. Кроме того, если ты увеличил цикл обсчёта, то соответственно dt дифф. части тоже увеличилось (если ПО ПИДа это учитывает) и её вес возрос. Короче пробуй меньший Д.

mahowik:

а вот аксель в mpu-ке получше, это да…

Дык в нём весь корень зла для автоматики!

SergDoc
rual:

Короче пробуй меньший Д.

ну так я и поступил, но с циклом надо повозиться… а правильнее наверно сделать как в арду, привязать цикл к прерываниям от MPU, ибо батарейки не с проста греться стали…

rual
SergDoc:

но с циклом надо повозиться…

вот ещё подумалось: длинный цикл - высокие “ступеньки” в регулировании, тоже источник дерготни.

SergDoc

это да, да и фильтры надо пересматривать, думаю надо время задать примерно 1200 - 1300, с учётом того, что гироаксель читается быстрее, да частота проца высока…
думаю будет пару свободных вечеров, тебе верхнюю плату с твоим gps нарисовать? как разъём на антенну обзывается?

rual
SergDoc:

думаю будет пару свободных вечеров, тебе верхнюю плату с твоим gps нарисовать?

если нет “творческого порыва”, то не надо, пока она не к спеху. Надоть сначала основу облетать, жду комплектухи, а пока подбиваю теоретическую базу (книжек вумных набрал), учу математику…

SergDoc:

как разъём на антенну обзывается?

не знаю, завтра уточню… а может кераму сразу на плату припаять?

SergDoc

для неё дырка на плате нужна, а значит сам модуль где-то снизу - не не выход, ну можно выкинуть MicroSD? либо “отодвинуть” питатели ближе к SPI на их место модуль, а на место модуля антенну, но думаю лучше выносную?

 currentTime = micros();
    if (mcfg.looptime == 0 || (int32_t)(currentTime - loopTime) >= 0) {
        loopTime = currentTime + mcfg.looptime;

        computeIMU();
        // Measure loop rate just afer reading the sensors
        currentTime = micros();
        cycleTime = (int32_t)(currentTime - previousTime);
        previousTime = currentTime;

короче просто изменение скорости петли опроса датчиков, как следствие нужно лишь вписаться чтобы маг с баро успевал прочитать если надо, а гироаксель успеет в любом случае, а так да происходит завышение D…

SergDoc

Александр mahowik меня тут озадачил, новым вийным ПИД-ом , полез я сравнивать и короче наткнулся вот на что:

// **** PITCH & ROLL & YAW PID ****
        prop = max(abs(rcCommand[PITCH]), abs(rcCommand[ROLL])); // range [0;500]
        for (axis = 0; axis < 3; axis++) {
            if ((f.ANGLE_MODE || f.HORIZON_MODE) && axis < 2) { // MODE relying on ACC
                // 50 degrees max inclination
                errorAngle = constrain(2 * rcCommand[axis] + GPS_angle[axis], -500, +500) - angle[axis] + cfg.angleTrim[axis];
                PTermACC = (int32_t)errorAngle * cfg.P8[PIDLEVEL] / 100; // 32 bits is needed for calculation: errorAngle*P8[PIDLEVEL] could exceed 32768   16 bits is ok for result
                PTermACC = constrain(PTermACC, -cfg.D8[PIDLEVEL] * 5, +cfg.D8[PIDLEVEL] * 5);

                errorAngleI[axis] = constrain(errorAngleI[axis] + errorAngle, -10000, +10000); // WindUp
                ITermACC = ((int32_t)errorAngleI[axis] * cfg.I8[PIDLEVEL]) >> 12;
            }
            if (!f.ANGLE_MODE || f.HORIZON_MODE || axis == 2) { // MODE relying on GYRO or YAW axis
                error = (int32_t)rcCommand[axis] * 10 * 8 / cfg.P8[axis];
                error -= gyroData[axis];

                PTermGYRO = rcCommand[axis];

                errorGyroI[axis] = constrain(errorGyroI[axis] + error, -16000, +16000); // WindUp
                if (abs(gyroData[axis]) > 640)
                    errorGyroI[axis] = 0;
                ITermGYRO = (errorGyroI[axis] / 125 * cfg.I8[axis]) >> 6;
            }
            if (f.HORIZON_MODE && axis < 2) {
                PTerm = ((int32_t)PTermACC * (500 - prop) + (int32_t)PTermGYRO * prop) / 500;
                ITerm = ((int32_t)ITermACC * (500 - prop) + (int32_t)ITermGYRO * prop) / 500;
            } else {
                if (f.ANGLE_MODE && axis < 2) {
                    PTerm = PTermACC;
                    ITerm = ITermACC;
                } else {
                    PTerm = PTermGYRO;
                    ITerm = ITermGYRO;
                }
            }

            PTerm -= (int32_t)gyroData[axis] * dynP8[axis] / 10 / 8; // 32 bits is needed for calculation
            delta = gyroData[axis] - lastGyro[axis]; // 16 bits is ok here, the dif between 2 consecutive gyro reads is limited to 800
            lastGyro[axis] = gyroData[axis];
            deltaSum = delta1[axis] + delta2[axis] + delta;
            delta2[axis] = delta1[axis];
            delta1[axis] = delta;
            DTerm = ((int32_t)deltaSum * dynD8[axis]) >> 5; // 32 bits is needed for calculation
            axisPID[axis] =  PTerm + ITerm - DTerm;
        }

и хде тут время цикла? - короче я как всегда запутался 😃

Таймкоп по ходу готовит 5-ю ревизию платы - обзавёлся SPI и кварц уже на 12мГц (интересно что за проц) и не понятно из каких побуждений использует РС13, РС14 для прерываний датчиков? на 4-й ревизии у него было PB12, PB13 и ничего путного из этого не вышло…

догнал оно же само в петле 😃

впихнул новый ПИД на выходных проверю…

rual
SergDoc:

и хде тут время цикла? - короче я как всегда запутался

В старом нет, а в новом явно присутствует

delta = (delta * ((uint16_t)0xFFFF / (cycleTime >> 4))) >> 6;

SergDoc

Старый в петле самой…
а в этом могу задать время цикла магическое 2048…
подождём вечера/ночи, может mahowik чего умного подскажет 😃
смущает, в отличии от стандартного wii дифференциальная составляющая с плюсом, наверно надо P занизить изначально, короче пропы есть надо экспериментировать 😃

вот этот кусок:

static void pidRewrite(void)
{
    int32_t errorAngle = 0;
    int axis;
    int32_t delta, deltaSum;
    static int32_t delta1[3], delta2[3];
    int32_t PTerm, ITerm, DTerm;
    static int32_t lastError[3] = { 0, 0, 0 };
    int32_t AngleRateTmp, RateError;

    // ----------PID controller----------
    for (axis = 0; axis < 3; axis++) {
        // -----Get the desired angle rate depending on flight mode
        if ((f.ANGLE_MODE || f.HORIZON_MODE) && axis < 2 ) { // MODE relying on ACC
            // calculate error and limit the angle to 50 degrees max inclination
            errorAngle = constrain((rcCommand[axis] << 1) + GPS_angle[axis], -500, +500) - angle[axis] + cfg.angleTrim[axis]; // 16 bits is ok here
        }
        if (axis == 2) { // YAW is always gyro-controlled (MAG correction is applied to rcCommand)
            AngleRateTmp = (((int32_t)(cfg.yawRate + 27) * rcCommand[2]) >> 5);
         } else {
            if (!f.ANGLE_MODE) { //control is GYRO based (ACRO and HORIZON - direct sticks control is applied to rate PID
                AngleRateTmp = ((int32_t) (cfg.rollPitchRate + 27) * rcCommand[axis]) >> 4;
                if (f.HORIZON_MODE) {
                    // mix up angle error to desired AngleRateTmp to add a little auto-level feel
                    AngleRateTmp += (errorAngle * cfg.I8[PIDLEVEL]) >> 8;
                }
            } else { // it's the ANGLE mode - control is angle based, so control loop is needed
                AngleRateTmp = (errorAngle * cfg.P8[PIDLEVEL]) >> 4;
            }
        }

        // --------low-level gyro-based PID. ----------
        // Used in stand-alone mode for ACRO, controlled by higher level regulators in other modes
        // -----calculate scaled error.AngleRates
        // multiplication of rcCommand corresponds to changing the sticks scaling here
        RateError = AngleRateTmp - gyroData[axis];

        // -----calculate P component
        PTerm = (RateError * cfg.P8[axis]) >> 7;
        // -----calculate I component
        // there should be no division before accumulating the error to integrator, because the precision would be reduced.
        // Precision is critical, as I prevents from long-time drift. Thus, 32 bits integrator is used.
        // Time correction (to avoid different I scaling for different builds based on average cycle time)
        // is normalized to cycle time = 2048.
        errorGyroI[axis] = errorGyroI[axis] + ((RateError * cycleTime) >> 11) * cfg.I8[axis];

        // limit maximum integrator value to prevent WindUp - accumulating extreme values when system is saturated.
        // I coefficient (I8) moved before integration to make limiting independent from PID settings
        errorGyroI[axis] = constrain(errorGyroI[axis], (int32_t)-GYRO_I_MAX << 13, (int32_t)+GYRO_I_MAX << 13);
        ITerm = errorGyroI[axis] >> 13;

        //-----calculate D-term
        delta = RateError - lastError[axis];  // 16 bits is ok here, the dif between 2 consecutive gyro reads is limited to 800
        lastError[axis] = RateError;

        // Correct difference by cycle time. Cycle time is jittery (can be different 2 times), so calculated difference
        // would be scaled by different dt each time. Division by dT fixes that.
        delta = (delta * ((uint16_t)0xFFFF / (cycleTime >> 4))) >> 6;
        // add moving average here to reduce noise
        deltaSum = delta1[axis] + delta2[axis] + delta;
        delta2[axis] = delta1[axis];
        delta1[axis] = delta;
        DTerm = (deltaSum * cfg.D8[axis]) >> 8;

        // -----calculate total PID output
        axisPID[axis] = PTerm + ITerm + DTerm;
    }
Razek

А мне тут пришел наконец HMC5983. Буду эксперементировать с вынесением датчика на шелдвоном проводе

rual
Razek:

Буду эксперементировать с вынесением датчика

Что хотите выяснить? Что не нравилось когда компас на плате?

Razek

Да в принцепе устраивает более мене, плывет немного при перегазовках. Просто хочу узнать на своем опыте какая разница.
Вообщем чисто академический интерес.

SergDoc

Так как mahowik отдыхает в бане, приходится общаться через личку, он мне объяснил про новый ПИД, ну и дабы не устраивать глухой телефон, разглашу тайную переписку:

SergDoc

вроде догнал, он не отвязан от времени цикла, а масштабируется по времени цикла - я правильно понял?

mahowik

если ты про новую имплементацию, то да

в классическом пид регуле два параметра из трех зависимы от dt

I * dt - умножение т.к. интегрирование/сложение
D / dt - деление т.к. дифферинцирование

вот по простому о непонятном написано 😉 pidcontrol.narod.ru

SergDoc

Я в принципе понял в чём камень преткновения, и почему сделано так, а не иначе.
Я представлял себе так: есть возмущение - которое нужно погасить D составляющей - завышающей пропорцию, потом пропорцией (от этого возмущения) вернуть в исходное положение - естественно сгладив/подтянув I составляющей, но такой метод не подходит, ибо надо практически предсказать какое возмущение будет в момент “запуска” D составляющей. По этой причине используется более простое представление, где пропорцией возвращаем в нужное положение, D составляющая служит как “тормоз” от переруливания самой P ну а интегральная составляющая дотягивает до нужного положения “медленно но верно” без переруливания. По сей причине и формула выглядит так P+I-D, вот и смутило что в новом ПИДе:

// -----calculate total PID output
        axisPID[axis] = PTerm + ITerm + DTerm;

как если бы было как в примере который я привёл выше…

mahowik

посмотрел код… короче и там и там знак минус 😉

просто в одном случае расчет D части ведется по угловой скорости гиры,

delta          = imu.gyroData[axis] - lastGyro[axis];

а в другом из ошибки по гире, а она в инверте

RateError = AngleRateTmp  - imu.gyroData[axis];
....
delta          = RateError - lastError[axis];

т.е. и там и там D работает на торможение 😉