Prozedur motorControlImuDir()

goonie

Member
Hallo Zusammen,

da ich, wie schon von anderen auch berichtet, Probleme bei der Ansteuerung der Antriebsmotoren hatte, habe ich mir mal die Prozedur motorControlImuDir() angesehen. Dabei ist mir aufgefallen, daß es zurzeit keine Drehzahlregelung (nach RPM) gibt.
Ausserdem wird in setMotorSpeed() die Solldrehzahl (in RPM) als PWM übergeben.
Das hatte bei mir zur Folge, daß nach 3 Sekunden der Robi stehen geblieben ist, weil die zu geringe Soll-PWM nicht ausgereicht hat.

Nun habe ich den Code mal testweise etwas abgeändert (siehe unten). So würde die kompassgeregelte Richtungssteuerung inclusive Drehzahlregelung funktionieren. Die Vorlage zur Drehzahlregelung habe ich der Prozedur motorControl() entnommen und den Parameter imuDirPID.y integriert.

Bei mir funktioniert das super. Quasi Tempomat und Richtungsregelung.
Vielleicht kann das ja mal jemand testen und Feedback geben!

EDIT: Sollte die Geschwindigkeitsregelung zu stark pendeln, dann ist der Proportionalfaktor "double P = 3.0;" zu veringern.

Viele Grüße
Aiko



Code:
// PID controller: correct direction during normal driving (requires IMU)
void Robot::motorControlImuDir(){   
    int motorLeftSetpoint = motorLeftSpeed;
    int motorRightSetpoint = motorRightSpeed;
    int correctLeft = 0;
    int correctRight = 0;
    
    double P = 3.0;

    imuDirPID.x = distancePI(imu.ypr.yaw, imuDriveHeading) / PI * 180.0; 
    imuDirPID.w = 0;
    imuDirPID.y_min = -motorSpeedMax;
    imuDirPID.y_max = motorSpeedMax;		
    imuDirPID.max_output = motorSpeedMax;
    imuDirPID.compute();	  	
  
  if (imuDirPID.y < 0) correctRight = imuDirPID.y;
  if (imuDirPID.y > 0) correctLeft  = imuDirPID.y;

    double motorLeftSpeedE =  (motorLeftSetpoint  - correctLeft)  - motorLeftRpm;          
    double motorRightSpeedE = (motorRightSetpoint + correctRight) - motorRightRpm;  
    int leftSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorLeftPWM + motorLeftSpeedE*P));
    int rightSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm,motorRightPWM + motorRightSpeedE*P));
      
  setMotorSpeed( leftSpeed, rightSpeed, true );   
 }
 
Sieht gut aus! Btw, müsste da nicht 'motorSpeedMaxPwm' statt 'motorSpeedMax' stehen?

...
perimeterPID.y_min = -motorSpeedMaxPwm;
perimeterPID.y_max = motorSpeedMaxPwm;
...

Der Ardumower SVN-Code ist derzeit nicht ganz durchgängig korrekt, wir müssen das korrigieren...
 
Nein, motorSpeedMax ist hier richtig, weil hier die Drehzahl in RPM zur Korrektur benutzt wird.



Code:
double motorLeftSpeedE =  (motorLeftSetpoint  - correctLeft)  - motorLeftRpm;


motorLeftSetpoint = Solldrehzahl (z.B. 33)
abzüglich
correctLeft = abzubremsende Drehzahl dieses Rades (0 - 33 ...je nach Kursabweichung)
abzüglich
motorLeftRpm = Istdrehzahl des Rades
ergibt
motorLeftSpeedE = Abweichung in RPM


Prinzipiell wäre es besser, wenn die Parameter für setMotorSpeed() in RPM übergeben würden.
Das ist nicht konsequent im Code. (...das meinst Du sicherlich damit)
Die Regelung der PWM (in Abhängigkeit ob Odometrie ja oder nein) würde ich dieser Prozedur überlassen.

Es werden im Moment an vielen Stellen SollPWM/1.5 o.ä. vorgegeben (ohne Regelung).

Da können wir uns ja mal rantasten... bin dabei
 
OK, sag Bescheid wenn Du eine saubere Lösung gefunden hast (ich blicke selber kaum noch richtig durch :-/) - finde auch dass da zuviel durcheinander geht. Ich könnte auch gut damit leben wenn wir Odometrie als Voraussetzung für den Ardumower machen.
 
So! Bin zwar mit der Programmierung nicht so fix dabei wie Du, Alex, aber hier mal meine derzeitige Überarbeitung....
Mit motorControlPerimeter() konnte ich mangels Funktionalität noch nicht arbeiten.
Das kommt aber noch. Habe den Sender schon bei Markus abgeholt und aufgebaut.
(PS @Markus: Die Fotos der Motoren sind nicht vergessen...)

motorControl() habe ich Deiner Vorlage mit PID-Regelung aus SVN übernommen. (Danke dafür!)


Code:
void Robot::motorControl(){
  if (odometryUse){
    // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
    motorLeftPID.x = motorLeftRpm;                 // IST 
    motorLeftPID.w = motorLeftSpeed;               // SOLL 
    motorLeftPID.y_min = -motorSpeedMaxPwm;        // Regel-MIN
    motorLeftPID.y_max = motorSpeedMaxPwm;	   // Regel-MAX
    motorLeftPID.max_output = motorSpeedMaxPwm;    // Begrenzung
    motorLeftPID.compute();
    int leftSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorLeftPWM + motorLeftPID.y));
    if((motorLeftSpeed >= 0 ) && (leftSpeed <0 )) leftSpeed = 0;
    if((motorLeftSpeed <= 0 ) && (leftSpeed >0 )) leftSpeed = 0;     

    // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
    motorRightPID.Kp = motorLeftPID.Kp;
    motorRightPID.Ki = motorLeftPID.Ki;
    motorRightPID.Kd = motorLeftPID.Kd;          
    motorRightPID.x = motorRightRpm;               // IST
    motorRightPID.w = motorRightSpeed;             // SOLL
    motorRightPID.y_min = -motorSpeedMaxPwm;       // Regel-MIN
    motorRightPID.y_max = motorSpeedMaxPwm;        // Regel-MAX
    motorRightPID.max_output = motorSpeedMaxPwm;   // Begrenzung
    motorRightPID.compute();            
    int rightSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorRightPWM + motorRightPID.y));
    if((motorRightSpeed >= 0 ) && (rightSpeed <0 )) rightSpeed = 0;
    if((motorRightSpeed <= 0 ) && (rightSpeed >0 )) rightSpeed = 0;         

    if (  ((stateCurr == STATE_OFF) || (stateCurr == STATE_CHARGE) || (stateCurr == STATE_ERROR)) && (millis()-stateStartTime>1000)  ){
      leftSpeed = rightSpeed = 0; // ensures PWM is zero if OFF/CHARGING
    }
    setMotorSpeed( leftSpeed, rightSpeed, false );  
  }
  else{
    int leftSpeed = min(motorSpeedMaxPwm, max(-motorSpeedMaxPwm, map(motorLeftSpeed, -motorSpeedMax, motorSpeedMax, -motorSpeedMaxPwm, motorSpeedMaxPwm)));
    int rightSpeed =min(motorSpeedMaxPwm, max(-motorSpeedMaxPwm, map(motorRightSpeed, -motorSpeedMax, motorSpeedMax, -motorSpeedMaxPwm, motorSpeedMaxPwm)));
    if (millis() < stateStartTime + 1000) {				
      leftSpeed = rightSpeed = 0; // slow down at state start      
      if (mowPatternCurr != MOW_LANES) imuDriveHeading = imu.ypr.yaw; // set drive heading    
    }
    setMotorSpeed( leftSpeed, rightSpeed, true );    
  }  
}



motorControlImuDir() basiert jetzt ebenso auf der PID-Regelung....



Code:
// PID controller: correct direction during normal driving (requires IMU)
void Robot::motorControlImuDir(){
  int correctLeft = 0;
  int correctRight = 0;
  
  // Regelbereich entspricht maximaler Drehzahl am Antriebsrad (motorSpeedMax)
  imuDirPID.x = distancePI(imu.ypr.yaw, imuDriveHeading) / PI * 180.0;            
  imuDirPID.w = 0;
  imuDirPID.y_min = -motorSpeedMax;
  imuDirPID.y_max = motorSpeedMax;		
  imuDirPID.max_output = motorSpeedMax;
  imuDirPID.compute();	  					      
                 
  if (imuDirPID.y < 0) correctRight = abs(imuDirPID.y);
  if (imuDirPID.y > 0) correctLeft  = abs(imuDirPID.y);
                 
  // Korrektur erfolgt über Abbremsen des linken Antriebsrades, falls Kursabweichung nach rechts
  // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
  motorLeftPID.x = motorLeftRpm;                     // IST 
  motorLeftPID.w = motorLeftSpeed - correctLeft;     // SOLL 
  motorLeftPID.y_min = -motorSpeedMaxPwm;            // Regel-MIN
  motorLeftPID.y_max = motorSpeedMaxPwm;	     // Regel-MAX
  motorLeftPID.max_output = motorSpeedMaxPwm;        // Begrenzung
  motorLeftPID.compute();
  int leftSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorLeftPWM + motorLeftPID.y));
  if((motorLeftSpeed >= 0 ) && (leftSpeed <0 )) leftSpeed = 0;
  if((motorLeftSpeed <= 0 ) && (leftSpeed >0 )) leftSpeed = 0;    

  // Korrektur erfolgt über Abbremsen des rechten Antriebsrades, falls Kursabweichung nach links 
  // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
  motorRightPID.Kp = motorLeftPID.Kp;
  motorRightPID.Ki = motorLeftPID.Ki;
  motorRightPID.Kd = motorLeftPID.Kd;
  motorRightPID.x = motorRightRpm;                   // IST 
  motorRightPID.w = motorRightSpeed - correctRight;  // SOLL 
  motorRightPID.y_min = -motorSpeedMaxPwm;           // Regel-MIN
  motorRightPID.y_max = motorSpeedMaxPwm;	     // Regel-MAX
  motorRightPID.max_output = motorSpeedMaxPwm;       // Begrenzung
  motorRightPID.compute();            
  int rightSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorRightPWM + motorRightPID.y));
  if((motorRightSpeed >= 0 ) && (rightSpeed <0 )) rightSpeed = 0;
  if((motorRightSpeed <= 0 ) && (rightSpeed >0 )) rightSpeed = 0;         
  
  if (  ((stateCurr == STATE_OFF) || (stateCurr == STATE_CHARGE) || (stateCurr == STATE_ERROR)) && (millis()-stateStartTime>1000)  ){
    leftSpeed = rightSpeed = 0; // ensures PWM is zero if OFF/CHARGING
  }
  setMotorSpeed( leftSpeed, rightSpeed, false );                   
                 
}



motorControlImuRoll() basiert jetzt ebenso auf der PID-Regelung....



Code:
// PID controller: roll robot to heading (requires IMU)
void Robot::motorControlImuRoll(){
  
  // Regelbereich entspricht 80% der maximalen Drehzahl am Antriebsrad (motorSpeedMax)
  imuRollPID.x = distancePI(imu.ypr.yaw, imuRollHeading) / PI * 180.0;            
  imuRollPID.w = 0;
  imuRollPID.y_min = -motorSpeedMax/1.25;        // da der Roll generell langsamer erfolgen soll
  imuRollPID.y_max = motorSpeedMax/1.25;	 //	
  imuRollPID.max_output = motorSpeedMax/1.25;    //
  imuRollPID.compute();						      

  // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
  motorLeftPID.x = motorLeftRpm;                 // IST 
  motorLeftPID.w = -imuRollPID.y;                // SOLL 
  motorLeftPID.y_min = -motorSpeedMaxPwm;        // Regel-MIN
  motorLeftPID.y_max = motorSpeedMaxPwm;	 // Regel-MAX
  motorLeftPID.max_output = motorSpeedMaxPwm;    // Begrenzung
  motorLeftPID.compute();
  int leftSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorLeftPWM + motorLeftPID.y));
  //if((motorLeftSpeed >= 0 ) && (leftSpeed <0 )) leftSpeed = 0;
  //if((motorLeftSpeed <= 0 ) && (leftSpeed >0 )) leftSpeed = 0;     

  // Regelbereich entspricht maximaler PWM am Antriebsrad (motorSpeedMaxPwm), um auch an Steigungen höchstes Drehmoment für die Solldrehzahl zu gewährleisten
  motorRightPID.Kp = motorLeftPID.Kp;
  motorRightPID.Ki = motorLeftPID.Ki;
  motorRightPID.Kd = motorLeftPID.Kd;
  motorRightPID.x = motorRightRpm;               // IST   
  motorRightPID.w = imuRollPID.y;                // SOLL
  motorRightPID.y_min = -motorSpeedMaxPwm;       // Regel-MIN
  motorRightPID.y_max = motorSpeedMaxPwm;	 // Regel-MAX	
  motorRightPID.max_output = motorSpeedMaxPwm;   // Begrenzung
  motorRightPID.compute();            
  int rightSpeed = max(-motorSpeedMaxPwm, min(motorSpeedMaxPwm, motorRightPWM + motorRightPID.y));
  //if((motorRightSpeed >= 0 ) && (rightSpeed <0 )) rightSpeed = 0;
  //if((motorRightSpeed <= 0 ) && (rightSpeed >0 )) rightSpeed = 0;         

  if (  ((stateCurr == STATE_OFF) || (stateCurr == STATE_CHARGE) || (stateCurr == STATE_ERROR)) && (millis()-stateStartTime>1000)  ){
    leftSpeed = rightSpeed = 0; // ensures PWM is zero if OFF/CHARGING
  }
  setMotorSpeed( leftSpeed, rightSpeed, false );                   

}




Wichtig waren noch zwei kleine Änderungen in der mower.cpp!
1. Diese auskommentierte Zeile "//&& (mowPatternCurr == MOW_RANDOM)" ist (glaube ich) fehlerhaft, da bei "mowPatternCurr == MOW_LANES" sonst kein ImuDir ausgeführt wird.
2. " //&& (millis() > stateStartTime + 3000)" habe ich rausgenommen, da er nach einer LANE->REV->ROLL erstmal 3 Sekunden unkontrolliert in die Richtung des ROLLs wegmarschierte. Ohne diese Zeile korrigiert er sofort auf die neu Bahn ein.




Code:
if (millis() >= nextTimeMotorControl) {            
    nextTimeMotorControl = millis() + 100;
    // decide which motor control to use
    if ( ((mowPatternCurr == MOW_LANES) && (stateCurr == STATE_ROLL)) || (stateCurr == STATE_ROLL_WAIT) ) motorControlImuRoll();
      else if (stateCurr == STATE_PERI_TRACK) motorControlPerimeter();
      else if (  (stateCurr == STATE_FORWARD)
       //&&  (mowPatternCurr == MOW_RANDOM)
       && (imuUse) 
       && (imuCorrectDir || (mowPatternCurr == MOW_LANES))        
       ) motorControlImuDir();                                   //&& (millis() > stateStartTime + 3000)
      else motorControl();  
  }


Also für Lawn-by-Lane sähe meine Idee so aus, daß er einen 90 Grad ROLL macht und mittels motorControlImuDir automatisch mit einem (inneren) stehenden Rad auf die neue Bahn einschwenkt. Das hat bei mir etliche Bahnen hin und her ganz gut funktioniert. Lediglich die ROLL-Richtung muß ich mir da nochmal angucken, da anscheinend zufällig.
So, erstmal genug Stuff für heute. :whistle:

Viele Grüße :cheer:
Aiko
 
Oben