Hallsensor zur Positionsbestimmung eines BLDC

Hallo liebe Freunde der Technik,

in meinem aktuellen Projekt und komme mit meinem informatischen Latein an meine Grenze. Nach dem ich bisher auf fast jede Arduino-Frage in diesem Forum eine Antwort gefunden habe, nun aber nicht mehr weiter komme , verfasse ich jetzt ganz offiziell meinen ersten Post hier und würde mich über eure Hilfe freuen.

Eine kleine Übersicht:
In meinem Projekt würde ich gerne einen Brushless DC 12V 30A Motor ansteuern. Dieser verfährt eine Spindel von Pos. A zu Pos. B.
Bisher klappt dies auch ganz gut. Ich verwende einen Arduino Mega und eine IBT_2 H-Bridge zum Ansteuern und beidseitigen Verfahren.Weil ich mich langsam an die Thematik herantaste wird der Motor aktuell noch über einen 10k Ohm Poti geregelt und manuell verfahren.

Da ich die Bewegung gerne per Knopfdruck ausführen möchte, bediene ich mich an den Signalen der integrierten Hall Sensoren. Zu diesen besitze ich leider kein Datenblatt und diese sind verdeckt verbaut. Das High Low Signal konnte ich allerdings nach ein wenig rumprobieren ausfindig machen.

Angeschlossen nach dem diesem Prinzip

bekomme ich bei der Motorbewegung Abwechselnd ein HIGH oder LOW Signal. Da beide Sensoren scheinbar Winkelversetzt verbaut sind ist auch eine Richtungsbestimmung möglich, je nachdem wessen Flanke als erstes Abfällt).

So weit so gut. Kalibriere ich jetzt jedoch eine Null-Position (z.B. am Anschlag), verfahre den Motor und kehre zur Ausgangsposition zurück. Habe ich eine negative Abweichung von ca 10%. Also werden einige Schritte doppelt oder gar nicht gezählt. Nachdem ich viele Ansätze ausprobiert habe um dieses Problem zu lösen, bin ich nun Ratlos. Das Signal der Hall-Sensoren kommt gleichmäßig an aber nach der Verrechnung passen die Positionen nicht mehr.

Anbei füge ich den Code ein, da ich befürchte der Fehler liegt in der interrupt Funktion mit der ich bisher noch nie gearbeitet habe.

Vielen Dank schonmal für eure Hilfe

const int SENSOR_PIN = A2; // center pin of the potentiometer
const int HALL_PIN1 = 2;
const int HALL_PIN2 = 3;
const int Bauf = 22; //B=Button
const int Bzu = 24;
const int Bcal = 26;
const int RPWM_Output = 9; // Arduino PWM output pin 5; connect to IBT-2 pin 1 (RPWM)
const int LPWM_Output = 10; // Arduino PWM output pin 6; connect to IBT-2 pin 2 (LPWM)

int BaufLastState = 0;  
int BzuLastState = 0;  
int BcalLastState = 0; 
int BaufState = 0;  
int BzuState = 0;  
int BcalState = 0;  
volatile int counter = 0;
 
void setup(){
  TCCR2B = TCCR2B & 0b11111000 | 0x01;
  Serial.begin (9600);
  pinMode(RPWM_Output, OUTPUT);
  pinMode(LPWM_Output, OUTPUT);
  pinMode(HALL_PIN1, INPUT);
  pinMode(HALL_PIN2, INPUT);
  pinMode(Bauf, INPUT);
  pinMode(Bzu, INPUT);
  pinMode(Bcal, INPUT);
  attachInterrupt(digitalPinToInterrupt(HALL_PIN1),Bewegung,FALLING);

}
 
void loop(){
    //Verfahren des Motors
  int POTI = analogRead(SENSOR_PIN);
  // sensor value is in the range 0 to 1023
  // the lower half of it we use for reverse rotation; the upper half for forward rotation
  if (POTI < 512) {
    // reverse rotation
    int reversePWM = -(POTI - 511) / 2;
    analogWrite(LPWM_Output, 0);
    analogWrite(RPWM_Output, reversePWM);
  }
  else  {
    // forward rotation
    int forwardPWM = (POTI - 512) / 2;
    analogWrite(LPWM_Output, forwardPWM);
    analogWrite(RPWM_Output, 0);
  }
    //Ausgabe Informationen
    Serial.print("POTI ");
    Serial.print(POTI); 
    Serial.print("\t");   
    Serial.print("Position ");  
    Serial.print(counter);
    Serial.print("\t"); 
    Serial.print("Buttonstate ");
    Serial.println(BcalState); 
//Knopf 0 Position
    BcalState = digitalRead(Bcal);
    if (BcalState != BcalLastState) {  // if the state has changed, increment the counter
    if (BcalState == HIGH) {           //  if the current state is HIGH then the button went from off to on
    counter=0;
    }
   }
  BcalLastState = BcalState;
}

void Bewegung() {
   volatile int HALL2 = digitalRead(HALL_PIN2);
   if (HALL2==0){
     counter--; 
    }
  else{
  counter++; 
    } 
}

Und bitte entschuldigt die unschöne Programmierung :-X

Sehe ich das richtig, daß Du den dreiphasigen BLDC als zweiphasigen Schrittmotor ansteuern möchtest? Das funktioniert zumindest dort nicht gut, wo die dritte Spule die Weiterbewegung übernehmen müßte. Zumindest konnte ich im Code keine 3 Signale für den BLDC finden.

Das Link führt leider ins Leere, deshalb kann ich zu der Schaltung nichts sagen.

Im Code verwendest Du "volatile" nicht richtig. Im ISR ist HALL2 nicht volatile. Bei counter ist das richtig, der wird ja asynchron vom ISR geändert, nur sollte diese Variable dann mit abgeschalteten Interrupts gelesen werden. Der Rest ist mir ziemlich unverständlich, paßt zu keinem mir bekannten Motortyp.

Hallo DrDiettrich,

vielen Dank für deine Antwort. Dann habe ich mich wahrscheinlich in der Motorbeschreibung geirrt. Der Motor besitzt auch nur zwei Kabel zum Ansteuern. Mir wurde während der Übergabe gesagt >>im einfachsten Fall 12V drauf und für den Rückwärtsgang umpolen. Drosseln über ein PWM Signal<<
Ich weiß, dass diese Aussage nur beinahe so Informativ wie ein Datenblatt ist aber was will man machen ;D.

Den Code bezüglich der Sensorauslesung habe ich wie vorgeschlagen geändert, dies zieht leider keine Veränderung mit sich.

void Bewegung() {
    noInterrupts();
   int HALL2 = digitalRead(HALL_PIN2);
   if (HALL2==0){
     counter--; }
  else{
     counter++;   
 interrupts();}
}

TheSheepoo:
Der Motor besitzt auch nur zwei Kabel zum Ansteuern. Mir wurde während der Übergabe gesagt >>im einfachsten Fall 12V drauf und für den Rückwärtsgang umpolen. Drosseln über ein PWM Signal<<

Dann ist es kein BLDC. Ich tippe auf einen Gleichstrommotor mit Dauermagneterregung und Bürsten.
Wenn Du einen Treiber hast, der noch nicht durchgebrannt ist, stimmt das wenigstens.

Grüße Uwe

Du hast den falschen Teil Deines Codes geändert. Im ISR reicht es, das "volatile" zu streichen, die Abschaltung der Interrupts muß vor

   Serial.print(counter);

erfolgen.

Zudem schließe ich mich der Meinung an, daß Du garkeinen BLDC hast, sondern einen DC Motor mit eingebautem Encoder.

Hallo,

deine Methode zur Richtungserkennung funktioniert so nicht. Wenn da nur 2 Sensoren irgendwo und irgendwie verbaut wären, kann man die Drehrichtung nicht erfassen. Zudem du auf Sensor 1 triggerst und Sensor 2 abfragst. Wie sollen die (angenommenen) versetzten Sensoren jemals zeitgleich geschalten sein? Geht nicht.

Im Besten Fall hast du einen Encoder dran wovon du noch nichts weißt. Diese Encodersignale musst nur richtig einlesen und auswerten. Dann stimmt alles was du möchtest. Schrittzähler und Drehrichtung. Stichwort Graycode. Wenn du zum Motor kein Datenblatt findest, wonach du suchen könntest, hänge ein Oszi oder Datalogger an die Sensoren. Dann erkennst du ob es ein Graycode und damit ein Encoder ist oder nicht.

Die Abfrage eines Encoders kann durchaus von einem Sensor getaktet werden. Bei einem Motor ändert sich die Drehrichtung nicht unkontrolliert, da reicht sogar die Triggerung auf eine Flanke. Kritisch wird das nur beim Stillstand, dafür müssen beide Flanken ausgewertet werden.

Hallo,

das mit der Flankentriggerung reicht leider nicht, selbst wenn man die "richtige" nimmt. Dann ist das Ergebnis immer Drehzahlabhängig und wie schnell der µC ist. Denn in dem Moment des einen Interrupts dreht sich der Encoder weiter und das auslesen der beiden Phasensignale ist nicht mehr das was man eigentlich haben möchte. Weil das einlesen beider verzögert abläuft. Zudem der TO noch die Schritte benötigt. Bei Stillstand gibts zudem keine Flanken. Nur Pegel. :slight_smile: Wenn man es richtig machen möchte, muss man beide Signale zeitgleich einlesen. Dazu bedarf es keiner Flankenerkennung. Man muss nur schnell genug zyklisch beide Pegel einlesen.

Beide Encoder-Signale ändern sich mit der gleichen Frequenz, nur phasenverschoben. Wenn also die Flanke des zweiten Signals so schnell kommt, daß sie im ISR verschlabbert wird, dann kommt auch die nächste Flanke des ersten Signals so schnell, daß die Interruptverarbeitung nicht mehr funktioniert. Das kann aber nur bei Drehzahlen über 50000/s erfolgen, die kein Motor mitmacht.

Im Stillstand kann Spiel im Getriebe und die angekoppelte Last zu kleinen Schwingungen führen. Die kommen erfahrungsgemäß genau dann, wenn man nicht damit rechnet :frowning:

DrDiettrich:
Im Stillstand kann Spiel im Getriebe und die angekoppelte Last zu kleinen Schwingungen führen. Die kommen erfahrungsgemäß genau dann, wenn man nicht damit rechnet :frowning:

Du hast Murphys Law gut angewandt :wink:

Gruß Tommy

Da der TO den Motor ja auch ansteuert braucht es keine Richtungskontrolle durch den Sensor weil die Richtung ja bereits gegeben ist.
Sicher kann Spiel im Getriebe Probleme bereiten aber der TO sagt nichts von Getrieben sondern läßt auf einenMotor mit angeflanschten Encoder tippen.

Kommen wir auf das Problem des TO zurücke der bemängelt daß sein Mitzählen der Impulse des Encoders ungenu ist.

@TheSheepoo bist Du sicher daß eine int-Variable reicht ( mögliche Werte von -32678 bis +32767) und Du keien unsigned Long nehmen mußt?

Grüße Uwe

Hallo,

die zweite Phase ist nur um eine viertel Periodendauer verschoben zur ersten. Das zur Vollständigkeit.

Das schwingen im Stillstand o.ä. stört nicht, weil Bit zappeln allgemein nicht stört.

Praktisch fragt man periodisch beide Signale ab. Ohne Interrupt auf ein bestimmtes Signal. Entweder pollt man innerhalb der loop oder man pollt zyklisch mittels Timerunterstützung. Kommt nur darauf an wie langsam die loop werden kann ob mit oder ohne Timer.

Was machst du wenn eine Störung auf deiner Interruptleitung ist? Dann könnte er falsch zählen. Fragt man beide Signale zyklisch ab muss schon etwas grosses passieren das genau in dem Moment auf einer Phase eine Signalstörung ist und sich auf der zweiten zeitgleich gewollt das Signal ändert. Nur dann zählt er falsch.

Das sind zugegeben kleinste Unterschiede in der Abfragemethode die jedoch den Unterschied zwischen sicher funktionieren und nicht machen. Meinste nicht auch?

Erklärungen und funktionierenden Code gibt es bei suche nach Dannegger und Drehgeber.