PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Arduino Leistungsfähig genug? 6 Schrittmotoren + Inkrementalgeber



Falkenauge
09.09.2014, 09:54
Hallo,

folgendes Problem beschäftigt mich zurzeit. Da ich selber noch recht wenig Erfahrung habe, hoffe ich dass einige von euch mir weiter helfen können.

Ich suche momentan einen Weg 6 Schrittmotoren sowie deren Inkrementalgeber zu steuern sowie auszulesen.
Nun liegt das Problem in der Sache, dass ich die Inkrementalgeber gerne über Interrupts auslesen würde, um möglichst keine Schritte zu verpassen. Bei 6 Inkrementalgebern die jeweils ungefähr 7000 mal pro Sekunde ein Signal senden, kommt da einiges zusammen. Ist das direkt auf einem Arduino von der Geschwindigkeit realisierbar?
Desweiteren bräuchte ich ja dann 6 Interrupt pins, allerdings habe ich da noch keinen Weg gefunden, da die Arduinos meistens ja 2 Interruptpins besitzten. Wenn jemand einen Link zu einem Shield hätte, der mir da weiterhilft wäre das echt super.
Welches Arduino Board benutzt wird, steht noch offen, daher haben wir da noch freie Wahl

Ansonsten sollte auf diesem Arduino noch 7 Servomotoren in einem vorher festgelegten Bewegungsablauf gesteuert werden, was ich über eine Maestro Karte realisieren werde.

Die alternative Idee, sollte die Schrittmotorsteuerung nicht möglich sein, wäre diese durch Nanotec Treiber zu realisieren, die sich dann mittels RS485 Bus mit dem Arduino unterhalten. Dies wäre allerdings erheblich teurer mMn.

Gruß,
Sagre

wkrug
09.09.2014, 18:02
Bei den Arduinos sind meines Wissens AVR Controller verbaut.
Einige, vor allem die neueren Typen unterstützen Pin Change Interrupts.
Das bedeutet ein Interrupt wird ausgelöst, sobald sich ein Pegel an einem Pin der Gruppe ändert.
Welcher das nun war müsstest Du in der Interruptroutine selber rausfinden.
Ich fürche auch fast, das das ein ATMEGA auch nicht mehr schafft.
Im Extremfall hast Du ja 7000 x 6 = 42000 Interrupts pro Sekunde.
Auch wenn der Controller mit 20MHz läuft hast Du gerade mal 476 Maschinenzyklen ( Takte ) pro Interrupt.
Das geht mit Assembler sicher noch, mit BASCOM wird das wohl nichts mehr.
Zusätzlich entstehen ja noch weitere Interrupts duch die Servoroutinen.

Wenn dein Programm für den Interrupt fertig ist, kannst Du per Simulator erproben, wieviele Zyklen der Controller für die Abarbeitung der Interruptroutine braucht.

Ob das mit den Servos noch geht oder nicht lässt sich vermutlich nur in der Praxis erproben.
Du könntest ja zum Beispiel einen Port auf 1 setzen, sobald der Controller im Interrupt ist, verlässt der Controller den Interrupt kannst Du diesen Port wieder auf 0 setzen.
Wenn Du dann ein Oszilloskop auf diesen Pin hängst siehst Du, ob der Controller noch Zyklen frei hat.

Falkenauge
10.09.2014, 11:37
Hallo, danke dir erst einmal für deine Antwort.

Ich werde mich ersteimal langsam herantasten, und erst einmal einen Sensor und einen Motor steuern. Leider bin ich sehr unerfahren was Mikokontroller anbegeht, und muss mich ersteinmal für jeden Begriff einlesen. Aber das wird schon

Vermutlich meinst du mit den Interupts einen Pin Change interrupt? Dadrin werde ich mich aufjedenfall mal einlesen, scheint genau das zu sein was ich brauche
Wir sind allerdings jetzt von einem Arduino weg, und versuchen uns gerade an einem Atmega32, der hier noch herumlag.

Da ist bereits bei uns eine Frage aufgetaucht:

Momentan versuchen wir erst einmal die USB-Kommunikation mit einem Rechner aufzubauen, um überhaupt rückmeldungen über die Sensor Daten zu erhalten. Das schicken von Nachrichten vom Atmega zum Rechner (hTerm) funktioniert schonmal super.
Nur leider reagiert der Atmega rein gar nicht auf die Nachrichten des Rechners.
Wir arbeiten mit der UART Library von PeterFleury. Kann uns da evt jemand weiterhelfen?

char UART_RECIEVE(void) {
unsigned int x;
for(;;) {
x= uart_getc();
if(!(x&UART_NO_DATA)) return (char)x;
}
}


in der main sieht es dann so (im Ausschnitt) so aus:

int main() {
while(1){
char c = UART_RECIEVE();
uart_puts(c);
sleep(1000);
}

oberallgeier
10.09.2014, 12:04
... meinst du mit den Interupts einen Pin Change interrupt? ... mal einlesen ... Atmega32, der hier noch herumlag ...Nur mal so als Anmerkung: gerade der ATmega32/ATmega32L hat keinen Pin Change Interrupt, siehe das (vermutlich aktuellste) Datenblatt 2503Q–AVR–02/11. Hier wird der pinkompatible mega1284 Deinen Vorstellungen eher entsprechen, drei externe Interrupts und vier Pingruppen ready for PCI - somit theoretisch sieben (externe, pinbezogene) Interrupts. Selbst beim Übertakten der maximal spezifizierten 20 MHz wirst Du aber feststellen müssen, dass wkrug mit seiner Abschätzung ziemlich gut liegt.


... Wenn dein Programm für den Interrupt fertig ... wieviele Zyklen ... für die Abarbeitung der Interruptroutine ...Die Zyklenzahl kannst Du aus der deincompilat.lls herauslesen. Da drin stehts nämlich haarklein. Ein Beispiel dazu hier. (https://www.roboternetz.de/community/threads/65554-Atmega-Prozessorauslastung-feststellen-und-Fehlerhandling?p=604280&viewfull=1#post604280)

malthy
10.09.2014, 13:23
Hi!

Vermutlich geben die Inkrementalgeber ein Quadratursignal aus. Es ist im Allgemeinen nicht die beste Lösung, dieses per ext. Interrupts (INTx/PCINT) auzuwerten (siehe: http://www.mikrocontroller.net/articles/Drehgeber). Unter gewissen Randbedingungen (insb. Encocder prellt nicht, weil magnetischer Encoder) mag das dennoch eine Variante sein. Grundsätzlich sollte man die Encodersignale besser per Timerinterrupt mit festem Intervall abtasten und auswerten. Dann müsstest Du pro Timer-ISR Aufruf dann eben alle Encoder auswerten. Die Auswertung ist nicht sonderlich rechenintensiv. Wenn Du Dir sicher bist dass die 7k Ticks/s reichen, sollte das grundsätzlich mit einem AVR zu machen sein. Ob das High-Level mit dieser Arduino Sprache hinhaut, kann ich nicht beurteilen, damit kenne ich mich nicht aus. Das wäre ansonsten ein klassischer Fall für Assembler. Zum Festlegen der Abtastrate solltest Du brücksichtigen, dass das Quadratursignal in den seltensten Fällen wirklich symmetrisch ist, zur Sicherheit könnte man größenordnungsmäßig mit Faktor zwei Abtasten.

Gruß
Malte

Schachmann
10.09.2014, 23:42
Hallo,

ich muss malthy Recht geben. Die Auswertung per Pin-Change-Interrupt ist nicht zu empfehlen. Für Dein Problem solltest Du den oder die Encoder im Timer-Interrupt mit einer festen Frequenz abfragen. Mir ist allerdings nicht ganz klar, ob Du jetzt nur einen AVR-Controller oder aber wirklich einen Arduino benutzt. Falls Du den Arduino nimmst, könntest Du einmal hier nachlesen: http://www.meinduino.de/drehencoder.shtml. Da ist in Teil I die Auswertung per Polling und in Teil II die Auswertung des Encoders im Timer-Interrupt beschrieben. Vielleicht gelingt es Dir, den Sketch im II. Teil an Deine Bedürfnisse anzupassen.

Gruß,
Ralf

oberallgeier
11.09.2014, 09:54
... Vermutlich geben die Inkrementalgeber ein Quadratursignal ... im Allgemeinen nicht die beste Lösung, dieses per ext. Interrupts ... auzuwerten ...Die Frage ist ja, ob es Sinn macht das komplette Quadratursignal (wenn es eins ist) auszuwerten.

......http://a.pololu-files.com/picture/0J2643.200.jpg?954dd7f1ce65b04b95900587e35eaf20

Warum? Weil ich einigermassen sicher bin, dass der Versatz der Signalflanken nicht genau gleich ist (so lange ich den Encoder nicht selbst hergestellt oder ausführlich getestet habe). Dann kommt eine Ungenauigkeit rein, die sich bei hohen Drehzahlen - und den damit verbundenen wenigen Timerticks schon wesentlich auf die Regelung auswirken kann. Hier wäre zu überlegen, ob man nicht (nur) jede gleichsinnige Flanke einer Encoderspur auswertet. Wenns nicht gerade eine hochgenaue Drehzahlregelung für etliche zehntausend Touren sein soll dürfte das reichen und möglicherweise eine genauere Regelungsgrundlage liefern als die Auswertung aller Flanken.

Die Lösung mit dem externen Interrupt ist nicht die schlechteste. Ich fahre bei meinem Dottie, beim MiniD0 und beim Archie, alles aber KEINE Schrittmotoren, mit dem externen Interrupt für steigende Flanke (beim Archie nur für EINE Endoderspur - je Motor *gg*) und werte in der zugehörigen ISR natürlich auch gleich die zweite Spur zur korrekten Wegberechnung (Drehrichtungserkennung) aus. Der Erfolg ist eine überraschend hohe Geschwindigkeitstreue. Ob Schrittmotoren in dieser Hinsicht empfindlicher sind, kann ich aber nicht sagen.


... Inkrementalgeber gerne über Interrupts auslesen würde, um möglichst keine Schritte zu verpassen ...Wenns nicht um genaue Drehzahlregelung geht, sondern nur um exakte Wegerfassung (Drehwinkel), dann sehe ich das CPU-Laufzeitproblem nicht so gravierend, weil es nur wenige Codezeilen sind:

Die vollständige ISR für (m)einen Encoder bei Archie (der zugehörige *lls-Auszug ist gaaaanz unten) :

// ================================================== =========================== =
// === Nicht unterbrechbare ISR für EXT_INT0 auf Pin 4/PD2/mega328
// Der Timer tmrE0 für Laufzeit des EXT_INT0 wird ausgelesen
// Der zugehörige Motor auf PD7/PB0 = re,li und PB1 Geschwind./PWM
ISR(INT0_vect) // INT0 triggert auf RISING edge =>
{ // => Wenn Aufruf, dann PORTD2 = high
// - - - - - - - - - - - - - - - -
// Encoderticks Iencdrx nur hochzählen, IencBx rauf- od runterzählen
Iz_diff0 = tmrE0; // Abspeichern Zeit seit dem letzten ISR-Aufruf
tmrE0 = 0; // Resetten ##>> IN der ISR ohne CLI/SEI möglich
Iencdr0 ++; // Incrementiere Encodercounter, zählt NUR aufwärts
if (IsBitSet (PIND, 4)) IencB0++; // Rad treibt vorwärts, math. negativ
else IencB0--; // Rad treibt rückwärts, math. positiv
} // Ende ISR(INT0_vect)
// ================================================== =========================== =

In der Timer-ISR - 20 kHz <=> 50 µs - wird der Zeitwert tmrE0 getickert:

// === Nicht unterbrechbare ISR für timer2 =================================== */
// Routine zählt hoch im Takt 20 kHz = 50 µs. Der Zählerwert wird von den ISR für
// EXT_INT0 und -INT1 ausgelesen
ISR(TIMER2_COMPA_vect) //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{ //
Izeit_1 --; // ###>>> Izeit_1 ist aktuell int16_t ==>>
// Izeit_1 bleibt bis 32000 in der int16-Grenze
tupUM0 ++; // Tupsicounter für Umdrehungsmessung(en)
tmrE0 ++; // Encodertimer hochtickern
tmrE1 ++; // Encodertimer hochtickern
//RCzeit1 ++; // Tupsicounter uint16_t für RC-5-Decoding

if ( Izeit_1 ) // Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{ } // WENN Izeit_1 =|= Null => wahr => Anweisung ausgeführen
else // Izeit_1 = Null = unwahr, daher "else" ausführen
{ // Eine Sekunde ist voll =>
Izeit_1 = Izthrznt; // ansonsten: Rückstellen auf Zeithorizont
ToggleBit (PgLED, L1g); // gnLED toggeln HEARTBEAT <<####, aktuell PC1
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
} // Ende if (Izeit_1 )

if (tmrE0 > 2000) // Grenzwert für Stillstand
// ... usf

Der *lls-teil zur Encoder-ISR:

// ================================================== =========================== =
// === Nicht unterbrechbare ISR für EXT_INT0 auf Pin 4/PD2/mega328
// Der Timer tmrE0 für Laufzeit des EXT_INT0 wird ausgelesen
// Der zugehörige Motor auf PD7/PB0 = re,li und PB1 Geschwind./PWM
ISR(INT0_vect) // INT0 triggert auf RISING edge =>
{ // => Wenn Aufruf, dann PORTD2 = high
8d4: 1f 92 push r1
8d6: 0f 92 push r0
8d8: 0f b6 in r0, 0x3f ; 63
8da: 0f 92 push r0
8dc: 11 24 eor r1, r1
8de: 8f 93 push r24
8e0: 9f 93 push r25
// - - - - - - - - - - - - - - - -
// Encoderticks Iencdrx nur hochzählen, IencBx rauf- od runterzählen
Iz_diff0 = tmrE0; // Abspeichern Zeit seit dem letzten ISR-Aufruf
8e2: 80 91 2f 07 lds r24, 0x072F
8e6: 90 91 30 07 lds r25, 0x0730
8ea: 90 93 0f 07 sts 0x070F, r25
8ee: 80 93 0e 07 sts 0x070E, r24
tmrE0 = 0; // Resetten ##>> IN der ISR ohne CLI/SEI möglich
8f2: 10 92 30 07 sts 0x0730, r1
8f6: 10 92 2f 07 sts 0x072F, r1
Iencdr0 ++; // Incrementiere Encodercounter, zählt NUR aufwärts
8fa: 80 91 0a 06 lds r24, 0x060A
8fe: 90 91 0b 06 lds r25, 0x060B
902: 01 96 adiw r24, 0x01 ; 1
904: 90 93 0b 06 sts 0x060B, r25
908: 80 93 0a 06 sts 0x060A, r24
if (IsBitSet (PIND, 4)) IencB0++; // Rad treibt vorwärts, math. negativ
90c: 4c 9b sbis 0x09, 4 ; 9
90e: 06 c0 rjmp .+12 ; 0x91c <__stack+0x1d>
910: 80 91 48 06 lds r24, 0x0648
914: 90 91 49 06 lds r25, 0x0649
918: 01 96 adiw r24, 0x01 ; 1
91a: 05 c0 rjmp .+10 ; 0x926 <__stack+0x27>
else IencB0--; // Rad treibt rückwärts, math. positiv
91c: 80 91 48 06 lds r24, 0x0648
920: 90 91 49 06 lds r25, 0x0649
924: 01 97 sbiw r24, 0x01 ; 1
926: 90 93 49 06 sts 0x0649, r25
92a: 80 93 48 06 sts 0x0648, r24
} // Ende ISR(INT0_vect)
92e: 9f 91 pop r25
930: 8f 91 pop r24
932: 0f 90 pop r0
934: 0f be out 0x3f, r0 ; 63
936: 0f 90 pop r0
938: 1f 90 pop r1
93a: 18 95 reti... das sind schlappe vierzig Maschinenzyklen für einen Encoder.

malthy
11.09.2014, 21:00
Hi!

Ich zitiere aus dem verlinkten µC.net Artikel (http://www.mikrocontroller.net/articles/Drehgeber): (http://www.mikrocontroller.net/articles/Drehgeber)


Warum Sparvarianten nicht gut sind

Oft sieht man im Netz "clevere" Sparvarianten, welche angeblich ebensogut zur Auswertung von Drehgebern geeignet sind. Ein genaueres Hinschauen sowie Tests unter realen Bedingungen zeigen jedoch schnell die Schwächen dieser Ansätze.

Flankenerkennung von A und Pegelauswertung von B

Viele Sparvarianten verwenden einen externen Interrupt, welcher auf die steigende oder fallende Flanke von Spur A auslöst und dann den Pegel von B auswertet. Ist B=0, dann dreht der Encoder nach rechts, anderenfalls nach links. Diese Auswertung hat zwei Schwachstellen.



Die Auflösung wird auf ein Viertel reduziert, weil nur jede steigende Flanke von A ausgewertet wird.
Pendelt der Encoder zwischen zwei Codes, bei denen A seinen Pegel wechselt,

kommt es zu (sehr) vielen Interrupts, die den Mikrocontroller vollkommen auslasten können.
interpretiert die Auswertung jede steigende Flanke als neuen Schritt. Der Encoder scheint sich für die Auswertung immer weiter zu drehen (wenn man nicht prüft, ob auch B den Pegel ändert), obwohl er nur pendelt.



Das Pendeln kann zwei Ursachen haben.


Der Encoder pendelt wirklich; das kann z. B. bei hochauflösenden Encodern ohne Rastung geschehen, welche an jeder beliebigen Stelle stehen bleiben können und durch geringe mechanische Erschütterungen dann zwischen zwei Codes pendeln; das kann z. B. bei hochauflösenden Encodern in CNC-Maschinen der Fall sein.
Die Signale prellen; das kommt vor allem bei billigen elektromechanischen Drehknöpfen vor, welche einfache Schleifkontakte zur Kodierung nutzen.

Wie man sieht ist diese Methode nicht geeignet, einen Drehgeber solide zu dekodieren.

Auswertung mit Interrupt durch Pegelwechsel

Es wird bisweilen die Auffassung vertreten, dass mit Hilfe von sog. Pin Change Interrupts Rechenzeit gespart werden kann. Dabei wird bei einem Pegelwechsel von Spur A oder B ein Interrupt erzeugt. Dort werden dann A und B eingelesen und vollständig ausgewertet. Diese Methode ist besser, aber nicht gut genug. Sie vermeidet Fehler 1. und 2.2 der oben genannten Auswertung, aber nicht 2.1, da auch sie durch einen pendelnden/prellenden Encoder die CPU stark belastet.



Das deckt sich so mit meiner persönlichen Erfahrung. Darauf, dass man in diesem Falle auch schon relativ nah an ein Performance-Problem mit der ext. Interrupt Methode (PCINT) kommen kann, wurde ja schon hingewiesen. Das Problem hat man ziemlich sicher nicht, wenn man mit einem festen Intervall abtastet und auswertet.

Gruß
Malte

oberallgeier
12.09.2014, 14:41
... Ich zitiere aus dem verlinkten µC.net Artikel (http://www.mikrocontroller.net/articles/Drehgeber): (http://www.mikrocontroller.net/articles/Drehgeber) ... Das deckt sich so ...Au, Malte. Da hast Du mich aber fein aus meiner laufenden Arbeit abgelenkt und - eigentlich wollte ich meine Auswertemethode beibehalten. Habe mal kurz in diesem m-net-Artikel gelesen. Die Auswertung auf Basis eines Zeitrasters kam mir u.A. deswegen suboptimal vor, weil z.B. ein Drehrichtungswechsel erst am Rasterende erkannt wird - andererseits ist die Stillstandserkennung mitunter . . . egal, ich sah Gründe für und wider.

Nun ist Peter Dannegger nach meiner bescheidenen Erfahrung mit allen Datenblättern - ähhhh - Wassern gewaschen. Und diesen Artikel kannte ich bisher nicht. Ich geh mal bald drüber.

Danke für den Hinweis auf den Artikel.

Basteler
12.06.2017, 21:45
http://www.meinduino.de/drehencoder.shtml. Da ist in Teil I die Auswertung per Polling und in Teil II die Auswertung des Encoders im Timer-Interrupt beschrieben. Vielleicht gelingt es Dir, den Sketch im II. Teil an Deine Bedürfnisse anzupassen.



Hallo, leider kommt es immer wieder vor, das gute Seiten nicht mehr betrieben werden bzw. völlig verschwinden, so wie bei der oben genannten.
Vielleicht habe ich das unverschämte Glück, das diese Seite bzw. der Artikel archiviert wurde und zur Verfügung gestellt werden kann.
Ich weiß, ist schon eine ganze Weile her, aber die Hoffnung stirbt ja zuletzt.......

Gruß Frank