Forum: FPGA, VHDL & Co. Drehzahlmessung mit Encoderscheibe


von Andreas M. (chillymu)


Angehängte Dateien:

Lesenswert?

Hi Leute,

habt Ihr vielleicht einen Tipp, wie ich die Drehzahl eines Motor mittels 
einer Encoderscheibe ermittel?
Die Encoder Signale kann ich bereits auslesen und bekomme eine Reihe von 
Impulsen.

Wie kann ich jetzt aus diesen Impulsen die Drehzahl ermitteln?

Dank für eure Hilfe.

Gruß Chillymu

von Christian R. (supachris)


Lesenswert?

Du brauchst dann noch einen Zähler für die Tor-Zeit, innerhalb dieser 
zählst du die Pulse und wenn die Zeit abgeleuafen ist, übertägst du den 
Zählerstand in ein Register, was dann auslesbar ist und setzt den Zähler 
zurück.

von Schlumpf (Gast)


Lesenswert?

Nachdem die Drehgeschwindigkeit Winkel/Zeit ist und ein Impuls einen 
bestimmten Winkel repräsentiert, hast du zwei Möglichkeiten: Entwder für 
eine feste zeit die Anzahl der Pulse zählen oder die Dauer zwischen zwei 
Pulsen auswerten.
Dazu reicht das Auswerten einer Spur.
Die zweite Spur liefert Pulse, die 90° versetzt zu der anderen Spur 
sind.
Wenn du die auch auswertest, kannst du zum einen eine höhere Auflösung 
erzielen und zum anderen die Drehrichtung.
Das erfolgt dann sinnvollerweise über einen Zustandsautomaten

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller schrieb:
> Wie kann ich jetzt aus diesen Impulsen die Drehzahl ermitteln?
In welcher Einheit und mit welcher Genauigkeit willst du die Drehzahl 
ermitteln?

> Die Encoder Signale kann ich bereits auslesen und bekomme eine Reihe von
> Impulsen.
Naja, die Impulse kommen ja schon von dem Encoder selber. Oder was 
meinst du mit dieser "Reihe von Impulsen"?

Schlumpf schrieb:
> die Dauer zwischen zwei Pulsen auswerten.
Ist ungünstig, weil der erhaltene Wert reziprok zur Drehzahl ist.

von Schlumpf (Gast)


Lesenswert?

Lothar Miller schrieb:
>> die Dauer zwischen zwei Pulsen auswerten.
> Ist ungünstig, weil der erhaltene Wert reziprok zur Drehzahl ist.

Ist aber günstig, wenn man sehr geringe Drehzahlen noch einigermaßen 
genau erfassen will ;-)
Die Wahl der Methode hängt halt vom Anwendungsfall ab.
Aber ich gebe dir recht: In den typischen Fällen wird man sicher die 
Anzahl der Impulse in einem Zeitfenster auswerten.

Dem TO möchte ich aber noch empfehlen, beide Spuren auszuwerten, auch 
wenn er die Drehrichtung nicht benötigt.
Falls bei Stillstand die Achse genau um einen Decoderstrich hin und her 
wackelt, würde er beim Auswerten nur einer Spur eine Frequenz erhalten, 
also "Drehzahl" messen. Diese Fehlinterpretation lässt sich durch 
Plausibilisierung beider Spuren besser beherrschen.

von Andreas M. (chillymu)


Lesenswert?

Lothar:
Als Einheit dachte ich Impulse pro ms. Da es 1600 impulse pro Umdrehung 
sind, ergibt das 0.225 Grad pro Impuls. So kann ich das in Grad pro s 
umrechnen und dadurch auch auf Weg pro Sekunde umrechnen. Also ist die 
Grundsätzliche Einheit Impulse/ms.

Das die Impulse vom Encoder kommen stimmt, aber wie zähle ich diese 
Impulsflanken und kann diesen Wert dann in einem Register speichern? 
Dieser soll sich aber nur jede ms ändern.

von kläb (Gast)


Lesenswert?


von kläb (Gast)


Lesenswert?

Dann noch ein ms Timer der einen FIFO mit dem Zählerstand füttert und 
ein Subtrahierer.
Das Ergebnis ist dann die Impulsdifferenz pro ms.
Allerdings wird das nicht sonderlich funktionieren.
Evtl das Abtastintervall größer machen und das Ergebnis noch etwas 
filtern! ?

von Schlumpf (Gast)


Lesenswert?

@ Andreas:

Beantworte doch mal bitte die folgenden Fragen:
1.) Du hast ganz am Anfang einen Block angehängt. Willst du diesen Block 
mit "Leben" füllen, oder hast du den bereits und willst die 
Informationen die dieser ausspuckt, weiterverarbeiten?

2.) Suchst du nach einer prinzipiellen Methode, wie man aus Impulsen zu 
einer Drehzahl kommt? --> Das wäre ja jetzt bereits beantwortet

3.) Kennst du dich in VHDL oder Verilog aus? Also weisst du, wie man 
Register, Counter, Timer etc in einer HDL beschreibt und sind dir 
Schlagworte wie "synchrones Design", "Eingänge einsynchronisieren", etc 
ein Begriff, oder bist du absoluter FPGA/VHDL-Neuling?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller schrieb:
> Lothar:
> dadurch auch auf Weg pro Sekunde umrechnen.
Willst letztlich die lineare Geschwindigkeit von irgendwas?
Dann würde ich dir empfehlen, die Abtastzeit so zu wählen, dass gleich 
die Geschwindigkeit in der endgültigen Einheit herauskommt. Denn
> die Grundsätzliche Einheit Impulse/ms.
in in Hardware in irgendwas anderes umzurechnen ist und aufwendig.

> Das die Impulse vom Encoder kommen stimmt, aber wie zähle ich diese
> Impulsflanken und kann diesen Wert dann in einem Register speichern?
> Dieser soll sich aber nur jede ms ändern.
Siehe die Fragen vom Schlupf...
Dazu 4.) welche Hardware hast du für diese Auswertung?

von Andreas M. (chillymu)


Lesenswert?

Hallo.

Schlumpf:
Zu 1): Ja der Block ist bereits fertig und funktioniert super. Er 
liefert mir aus den beiden Encodersignalen eine Reihe von Impulsen. 
Diese möchte ich weiter verarbeiten um später vielleicht ein Regelung zu 
erarbeiten zur genauen Positionierung.

Zu 2) Ja. Vorerst suche ich nach einer Methode um aus Impulsen eine 
Drehzahl zu ermitteln. Das werde ich morgen gleich mal versuchen. Im 
Prinzip suche ich nach einer Regelung zur genauen Positionierung und 
dachte dies wäre ein erster Schritt bevor es an die Regelung geht. Da 
bräuchte ich ebenfalls einen Denkanstoß.

Zu 3) In Vhdl kenn ich mich schon ganz gut aus. Hab auch schon einiges 
gemacht, doch derzeit steh ich etwas auf dem Schlauch um zur Lösung zu 
kommen.

Wie würde ich prinzipell vorgehen wenn ich einen DC-Motor mit 
Encoderscheibe hätte und möchte damit eine Position anfahren. Ich dachte 
da an einen PID-Regler oder wie seht ihr das?

Die Hardware ist ein Cyclone II Board von mir entwickelt, mit einem Nios 
II drin. Funktioniert einwandfrei.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller schrieb:
> Wie würde ich prinzipell vorgehen wenn ich einen DC-Motor mit
> Encoderscheibe hätte und möchte damit eine Position anfahren. Ich dachte
> da an einen PID-Regler oder wie seht ihr das?
WEnn die Strecke das mitmacht...  ;-)
PID sagt ja noch nichts über die Verhältnisse zwischen P, I und D aus.

Worauf soll denn der Regler laufen? Auf dem NIOS?

> Das die Impulse vom Encoder kommen stimmt, aber wie zähle ich diese
> Impulsflanken
Das ist doch ganz einfach: du willst keine Geschwindigkeit, sondern 
eine Position ermitteln. Und genau das ist mit einem Inkrementalgeber 
ein leichtes Spiel:
http://www.lothar-miller.de/s9y/categories/46-Encoder
Und diese Position speicherst du dann einfach jede ms (diese Zeit wird 
einfach mit einem Zähler aus der Quarzfrequenz hergeleitet) in ein 
Register. Fertig ist die Laube. Mit deutlich weniger als 50 Zeilen VHDL 
bist du am Ziel...

von Schlumpf (Gast)


Lesenswert?

@ Andreas:

zu 1)
Prima! dann gehe ich jetzt mal stillschweingend davon aus, dass die 
Ausgangssignale des Blocks bereits synchron zu deinem Systemtakt im FPGA 
sind.

zu 2)
Die Drehzahl könntest du dann ermitteln, indem du einen Timer und einen 
Counter baust.
Der Timer läuft immer schön im Kreis
Während der Timer läuft, zählst du die Impulse (entweder 
vorzeichenbehaftet, wenn du die Position willst, oder eben ohne, wenn du 
nur die Drehzahl willst)
Kommt der Timer durch den Nulldurchgang, dann kopierst du die Werte des 
Counters (Anzahl der Impulse im letzten Messintervall) in ein Register 
und setzt den Counter zurück.
Parallel dazu machst du noch einen Triggerimpuls für deine Regelung, 
dass ein neuer Messwert vorliegt.

zu 3)
Wenn dem so ist, dann sollte die Umsetzung von 2) kein Problem 
darstellen, nehme ich mal an.

Andreas Müller schrieb:
> Wie würde ich prinzipell vorgehen wenn ich einen DC-Motor mit
> Encoderscheibe hätte und möchte damit eine Position anfahren. Ich dachte
> da an einen PID-Regler oder wie seht ihr das?

Richtig. ABER, so einfach, wie es vielleicht grad erscheint, ist es 
nicht.
Die Regelparameter für P-, I- und D-Anteil müssen so ermittelt werden, 
dass der Motor nachher nicht Tango tanzt. Vorallem brauchst du dazu 
meines Erachtens einen geschachtelten Regler.
Einen Regler für die Geschwindigkeit und einen anderen für die Position.
Oder willst du Vollgas bis zur Sollposition fahren, dann über´s Ziel 
hinausschießen, dann wieder vollgas zurück usw.. ich meine, so parken 
manche Leute ein, aber schön ist das nicht gg

von Andreas M. (chillymu)


Lesenswert?

Alles klar. Ich werde den Tipp von Lothar morgen mal versuchen und gebe 
Bescheid.

Danke.

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Worüber Du auch noch nachdenken musst, ist eine Filterung der Daten, 
damit Du keine digitalen Abtastfehler in die Geschwindigkeit 
miteinrechnest, sowie eine Totzeit-Vorberechnung, damit Du keine 
falschen Beschleunigungswerte miteinrechnest und eine 
Linearitätskorrektur, die die Ungleichmässigkeiten der Encoderscheibe 
vorkompensiert.

von Andreas M. (chillymu)


Angehängte Dateien:

Lesenswert?

Hi Lothar,

ich habe im jetzt deine Positionsermittlung genommen und entsprechend 
meinen Bedürfnissen modifiziert.

Das ist dabei herausgekommen:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity Encoder_Pos is
6
  Generic(Clock_Counter : INTEGER := 81920
7
  );
8
    Port ( clk : in  STD_LOGIC;
9
           A   : in  STD_LOGIC;
10
           B   : in  STD_LOGIC;
11
           Up_Dn  : out  STD_LOGIC;
12
           Position_ms  : out  STD_LOGIC_VECTOR (31 downto 0);
13
           Position_act : out  STD_LOGIC_VECTOR (31 downto 0);
14
           Position_ms_dif  : out  STD_LOGIC_VECTOR (31 downto 0));
15
end Encoder_Pos;
16
17
architecture Behavioral of Encoder_Pos is
18
type zustaende is (Z00, Z01, Z11, Z10);
19
signal z : zustaende := Z00;
20
signal p : integer := 0;
21
signal p2 : integer := 0;
22
signal p_old : integer := 0;
23
signal p_dif : integer := 0;
24
signal i : std_logic_vector(1 downto 0);
25
signal e : std_logic_vector(1 downto 0);
26
signal clock_ctr : integer := 0;
27
signal pos_reg : std_logic_vector(31 downto 0) := (OTHERS => '0');
28
signal up_dn_reg : std_logic := '0';
29
begin  
30
  process begin -- Eintakten der asynchronen Signale
31
    wait until rising_edge(clk);
32
    i <= A & B;  -- Zusammenfassen der Eingänge A und B
33
    e <= i;
34
  end process;
35
36
  process      -- Weiterschalten und Zählen
37
  variable cu, cd : std_logic := '0';
38
  begin
39
    wait until rising_edge(clk);
40
    cu := '0'; -- lokale Werte
41
    cd := '0';
42
    case z is
43
      when Z00 => if    (e = "01") then z <= Z01; cu := '1';
44
                  elsif (e = "10") then z <= Z10; cd := '1';
45
                  end if;
46
      when Z01 => if    (e = "11") then z <= Z11; cu := '1';
47
                  elsif (e = "00") then z <= Z00; cd := '1';
48
                  end if;
49
      when Z11 => if    (e = "10") then z <= Z10; cu := '1';
50
                  elsif (e = "01") then z <= Z01; cd := '1';
51
                  end if;
52
      when Z10 => if    (e = "00") then z <= Z00; cu := '1';
53
                  elsif (e = "11") then z <= Z11; cd := '1';
54
                  end if;
55
    end case;
56
    if    (cu='1') then
57
    p <= p+1;
58
    up_dn_reg <= '1';
59
  elsif (cd='1')then
60
    p <= p-1;
61
    up_dn_reg <= '0';
62
  end if;
63
  end process;
64
  
65
  process begin -- Takt Zählen
66
    wait until rising_edge(clk);
67
  clock_ctr <= clock_ctr + 1;  -- Zählt den Takt
68
  if (clock_ctr=1) then
69
    if    (up_dn_reg='1') then p_dif <= p - p_old;  
70
    elsif (up_dn_reg='0') then p_dif <= p_old - p;
71
    end if;  
72
    end if;
73
  if (clock_ctr=Clock_Counter-1) then
74
    p_old <= p2;
75
  end if;
76
  if (clock_ctr=Clock_Counter) then
77
    pos_reg <= std_logic_vector(to_signed(p,32));
78
    p2 <= p;
79
    clock_ctr <= 0;
80
  end if;
81
  end process;
82
  Position_ms <= pos_reg; -- Position gemessen je Zähleinheit
83
  Position_act <= std_logic_vector(to_signed(p,32)); -- Position ausgeben
84
  Position_ms_dif <= std_logic_vector(to_signed(p_dif,32)); -- Impulse pro Taktzähler
85
  Up_Dn <= up_dn_reg; -- Richtung
86
end Behavioral;

Dabei ist der Ausgang Position_ms_dif der Berechnete Wert für Impulse 
pro ms.
Funktioniert super. Ich habe dies an meinen NIOSII angeschlossen und 
kann nun die Impulse pro ms lesen.(siehe Bild) Freu, Freu.

Habt ihr jetzt vielleicht eine Idee wie ich das mit der Positionierung 
mache. Ich müsste ja rein theoretisch eine Art Trapezfahrt ausführen. 
D.h. langsam anfahren auf die Zielgeschwindigkeit und kurz vor dem 
erreichen des Ziels langsamer werden.
So wie Schlumpf schon sagt: Einparken alla vor zurück ist nich 
sonderlich schön.

Irgend einen Vorschlag??

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller schrieb:
> Ich müsste ja rein theoretisch eine Art Trapezfahrt ausführen. D.h.
> langsam anfahren auf die Zielgeschwindigkeit und kurz vor dem erreichen
> des Ziels langsamer werden.
Da müsste man mehr über die Strecke wissen.
> So wie Schlumpf schon sagt: Einparken alla vor zurück ist nich
> sonderlich schön.
Ein PID Regler muss nicht zwingend überschwingen...

Das hatte ich befürchtet:
1
    if    (cu='1') then
2
    p <= p+1;                      -- warum nicht gleich hier und ...
3
    up_dn_reg <= '1';
4
  elsif (cd='1')then
5
    p <= p-1;                      -- ... hier zählen
6
    up_dn_reg <= '0';
7
  end if;
8
  end process;
9
  
10
  process begin -- Takt Zählen
11
    wait until rising_edge(clk);
12
  clock_ctr <= clock_ctr + 1;  -- Zählt den Takt
13
  if (clock_ctr=1) then
14
    if    (up_dn_reg='1') then p_dif <= p - p_old;  -- und stattdessen kompliziert zurückrechnen?
15
    elsif (up_dn_reg='0') then p_dif <= p_old - p;
Warum rechnest du erst eine Strecke aus und daraus wieder einen 
Zählerwert?
Mit ein wenig Nachdenken siehst du, dass das irgendwie doppelt gemoppelt 
ist...

> signal clock_ctr : integer := 0;
Bitte nicht mit solchen Schlampereien anfangen! Wozu brauchst du hier 
einen 32-Bit-Zahl? Schränke gleich zu Beginn den Zähler so ein, dass dir 
der Simulator auf die Finger klopfen kann, wenn der Zähler Amok läuft.

: Bearbeitet durch Moderator
von Andreas M. (chillymu)


Angehängte Dateien:

Lesenswert?

Die Beschränkung des Integerwerts habe ich gemacht. Da hast du recht. 
Bei dem anderen Punkt muss ich mir überlegen wie ich das zusammenfasse. 
Wäre bestimmt eleganter.

Nochmal zum Drehzahlregler:

Wenn ich eine  P-Regler nehme(abgesehen davon ob das Sinn macht oder 
nicht), wie würde der Theoretisch aussehen?

Eine Regel Strecke sieht im allgemeinen so aus wie im Bild.

Dabei ist

w = Soll-Drehzahl(z.B. 6 Imp/ms)
x = Ist-Drehzahl(z.B. bei Start 0 Imp/ms)
e = w-x(z.B. 6 Imp/ms)
y = Kp * e

Wo fließt jetzt mein PWM-Wert ein. Bei dem ich den duty-cycle ändere? y 
müsste doch in diesem Fall der duty-cycle wert sein, oder?

von Andreas M. (chillymu)


Lesenswert?

Könnt Ihr mir nochmal kurz erklären wie ich eine einfachen P-Regler in 
VHDL erstellen würde bei dem ich Kp einstellbar machen würde. Gibt es da 
vielleicht schon einen fertigen Block für den P-Regler?

von Schlumpf (Gast)


Lesenswert?

Lothar Miller schrieb:
> Ein PID Regler muss nicht zwingend überschwingen...

Richtig, das stimmt. Aber Der TO will auf eine Position regeln, nimmt 
aber als Eingangsgröße für seinen Regler die Drehzahl und das geht 
schief.

Andreas Müller schrieb:
> Könnt Ihr mir nochmal kurz erklären wie ich eine einfachen P-Regler in
> VHDL erstellen würde bei dem ich Kp einstellbar machen würde.

Kp könnte in einem Register liegen, welches du von "außen" über einen 
µC, Dipswitches, Poti oder sonstwas veränderbar machst.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Müller schrieb:
> eine einfachen P-Regler in VHDL
Das ist m.E. der falsche Ansatz, wenn du
>>> mit einem Nios II
regeln könntest...  :-/
Sowas schnarchlangsames wie eine Positionsregelung eines Motors ist doch 
in C einfacher zu debuggen als in VHDL.

> Könnt Ihr mir nochmal kurz erklären wie ich eine einfachen P-Regler in
> VHDL erstellen würde bei dem ich Kp einstellbar machen würde.
Wie würdest du das denn in C machen?
> Gibt es da vielleicht schon einen fertigen Block für den P-Regler?
Sowas kannst du sicher irgendwo kaufen, aber ein P-Regler ist ja 
nichts weiter als eine Subtraktion (für e) und eine Multiplikation (mit 
kp), das dürfte nicht allzu schwer sein.

Ich würde sagen, mit höchstens 30 Zeilen VHDL-Code bekommt man ein 
eigenes VHDL-P-Regler-Modul...

: Bearbeitet durch Moderator
von Andreas M. (chillymu)


Lesenswert?

Da hast du schon recht. Ich werde die Drehzahlregelung jetzt im c-Code 
des Nios machen. Das ist fürs erste einfacher uns schneller zu 
realisieren.
Dennoch ist immernoch die Frage ob ich bei der anschließenden 
Positionierung etwas beachten muss un da so genau wie möglich zu werden?

von Schlumpf (Gast)


Lesenswert?

Andreas Müller schrieb:
> Dennoch ist immernoch die Frage ob ich bei der anschließenden
> Positionierung etwas beachten muss un da so genau wie möglich zu werden?

Ja, musst du.
Wenn du auf eine Position regeln willst, dann musst du als 
Eingangsgrößen für deinen Regler die Soll-Position und die Ist-Position 
nehmen und nicht die Drehzahl.

von Bronco (Gast)


Lesenswert?

Noch ein paar Tips aus jahrelanger Encoder-Auswerte-Raxis:
- Das Einsynchronisieren der Eingänge zum FPGA-Takt ist wirklich sehr 
wichtig (siehe Lothars Homepage)!
- Es kann passieren, daß der Encoder im (scheinbaren) Stillstand prellt, 
d.h. ein Signal (A oder B) "wackelt herum", obwohl sich eigentlich 
nichts bewegt. Aus diesem Grund ist es nicht verkehrt, einen 
Positionsänderung erst dann zu übernehmen, wenn sich beide Signale (A 
und B) geänderten haben.
- Jenachdem wie Dein Encoder mechanisch an den Motor angeschlossen ist, 
mußt Du ggf. Schlupf beachten.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.