Digital Servo 333hz + Arduino Pro mini + servo.h + DMP6050 Jitter Problem

milz

Erfahrener Benutzer
#1
Hi

Ich sitz hier jetzt seit locker gesamt 100 Stunden und Versuch rauszufinden warum
meine Servo die ganze Zeit ein Microzittern erzeugen.
Aufbau Arduino Pro, Mini 6DOF mit DMP6050
Strom kommt von einem stabilisierten 5V Netzeil mit 2A.
Ich benutze die Arduino servo lib + dmp6050 lib.

Das Problem liegt irgendwo an den Interrupt Routinen von der DMP6050 lib und dem Hardware Timer für den Servo Puls. Ohne DMP6050 lib oder direkt am Emfänger laufen die Servo (Savöx SC1257TG) einwandfrei.
Sobald ich alles zusammen bringe zittern die bzw zittern nicht mal wirklich aber arbeiten die ganze Zeit.
Ich hab mich jetzt komplett in die PWM Programmierung und das Hardware Timer Zeugs eingelesen und schon an den Corelibs von Arduino geschraubt alles ohne Erflog.
Diese Geräusch zzzzzzzzzzzzzzzzzzzzzzz treibt mich noch in den Wahnsinn und ich bau mir gleich einen Weltenvernichterextremspecial Laser und verdampf den ganzen Kram ....

Da die Bauteile und Libs ja häufig für Gimbal Steuerungen eingesetzt werden stand vll schon mal jemand vor dem gleichen Problem und hat eine Lösung für mich ?

mfg milz
 
Zuletzt bearbeitet:

milz

Erfahrener Benutzer
#4
Hi

Kann ich nicht genau sagen. hatte vorher die Servo Lib von Arduino benutzt.
Ich habe kein Oszi (werd mir aber jetzt mal eins leisten ). Daher kann ich nur raten.
Hatte auf jedenfall was mit dem timing zu tun. Der alte Code hat mit analogen und
langsamen digital Servo funktioniert. Sobald ich aber mehr als 200hz haben wollte haben die
Servo angefangen zu zittern.
Das hier hat mir sehr geholfen.
https://github.com/noobee/FlightStab
Hab mir jetzt da den Code für die Servos enlehnt aber das auslesen der MPU über
DMP eingebaut. Hab mir das Timing per micros ausgeben lassen und dann versucht
das alles "syncron" zu bekommen.

Ich bin kein großer C coder. C#,AS3,PHP ist meine Welt ...
Von daher ist das alles bischen zusammenbgestückelt..
Aber es läuft -))) Meiner Meinung nach sogar recht gut und flott.
Und irgendwie hat mich das Coden direkt auf dem Atmel angefixet.
Hol mir jetzt erstmal ein Oszi und dann werde ich mein C Kenntnisse etwas vertiefen ...

Ich bastel noch bischen am Code... aufräumen, kommentieren und den Code für die IR
steuerung für cameras rein dann wird das opensource... btw ist eh schon den sing lalal
Es ist alles nur geklaut, das ist alles garnicht mein code ...

P.S Die Dx6 war de erste Funke die gekauft habe. Würde ich heute nicht mehr machen.
allerdings nicht weil ich nicht zufrieden aber 6 Channel sind einfach nicht genug -)
Min. 1x 3 Wege und eine Drehpoti sollte die Funke haben... Warte gerade auf meine 9xr...

mfg milz
 

gfunk

Erfahrener Benutzer
#5
Hi

Kann ich nicht genau sagen. hatte vorher die Servo Lib von Arduino benutzt.
Ich habe kein Oszi (werd mir aber jetzt mal eins leisten ). Daher kann ich nur raten.
Hatte auf jedenfall was mit dem timing zu tun. Der alte Code hat mit analogen und
langsamen digital Servo funktioniert. Sobald ich aber mehr als 200hz haben wollte haben die
Servo angefangen zu zittern.
Das hier hat mir sehr geholfen.
https://github.com/noobee/FlightStab
Hab mir jetzt da den Code für die Servos enlehnt aber das auslesen der MPU über
DMP eingebaut. Hab mir das Timing per micros ausgeben lassen und dann versucht
das alles "syncron" zu bekommen.

Ich bin kein großer C coder. C#,AS3,PHP ist meine Welt ...
Von daher ist das alles bischen zusammenbgestückelt..
Aber es läuft -))) Meiner Meinung nach sogar recht gut und flott.
Und irgendwie hat mich das Coden direkt auf dem Atmel angefixet.
Hol mir jetzt erstmal ein Oszi und dann werde ich mein C Kenntnisse etwas vertiefen ...

Ich bastel noch bischen am Code... aufräumen, kommentieren und den Code für die IR
steuerung für cameras rein dann wird das opensource... btw ist eh schon den sing lalal
Es ist alles nur geklaut, das ist alles garnicht mein code ...

P.S Die Dx6 war de erste Funke die gekauft habe. Würde ich heute nicht mehr machen.
allerdings nicht weil ich nicht zufrieden aber 6 Channel sind einfach nicht genug -)
Min. 1x 3 Wege und eine Drehpoti sollte die Funke haben... Warte gerade auf meine 9xr...

mfg milz

danke für die info!

(dx6i war auch meine erste und hat mich 2 es gekostet, reichweite ist misserabel ;))
 

milz

Erfahrener Benutzer
#6
hier schonmal mein gewerkel -)) c coder aller länder man möge mir verzeihen -)
Code:
bool debugit = 0;
#include <avr/eeprom.h>
#include <util/atomic.h>
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps21.h"
#include "PinChangeInt.h"

#define F_8MHZ 8000000UL
#define F_16MHZ 16000000UL

// <VR>
//#define AIN_PORTC {&ail_vr, &ele_vr, &rud_vr, NULL, NULL, NULL}

#define PWM_OUT_VAR {&sv_nick_out, &sv_roll_out, &sv_yaw_out, NULL}
#define PWM_OUT_PIN {NICK_OUT_PIN, ROLL_OUT_PIN, YAW_OUT_PIN, -1}

// <SERVO>

#define NICK_OUT_PIN 5
#define ROLL_OUT_PIN 6
#define YAW_OUT_PIN 7

#define F_XTAL F_16MHZ // external crystal oscillator frequency
#define F_CPU  F_16MHZ

// adc
//volatile uint8_t ail_vr = 128;
//volatile uint8_t ele_vr = 128;
//volatile uint8_t rud_vr = 128;
uint16_t PWM_MIN_N = 800;
uint16_t PWM_MIN_R = 800;
uint16_t PWM_MIN_Y = 800;

uint16_t PWM_MAX_N = 2200; 
uint16_t PWM_MAX_R = 2200; 
uint16_t PWM_MAX_Y = 2200; 

float DEG_MIN_N = -67.5;
float DEG_MIN_R = -67.5;
float DEG_MIN_Y = -67.5;

float DEG_MAX_N = 68.5;
float DEG_MAX_R = 68.5;
float DEG_MAX_Y = 68.5;
// servo
volatile int16_t sv_roll_out;
volatile int16_t sv_nick_out;
volatile int16_t sv_yaw_out;

int i ;
int old_setPWM1 = 0;
int old_setPWM2 = 0;
int16_t sv_roll_out2, sv_nick_out2, sv_yaw_out2;
uint32_t t;
volatile int8_t servo_sync = true;
bool initServos = true;
#define AD0 high = 0x69
float isDeg, setDeg, sollDeg,offset,offset2;
int16_t setPWM1,setPWM2;

// RC Channels
#define RCP1 10
#define RCP2 11
#define RCP3 12

// Neutral PWM 
#define NE_PPM1 1500
#define NE_PPM2 1500 
#define NE_PPM3 1500 

volatile int nPPMIn = NE_PPM1; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile int nPPM1In = NE_PPM1; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile int nPPM2In = NE_PPM2; // volatile, we set this in the Interrupt and read it in loop so it must be declared volatile
volatile unsigned long ulStartPeriod = 0; // set in the interrupt
volatile boolean bNewPPM1Signal = false; // set in the interrupt and read in the loop
volatile boolean bNewPPM2Signal = false; // set in the interrupt and read in the loop
int bNewPPMSig;
int count = 0;
uint8_t latest_interrupted_pin;
uint8_t interrupt_count[20]={0}; // 20 possible arduino pins
void quicfunc() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  interrupt_count[latest_interrupted_pin]++;
 // Serial.println(" Call input");
  calcInputPPM1();

}
void quicfunc2() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  interrupt_count[latest_interrupted_pin]++;
  calcInputPPM2();
}

/***************************************************************************************************************
 * MPU
 ***************************************************************************************************************/
MPU6050 mpu;
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

Quaternion q;           // [w, x, y, z]         quaternion container
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector


volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}

/***************************************************************************************************************
 * TIMER1 and MISC
 ***************************************************************************************************************/

volatile uint16_t timer1_high = 0;
volatile uint8_t timer1_ovf = 0;

ISR(TIMER1_OVF_vect)
{
  if (!++timer1_high)
    timer1_ovf++;
}

uint32_t micros1()
{
  uint16_t th, tl;
  uint8_t to;
#if 0
  // interrupt-disable version
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    tl = TCNT1; // tick=0.5us if F_CPU=16M, tick=1.0us if F_CPU=8M 
    th = timer1_high;
    to = timer1_ovf;
  }
#else
  // lock-free version. read twice, ensure low order counter did not overflow and 
  // high order bits remain unchanged
  while (1) {
    tl = TCNT1; // tick=0.5us if F_CPU=16M, tick=1.0us if F_CPU=8M 
    th = timer1_high;
    to = timer1_ovf;
    if (!((tl ^ TCNT1) & 0x8000) && th == timer1_high && to == timer1_ovf)
      break;    
  }  
#endif
#if F_CPU == F_16MHZ
  return ((uint32_t)to << 31) | ((uint32_t)th << 15) | (tl >> 1);
#else
  return ((uint32_t)th << 16) | tl;
#endif
}

void delay1(uint32_t ms)
{
  uint32_t us = ms * 1000; 
  uint32_t t = micros1();
  while ((int32_t)(micros1() - t) < us);
}

int freeRam() 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}



/***************************************************************************************************************
 * DIGITAL OUT (SERVO)
 ***************************************************************************************************************/

volatile int16_t *pwm_out_var[] = PWM_OUT_VAR;
volatile int8_t pwm_out_pin[] = PWM_OUT_PIN;
volatile int8_t servo_busy = false;

ISR(TIMER1_COMPA_vect)
{
  static int8_t fall_ch = 0;
  static int8_t rise_ch = 0;
  uint16_t wait;
  uint16_t tcnt1;
 
  digitalWrite(pwm_out_pin[fall_ch], LOW);
  if (rise_ch >= 0) {
    tcnt1 = TCNT1;
    digitalWrite(pwm_out_pin[rise_ch], HIGH);
    wait = (*pwm_out_var[rise_ch] - 2) << (F_CPU == F_16MHZ ? 1 : 0);
    fall_ch = rise_ch;
    if (!pwm_out_var[++rise_ch]) {
      rise_ch = -1;
    }
  } else {
    rise_ch = 0;
    servo_busy = false;
    TIMSK1 &= ~(1 << OCIE1A); // disable further interrupt on TCNT1 == OCR1A
  }
  OCR1A = tcnt1 + wait;
}

void init_digital_out()
{
  int8_t i = 0;
  while (pwm_out_pin[i] >= 0) {
    pinMode(pwm_out_pin[i], OUTPUT);
    i++;
  }
} 


/***************************************************************************************************************
 * RX IN - MIXER - SERVO OUT
 ***************************************************************************************************************/



void start_servo_frame(int svout,int servo_on)
{
  
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
 // 
   servo_busy = true;
    switch(servo_on){
    case 1: sv_nick_out = svout;  break;
    case 2: sv_roll_out = svout; break;
    case 3: sv_yaw_out = svout; break;
    }
    
 //   Serial.print(gRoll); Serial.print("\t");
  //  Serial.println(ele_out);
    TIMSK1 |= (1 << OCIE1A); // enable interrupt on TCNT1 == OCR1A
    TIFR1 |= (1 << OCF1A); // clear any pending interrupt
    OCR1A = TCNT1 + 4; // force interrupt condition
  } // reenable global interrupts
 servo_busy = false;
}


void setup() 
{
  Wire.begin();
  Serial.begin(115200);

  Serial.println("Setup");
  
    mpu.initialize();
    Serial.println(F("Testing device connections..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
    devStatus = mpu.dmpInitialize();
        if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);

        // enable Arduino interrupt detection
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(0, dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();

        // set our DMP Ready flag so the main loop() function knows it's okay to use it
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;

        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
    }
   
int8_t i;
  // set clock prescaler to /2 when (F_XTAL, F_CPU) = (16M, 8M) 
#if F_XTAL == F_16MHZ && F_CPU == F_8MHZ
  uint8_t saveSREG = SREG;
  cli();
  CLKPR = (1 << CLKPCE);
  CLKPR = (1 << CLKPS0);
  SREG = saveSREG;
#endif

  // init TIMER1
  TCCR1A = 0; // normal counting mode
  TCCR1B = (1 << CS11); // clkio/8
  TIMSK1 = (1 << TOIE1); // enable overflow interrupt
//  TIMSK1 |= (1 << OCIE1A); // enable interrupt on TCNT1 == OCR1A

  // disable TIMER0
  TCCR0B &= ~((1 << CS00) | (1 << CS01) | (1 << CS02)); // clock stopped
//  TIMSK0 &= ~(1 << TOIE0); // disable overflow interrupt


  init_digital_out(); // servo
  
    pinMode(RCP1, INPUT);digitalWrite(RCP1, HIGH);
    pinMode(RCP2, INPUT);digitalWrite(RCP2, HIGH);
    PCintPort::attachInterrupt(RCP1,&quicfunc, CHANGE);
    PCintPort::attachInterrupt(RCP2,&quicfunc2, CHANGE);
  
  Serial.print("Setup complete ");

}

/***************************************************************************************************************
 * LOOP
 ***************************************************************************************************************/

void loop() 
{ 

again:
servo_sync = false;
  for (i=0; i < 20; i++) {
    if (interrupt_count[i] != 0) {
      count=interrupt_count[i];
      interrupt_count[i]=0;
    }
  }

if(bNewPPM1Signal){bNewPPM1Signal = false;}
if(bNewPPM2Signal){bNewPPM2Signal = false;}
  
    // if programming failed, don't try to do anything
    if (!dmpReady) {Serial.println("not ready");return;}
   //  while (!mpuInterrupt && fifoCount < packetSize) {}

    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
    fifoCount = mpu.getFIFOCount();

        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        fifoCount -= packetSize;

            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            if(debugit){
            Serial.print("ypr\t");
            Serial.print(ypr[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(ypr[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(ypr[2] * 180/M_PI);
            }
//    Serial.print(nPPM1In);Serial.print("\t");Serial.print(nPPM2In);Serial.print("\t");
            if(initServos)
            { 
             Serial.println("Set init servo postition"); 
             start_servo_frame(1500,1);
             start_servo_frame(1500,2);
             initServos=false;
             Serial.println(initServos); 
             }
           offset=calulate_offset(nPPM1In,offset);
           offset2=calulate_offset(nPPM2In,offset2);
           
            servo_sync = true;   
            setPWM1 = mapf(computeAngle(ypr[2],offset), DEG_MAX_N, DEG_MIN_N, PWM_MIN_N, PWM_MAX_N);
//   Serial.print(offset);Serial.print("\t");Serial.println(setPWM1);
           if (servo_sync && !servo_busy) {if(old_setPWM1!=setPWM1){ start_servo_frame(setPWM1,1);old_setPWM1=setPWM1;}}
//  Serial.print(DEG_MAX_N);Serial.print("\t");Serial.print(DEG_MIN_N);Serial.print("\t");Serial.print(PWM_MIN_N);Serial.print("\t");Serial.print(PWM_MAX_N);Serial.print("\t");Serial.print(val);Serial.print("\t");
           setPWM2 = mapf(computeAngle(ypr[1],offset2), DEG_MAX_R, DEG_MIN_R, PWM_MIN_R, PWM_MAX_R);
           if (servo_sync && !servo_busy) {if(old_setPWM2!=setPWM2){start_servo_frame(setPWM2,2);old_setPWM2=setPWM2;}}
           t = micros1();
goto again;
}


long mapf(float x, float in_min, float in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

float computeAngle(float angleIN, float offsetIN)
{
//angleIN = (float)((int)((angleIN * 180/M_PI)*10))/10; // round floar much load
angleIN = (angleIN * 180/M_PI)+offsetIN;
if(angleIN>=0){setDeg = sollDeg-angleIN;} else {setDeg = sollDeg + abs(angleIN); }
return setDeg;
}

void calcInputPPM1()
{
  if(digitalRead(RCP1) == HIGH)
  {
  ulStartPeriod = micros1();
  }
  else
  {
    if(ulStartPeriod && (bNewPPM1Signal == false))
    {
    nPPM1In = (int)(micros1() - ulStartPeriod);
    ulStartPeriod = 0;
    bNewPPM1Signal = true;
    }
  }
}

void calcInputPPM2()
  {
  if(digitalRead(RCP2) == HIGH)
  {
  ulStartPeriod = micros1();
  }
  else
  {
    if(ulStartPeriod && (bNewPPM2Signal == false))
    {
    nPPM2In = (int)(micros1() - ulStartPeriod);
    ulStartPeriod = 0;
    bNewPPM2Signal = true;
    }
  }
}

float calulate_offset(float nPPMIn,float offsetIN){
           if(nPPMIn>=700 && nPPMIn<=2000)
           {
           if(nPPMIn>=1660 && offsetIN<=45){offsetIN+=0.15;}
           if(nPPMIn>=1720 && offsetIN<=45){offsetIN+=0.3;}
           if(nPPMIn>=1780 && offsetIN<=45){offsetIN+=0.5;}
           if(nPPMIn>=1830 && offsetIN<=45){offsetIN+=1;}
           if(nPPMIn>=1870 && offsetIN<=45){offsetIN+=2;}
           if(nPPMIn>=1910 && offsetIN<=45){offsetIN+=4;}
           
           if(nPPMIn<=1400 && offsetIN>=-45){offsetIN-=0.15;}
           if(nPPMIn<=1340 && offsetIN>=-45){offsetIN-=0.3;}
           if(nPPMIn<=1280 && offsetIN>=-45){offsetIN-=0.5;}
           if(nPPMIn<=1230 && offsetIN>=-45){offsetIN-=1;}
           if(nPPMIn<=1190 && offsetIN>=-45){offsetIN-=2;}
           if(nPPMIn<=1140 && offsetIN>=-45){offsetIN-=4;}
           } 
           return offsetIN;
}
 
FPV1

Banggood

Oben Unten