Forum: Mikrocontroller und Digitale Elektronik Raumzeigermodulation Foc


von Jürgen (jliegner)


Lesenswert?

Hallo zusammen,

ich beziehe mich auf den Beitrag:
http://www.mikrocontroller.net/articles/Frequenzumrichter_mit_Raumzeigermodulation

dort steht geschrieben:

Ta = Zeit Anfangsvektor = proportional zu: sin (60-omega)* Motorspannung 
in %
Te = Zeit Endvektor = proportional zu: sin (omega)* Motorspannung in %
To = Zeit Nullspannungsvektor = Pulsperiode-(Zeit Anfangsvektor + Zeit 
Endvektor)

Damit werden aus den Winkel omega und der Motorspannung die Schaltzeiten 
eines einzelnen Timers berechnet mit dem dann die 6 Ausgänge 
sektorabhängig für die Mosfet-Treiber gesetzt werden. Kommt jetzt die 
foc ins Spiel, dann habe ich aber als nicht winklelabhängige 
Eingangsgrößen d und q. Danach kommt park_inv z.B. aus AVR 32723 so:
1
void park_inv(int Vd, int Vq, unsigned short teta, volatile int  *valpha, volatile int *vbeta)
2
{
3
  // valpha = Vd cos(teta)-Vq sin(teta)
4
  // vbeta= Vq cos'teta)+ Vd sin( teta)
5
  *valpha=(int)((((long long int)Vd*(long long int)coss[teta])-((long long int)Vq*(long long int)sinn[teta]))>>31);
6
  *vbeta =(int)((((long long int)Vq*(long long int)coss[teta])+((long long int)Vd*(long long int)sinn[teta]))>>31);
7
}
damit kriege ich dann valpha und vbeta. Die Routine "svpwm" aus AVR 
32723 erzeugt aus valpha und vbeta die 6 PWM-Werte für MC's die sowas in 
Hardware haben.
1
void svpwm(volatile svpwm_options_t *svpwm_options) 
2
{
3
   int tempsr0, tempsr2, tempsr4;
4
   int delta_i, delta_2i, delta_z;
5
   int Vb_M_Va, Vb_P_Va, Va_P_Vb, Va_M_Vb;
6
7
   //******************************************************************
8
   // Vbeta-sqrt(3)*Valpha
9
   Vb_M_Va = svpwm_options->Vbeta - (svpwm_options->Valpha <<1) + (svpwm_options->Valpha >>2) + (svpwm_options->Valpha >>6);
10
   // Vbeta+sqrt(3)*Valpha
11
   Vb_P_Va = (svpwm_options->Vbeta <<1) - Vb_M_Va;  
12
   if (svpwm_options->Vbeta >0)
13
   {
14
     if (svpwm_options->Valpha >0)
15
     {
16
       if (Vb_M_Va <0) // SECTOR =i =1
17
       { // delta_2i= 2*sqrt(2)* Vbeta
18
          svpwm_options->current_to_be_measured=BC; 
19
          delta_2i = (svpwm_options->Vbeta<<1) + (svpwm_options->Vbeta>>1) + (svpwm_options->Vbeta>>2) + (svpwm_options->Vbeta>>4)+ (svpwm_options->Vbeta>>6);
20
         
21
         // delta_i = 2*sqrt(1.5)*Valpha - 0.5*delta_2i           
22
          delta_i = (svpwm_options->Valpha<<1) + (svpwm_options->Valpha>>1) - (svpwm_options->Valpha>>4) - (delta_2i>>1);
23
24
         // delta_z = 0.5*( 1 - delta_2i - delta_i )          
25
          delta_z = (Un_1 - delta_2i - delta_i)>>1;
26
          
27
          tempsr0 = delta_z + delta_2i + delta_i;
28
          tempsr2 = delta_2i + delta_z;
29
          tempsr4 = delta_z;
30
       }
31
                 
32
       else // SECTOR = i = 2
33
       { 
34
         svpwm_options->current_to_be_measured=AC; 
35
         //Valpha-sqrt(3)*Vbeta
36
         Va_P_Vb = svpwm_options->Valpha + (svpwm_options->Vbeta<<1) - (svpwm_options->Vbeta>>2) - (svpwm_options->Vbeta>>6); 
37
         // delta_2i = sqrt(2)* Vb_M_Va           
38
         delta_2i = (Vb_M_Va<<1) - (Vb_M_Va>>1) -(Vb_M_Va>>4) -(Vb_M_Va>>5);
39
         // delta_i = sqrt(1.5)*(Valpha+sqrt(3)*Vbeta) - 0.5*delta_2i         
40
         delta_i  = Va_P_Vb + (Va_P_Vb>>2) - (Va_P_Vb>>5) - (delta_2i>>1);
41
         delta_z  = (Un_1 - delta_2i - delta_i)>>1;
42
         tempsr0  = delta_i + delta_z;
43
         tempsr2  = delta_i + delta_2i + delta_z;
44
         tempsr4  = delta_z;
45
       }
46
     }
47
     else
48
       if (Vb_P_Va <0)  // SECTOR = i = 3
49
       { 
50
         svpwm_options->current_to_be_measured=CA; 
51
         // delta_2i = -sqrt(2)*Vb_P_Va
52
         delta_2i = -(Vb_P_Va<<1) + (Vb_P_Va>>1) + (Vb_P_Va>>4) + (Vb_P_Va>>5);
53
         // Valpha-sqrt(3)*Vbeta
54
         Va_M_Vb = svpwm_options->Valpha - (svpwm_options->Vbeta<<1) + (svpwm_options->Vbeta>>2) + (svpwm_options->Vbeta>>6);
55
         // delta_i = -sqrt(1.5)*(Va_M_Vb)- 0.5*delta_2i
56
         delta_i = - Va_M_Vb - (Va_M_Vb>>2) + (Va_M_Vb>>5) - (delta_2i>>1);
57
         delta_z = (Un_1 - delta_2i - delta_i)>>1;
58
         tempsr0 = delta_z;
59
         tempsr2 = delta_i + delta_2i+ delta_z;
60
         tempsr4 = delta_2i + delta_z;
61
       }
62
     else  // SECTOR = i = 2
63
     { 
64
         
65
         svpwm_options->current_to_be_measured=AC; 
66
         //Valpha+sqrt(3)*Vbeta
67
         Va_P_Vb = svpwm_options->Valpha + (svpwm_options->Vbeta<<1) - (svpwm_options->Vbeta>>2) - (svpwm_options->Vbeta>>6); 
68
         // delta_2i = sqrt(2)* Vb_M_Va  
69
         delta_2i = (Vb_M_Va<<1) - (Vb_M_Va>>1) -(Vb_M_Va>>4) -(Vb_M_Va>>5);
70
         // delta_i = sqrt(1.5)*(Valpha+sqrt(3)*Vbeta) - 0.5*delta_2i
71
         delta_i  = Va_P_Vb + (Va_P_Vb>>2) - (Va_P_Vb>>5) - (delta_2i>>1);
72
         delta_z  = (Un_1 - delta_2i - delta_i)>>1;
73
         tempsr0  = delta_i + delta_z;
74
         tempsr2  = delta_i + delta_2i + delta_z;
75
         tempsr4  = delta_z;
76
     }
77
   }
78
   else
79
   {
80
      if (svpwm_options->Valpha >0)
81
      {
82
        if (Vb_P_Va <0)// SECTOR = i = 5
83
        { 
84
          svpwm_options->current_to_be_measured=AB;
85
          // delta_2i = - sqrt(2)*( Vbeta - sqrt(3)*Valpha)
86
          delta_2i = -(Vb_M_Va<<1) + (Vb_M_Va>>1) + (Vb_M_Va>>4) + (Vb_M_Va>>5);
87
          //Valpha+sqrt(3)*Vbeta
88
          Va_P_Vb = svpwm_options->Valpha + (svpwm_options->Vbeta<<1) - (svpwm_options->Vbeta>>2) - (svpwm_options->Vbeta>>6); 
89
          // delta_i = - sqrt(1.5)*(Valpha+sqrt(3)*Vbeta) - 0.5* delta_2i
90
          delta_i  = - Va_P_Vb - (Va_P_Vb>>2) + (Va_P_Vb>>5) - (delta_2i>>1);
91
          delta_z  = (Un_1 - delta_2i - delta_i)>>1;
92
          tempsr0  = delta_2i + delta_z;
93
          tempsr2  = delta_z;
94
          tempsr4  = delta_z + delta_2i + delta_i;
95
        }
96
        else   // SECTOR = i = 6
97
        {  
98
          svpwm_options->current_to_be_measured=CB;
99
          // delta_2i =  sqrt(2)*( Vbeta + sqrt(3)*Valpha)
100
          delta_2i = (Vb_P_Va<<1) - (Vb_P_Va>>1) - (Vb_P_Va>>4) - (Vb_P_Va>>5);
101
          // Valpha-sqrt(3)*Vbeta
102
          Va_M_Vb = svpwm_options->Valpha - (svpwm_options->Vbeta<<1) + (svpwm_options->Vbeta>>2) + (svpwm_options->Vbeta>>6);
103
          // delta_i =  sqrt(1.5)*(Valpha-sqrt(3)*Vbeta) - 0.5* delta_2i          
104
          delta_i = Va_M_Vb + (Va_M_Vb>>2) - (Va_M_Vb>>5) - (delta_2i>>1);
105
          delta_z  = (Un_1 - delta_2i - delta_i)>>1;
106
          tempsr0  = delta_2i + delta_z + delta_i;
107
          tempsr2  = delta_z;
108
          tempsr4  = delta_z + delta_i;
109
        }
110
      }
111
      else
112
        if (Vb_M_Va <0)// SECTOR = i = 5
113
        {
114
          svpwm_options->current_to_be_measured=AB;
115
          // delta_2i = - sqrt(2)*( Vbeta - sqrt(3)*Valpha)         
116
          delta_2i = -(Vb_M_Va<<1) + (Vb_M_Va>>1) + (Vb_M_Va>>4) + (Vb_M_Va>>5);
117
          //Valpha+sqrt(3)*Vbeta
118
          Va_P_Vb = svpwm_options->Valpha + (svpwm_options->Vbeta<<1) - (svpwm_options->Vbeta>>2) - (svpwm_options->Vbeta>>6); 
119
          // delta_i = - sqrt(1.5)*(Valpha+sqrt(3)*Vbeta) - 0.5* delta_2i         
120
          delta_i  = - Va_P_Vb - (Va_P_Vb>>2) + (Va_P_Vb>>5) - (delta_2i>>1);
121
          delta_z  = (Un_1 - delta_2i - delta_i)>>1;
122
          tempsr0  = delta_2i + delta_z;
123
          tempsr2  = delta_z;
124
          tempsr4  = delta_z + delta_2i + delta_i;
125
        }
126
      else // SECTOR = i = 4
127
      {
128
          svpwm_options->current_to_be_measured=BA;
129
         // delta_2i= -2*sqrt(2)* Vbeta         
130
          delta_2i = -(svpwm_options->Vbeta<<1)- (svpwm_options->Vbeta>>1) - (svpwm_options->Vbeta>>2) - (svpwm_options->Vbeta>>4)- (svpwm_options->Vbeta>>6);
131
         // delta_i = -2*sqrt(1.5)*Valpha - 0.5*delta_2i  
132
          delta_i = -(svpwm_options->Valpha<<1) - (svpwm_options->Valpha>>1) + (svpwm_options->Valpha>>4) - (delta_2i>>1);
133
          delta_z = (Un_1 - delta_2i - delta_i)>>1;
134
          tempsr0  = delta_z;
135
          tempsr2  = delta_z + delta_i;
136
          tempsr4  = delta_z + delta_2i + delta_i;
137
      }
138
   }
139
140
  svpwm_options->duty0 = (tempsr0>>20) - (tempsr0>>22) - (tempsr0>>23) - (tempsr0>>25) - (tempsr0>>26)-10; 
141
  svpwm_options->duty1 = svpwm_options->duty0+20; 
142
  svpwm_options->duty2 = (tempsr2>>20) - (tempsr2>>22) - (tempsr2>>23) - (tempsr2>>25) - (tempsr2>>26)-10;
143
  svpwm_options->duty3 = svpwm_options->duty2+20;
144
  svpwm_options->duty4 = (tempsr4>>20) - (tempsr4>>22) - (tempsr4>>23) - (tempsr4>>25) - (tempsr4>>26)-10;
145
  svpwm_options->duty5 = svpwm_options->duty4+20;
146
}

Wenn ich diese Werte in ein Diagram über den Winkel plotte, kriege ich 
die gewünschten "Popokurven", d.h. eigentlich die Spannungswerte der 3 
Phasen. Meinen Frage ist jetzt wie ich aus den Werten valpha und vbeta 
die Werte Ta, Te und To berechnen kann, wenn ich das wie ganz oben in 
einem einzelnen Timerinterrupt abwickeln will.

Ich hoffe jemand versteht überhaupt meine Frage und noch besser wäre die 
Entknotung meines Rätsels.

von Tobias P. (hubertus)


Lesenswert?

Würde mich auch interessieren.
Leider wird das extrem mystifiziert; ich habe noch in keiner einzigen 
Application Note oder so eine brauchbare Erklärung gefunden.
Aber ich rate mal folgendes:

tan omega = vbeta / valpha

und dann wieder

"Ta = Zeit Anfangsvektor = proportional zu: sin (60-omega)* 
Motorspannung
in %
Te = Zeit Endvektor = proportional zu: sin (omega)* Motorspannung in %
To = Zeit Nullspannungsvektor = Pulsperiode-(Zeit Anfangsvektor + Zeit
Endvektor)"

oder so ähnlich? extrem gruselig dabei ist, dass man einen Arctan 
braucht. (wenn denn mein Lösungsansatz funktioniert...)

von Jürgen (jliegner)


Lesenswert?

Hallo Tobias,

es freut mich schon mal einen Leidensgenossen gefunden zu haben. Ich 
habe auch schon unzählige App-Notes und den Code dazu durchforstet. Bei 
den meisten ist fast keine Zeile verständlich kommentiert. Ich habe auch 
noch kein weiteres Beispiel für die PWM mit einem einzigen 
Timerinterrupt gefunden so wie es Axel Jeromin macht. Das hat aber für 
mich den Reiz, dass es sehr leicht auf die verschiedenen MC's portiert 
werden kann. Aber ich bleibe dran.

von Tobias P. (hubertus)


Lesenswert?

Die Frage nach der RZM mit FOC taucht hier des öfteren auf, allerdings 
konnte sie wohl tatsächlich noch nicht wirklich zufriedenstellend 
beantwortet werden...

Aber wie gesagt, ich habe diese Vermutung. Denn:
Valpha und Vbeta liegen ja in dem rechtwinkligen Koordinatensystem. Sie 
können als komplexer Zeiger

Valpha + j * Vbeta

aufgefasst werden. Dann kann man mit dem Tangens den Winkel dieses 
komplexen Zeigers bestimmen und hat so das gewünschte Omega - und wie 
man aus diesen dann die Zeiten für die PWM berechnet, das ist ja klar. 
Ich meine auch, hier im Forum mal gelesen zu haben, dass man das so 
macht.
Der Knackpunkt dabei ist natürlich, dass man auf dem uC keinen arctan 
hat, und mit einer Tabelle kann man das auch nicht so gut machen - das 
müsste ja dann eine zweidimensionale Tabelle sein, mit tausenden von 
Einträgen, damit man eine einigermassen bruahcbare Genauigkeit erreicht.

von Jürgen (jliegner)


Lesenswert?

Die Position des Rotors, der Winkel teta, wird ja schon der park_inv 
übergeben. Dein Vorschlag würde bedeuten, dass nach der Berechnung der 
Winkel omega nicht mehr dem teta entspricht und somit das Feld Vor- oder 
Nacheilt. Liege ich mit meiner Vermutung da richtig?

von Tobias P. (hubertus)


Lesenswert?

Das soll es doch auch, damit der Motor sich dreht. Oder nicht?
Nicht umsonst heisst es ja "Drehfeld" :-)

von Jürgen (jliegner)


Lesenswert?

Ich glaube ich kommer der Sachen schon näher. Das Auffrischen von 
Grundlagen die ich vor ca. 35 Jahren mal gelernt und danach nie wieder 
gebrauch habe, dauert halt etwas....
Die Werte d und q kommen ja als zum Drehfeld gesehen statische Größen 
aus dem Regler. Die Verhältnisse zwischen beiden entsprechen denen von 
Valpha und Vbeta. Wenn ich aus dem d und q mit atan2 den Winkel in Grad 
berechne, erhalte ich den Versatz des Drehfeldes. Ich nenne das mal 
omega_diff.
Wurzel aus (d quadrat + q quadrat) ergibt die "Motorspannung".
Kann natürlich auch über sin bzw. cos und omega_diff gerechnet werden.

Das kann ich dann direkt ohne park_inv in:

Ta = proportional zu: sin (60-teta+omega_diff) * Motorspannung in %
Te = proportional zu: sin (teta+omega_diff)* Motorspannung in %
To = Pulsperiode-(Zeit Anfangsvektor + ZeitEndvektor)

einsetzen.

Die Berechnung der "Motorspannung" und omega_diff erfolgt am Ausgang des 
Reglers und hat da alle Zeit der Welt. In der Interruptroutine gibts 
dann nur noch einfache Berechnungen.

Für die atan2 Berechnung gibts hier Beispiele:
Beitrag "Cordic, atan2, 32bit integerarithmetik, cortex M3"

Kann das so stimmen?

von Naseweis (Gast)


Lesenswert?

Hi, wenn ihr garnicht mehr klar kommt, schaut euch doch mal den Toshiba 
TMPM370 an. 
http://www.toshiba-components.com/microcontroller/TMPM370.html
Auch wenn der die ganze FOC in Hardware berechnet, ist im Datenblatt 
zumindest eine schematische Erklärung der SVPWM ( ab ca Seite 280 ) zu 
finden. Da ist nix mit arctan. Dafür werden aus valpha und vbeta die 
Spannungen va, vb und vc erzeugt und diese wiederum in entsprechende PWM 
Werte umgerechnet. AppNotes und Software gibt's glaube ich auch.

von Jürgen (jliegner)


Lesenswert?

Naseweis hat unser Problem nicht verstanden.
Wir wollen ja gerade nicht die Spannungen va vb und vc haben, sondern 
die PWM wie um Artikel: 
http://www.mikrocontroller.net/articles/Frequenzumrichter_mit_Raumzeigermodulation 
realisieren. Das macht einen gewaltigen Unterschied. Der TMPM370 macht 
es so wie alle andern auch und so ist ja auch das Quellcodebeispiel von 
ganz oben.

von Naseweis (Gast)


Lesenswert?

Hi, ich denke, durch geschicktes Zusammenfassen der Berechnungen, kann 
va, vb, vc übersprungen werden und somit unmittelbar aus valpha und 
vbeta die PWM Werte berechnet werden, vermutlich auch ohne über arctan 
zu gehen.

von Tobias P. (hubertus)


Lesenswert?

Die 12phasige PWM-Einheit des uC, den ich einsetze, erlaubt es für jedes 
der PWM Signale den Einschalt- und Ausschaltzeitpunkt separat 
einzustellen. Ich gebe also nicht einen gewünschten Tastgrad vor, 
sondern effektiv die Einschalt- und Ausschaltzeitpunkte. Das 
verkompliziert die Sache wohl noch mehr, oder? gibts dafür wohl auch 
eine Lösung?
Btw, von TI gibt es eine Application Note. Ich hab zwar grad keinen Link 
parat (schreibe gerade vom Handy aus). Aber auf jeden Fall wurde da die 
Berechnung der PWM erklärt, leider völlig unverständlich und ausserdem 
wurde da noch mit komplexen Zahlen gerechnet - das ist wahrscheinlich 
selbst auf einem ARM eine unbrauchbare Lösung, oder?

von Tobias P. (hubertus)


Lesenswert?

Jürgen,
da fällt mir gleich noch was ein.
Hast du eigentlich eine Hardware aufgebaut, mit der du das testest, oder 
machst du erstmal Trockenübungen mit Excel?
Weil, ich hab hier ein Board mit einem ARM7 drauf und wie gesagt einem 
12phasigen PWM-Modul. Damit liessen sich also problemlos 6 FETs 
ansteuern um einen kleinen Motor drehen zu lassen.
Ich frage mich nur, wie ich die Rotorposition erfassen soll. Wie planst 
du das? Die Hallsensoren eines gewöhnlichen BLDC werden ja eh nicht 
hochauflösend genug sein. Soll man einen Inkrementalgeber oder einen 
absoluten Geber verwenden, und wie hoch muss denn dessen Auflösung sein?
Wenn ich einen selbstgebauten Geber habe, der 10° Auflösung bringt - 
reicht das denn dann, damit der Motor sich dreht, oder brauch ich eine 
höhere Auflösung?

An diejenigen, die sowas schon mal gemacht haben:
reicht von der Rechenleistung her ein ARM7 wohl, oder geht das wirklich 
nur auf einem DSP? float und Konsorten stehen mir natürlich nicht 
wirklich zur Verfügung, daher auch kein arctan oder arctan2... :-/

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Tobias Plüss schrieb:
> Die Hallsensoren eines gewöhnlichen BLDC werden ja eh nicht
> hochauflösend genug sein. Soll man einen Inkrementalgeber oder einen
> absoluten Geber verwenden, und wie hoch muss denn dessen Auflösung sein?

Eine mögliche Lösung ( beschrieben in AVR447) ist die Interpolation 
zwischen zwei Hallpositionen mittels eines mitlaufenden Timers. Eine 
Hallstatusänderung resynchronisiert dann den Rotationswinkel. Um sich 
sämtliche Laufzeitberechnungen zu sparen (AVR447 ist für ATMega88/168) 
arbeitet man dort mit Sinustabelle und einer Auflösung von 360/192 = 
1.875 Grad.
Meiner Erfahrung nach reicht das sogar bei Plattenspielermotoren für 
einen extrem ruhigen Lauf.

von Tobias P. (hubertus)


Lesenswert?

Also eigentlich so eine Art Software-PLL, oder nicht?
klingt recht interessant. Dass das tatsächlich brauchbar funktioniert, 
hätte ich nicht gedacht :-) Ich werde das baldmöglichst versuchen 
aufzubauen.
Dann muss ich nur noch irgendwie die Park und Clarke Transformationen 
irgendwie versuchen so zu realisieren, dass ich float vermeiden kann, 
und wenn wir dann hier endlich raus gefunden haben, wie man aus Valpha 
und Vbeta die Schaltzeitpunkte berechnet, dann kann man loslegen ;-)

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Naja, die Regelung ist da noch nicht drin. AVR447 zählt einfach die 
Anzahl von Ticks und holt sich den passenden Tabellenwert, sobald der 
PWM Timer überläuft. Die Tabelle haben die Jungs so organisiert, das da 
immer gleich alle 3 Phasen rauspurzeln - passnd zum AVR in 8-bit 
Auflösung. Die 'Popokurven' sind allerdings recht gut gelungen. BLDC 
typisch wird die Geschwindigkeit über die Amplitude der PWM gelöst.

Die Regelung realisieren sie über den PID Regler aus AVR221 was recht 
gut funktioniert, wenn die Motorcharakteristika einmal angepasst sind.

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.