Forum: Compiler & IDEs Frage zur Wahl der Variablengrößen


von Per P. (mullwark)


Lesenswert?

Guten Abend, Community

Ich bin gerade dabei, einen PID-Regler für die Fahrtregelung meines 
kleinen Roboters zu schreiben. Rechenknecht ist ein ATmega32 der einen 
Maussensor einliest und damit die Motoren regeln soll. Die Motoren 
werden dabei mit PWM über den T/C1 betrieben. Das Ganze programmiere ich 
in C.

Ich nutze beim Zähler (erstmal) nur 8 Bit. Wenn ich jetzt meine 
Mausdaten kontinuierlich einsammel und aufsummiere und der Regler mit 
etwa 10 Hz darauf zugreift, habe ich hardwarebedingt einen Istwert nahe 
der 600. Also muss ich da mit 16 bit arbeiten. Soweit klar. Wenn ich 
dann die Abweichung berechne, und diese mit der P-Regler-Konstanten 
multipliziere, will ich ja einen Wert bekommen, der auf die OCR1n 
addiert, immer in die 8bit der OCR1n passen muss. (Ja, ich weiß, warum 
dann nicht den 16bit-Zähler auch als solchen verwenden? Ist geplant, 
löst aber diese Frage nicht in Luft auf.) Demnach könnte ich diesen Wert 
ja wieder in eine 8bit-Variable stecken. Ein If-Else-Konstrukt 
verhindert auch, daß OCR1n mit Überläufen konfrontiert wird.
Naja geht irgendwie, der Regler läuft, aber theoretisch ist das ja 
ziemlich schmutzig, denn 16 bit - 16 bit kann auch 32 bit werden. 32 bit 
* 8 bit passt auch nicht immer in 32 bit, also müsste ich ja den Wert 
mathematisch korrekt in 64 bit schreiben. Auf einer 8bit Architektur 
etwas wild, oder?

Wie hält man da die Bitbreiten in einem sauberen Verhältis zwischen 
Aufwand und Robustheit? Habt Ihr irgendwelche Suchmaschinenparameter für 
mich oder gibt es dazu bewährte Herangehensweisen?
Oder macht der Compiler aus meinem unansehnlichen Stück Code etwas 
Vernünftiges?

Besten Dank schonmal für eure Hilfe.

von B. S. (bestucki)


Lesenswert?

Ich halte mich bei 8Bit Controllern immer an folgende Regel:
- So klein wie möglich, so gross wie nötig


Da bei Programmen für Mikrocontrollern in der Regel der Wertebereich 
definiert ist, sollte es kein Problem sein, den korrekten Datentyp 
auszuwählen.

Ein Beispiel:
Ein Temperatursensor liefert Temperaturen im Bereich von -50 bis 150°C 
in einer Auflösung von 0.1°C. Dann verwende ich einen int16_t als 
Fixkommazahl => Wertebereich der Variable: -500 bis 1500.

Nun schreibe ich die Funktion, die den Sensor ausliest und die 
Temperatur in das richtige Format bringt:
1
int16_t ReadTemperature(void){
2
  int16_t Temperature;
3
4
  /* Lese Sensor */
5
  /* Formatiere Temperatur */
6
7
  if(Temperature>1500){
8
    Temperature=1500;
9
  }
10
  if(Temperature<-500){
11
    Temperature=-500;
12
  }
13
}
Hier füge ich Prüfungen und Korrekturen ein, die bei einem allfälligen 
Fehler den Wertebereich der Variable garantiert. So können bei darauf 
folgenden Berechnungen, die sich auf den Wertebereich der Variable 
verlassen, keine Überläufe entstehen.

Diese Vorgehensweise hat den Nachteil, dass du bei einer Anpassung des 
Wertebereichs alle betroffenen Codezeilen manuell prüfen und evt. 
anpassen musst.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

>> denn 16 bit - 16 bit kann auch 32 bit werden.

Ne, 16 Bit - 16 Bit wird höchstens 17 Bit. Zur Speicherung wird dann 
eine 32 Bit Variable benötigt, bei der aber 15 Bit unbenutzt sind.

Multipliziert mit 8 Bit sind das jetzt 25 Bit. Dafür wird immer nur noch 
eine 32 Bit Variable benötigt, bei der dann immer noch 7 Bit unbenutzt 
sind.

Also kein Zwang zu 64 Bit.

von Falk B. (falk)


Lesenswert?

@ Per P. (mullwark)

>etwa 10 Hz darauf zugreift, habe ich hardwarebedingt einen Istwert nahe
>der 600. Also muss ich da mit 16 bit arbeiten. Soweit klar.

OK.

> Wenn ich
>dann die Abweichung berechne, und diese mit der P-Regler-Konstanten
>multipliziere, will ich ja einen Wert bekommen, der auf die OCR1n
>addiert, immer in die 8bit der OCR1n passen muss.

Wieso addiert? Du berechnest doch den PWM-Wert für die Stellgröße. D.h. 
OCR1n wird mit einem neuen Wert geladen.

>löst aber diese Frage nicht in Luft auf.) Demnach könnte ich diesen Wert
>ja wieder in eine 8bit-Variable stecken.

???

>Naja geht irgendwie, der Regler läuft, aber theoretisch ist das ja
>ziemlich schmutzig, denn 16 bit - 16 bit kann auch 32 bit werden.

Nö. Bestenfalls 17 Bit.

> 32 bit
>* 8 bit passt auch nicht immer in 32 bit, also müsste ich ja den Wert
>mathematisch korrekt in 64 bit schreiben.

Um Gottes Willen, bloß nicht! Da geht der AVR ganz ordentlich in die 
Knie!
64 Bit Arithmetik ist nicht sonderlich leistungsfähig auf dem AVR 
umgesetzt.

Man muss es nicht immer übertreiben und mal abschätzen, welche maximalen 
Zahlen auftreten können. Man wird selten 255 * 2^(32-1) rechnen, sondern 
die einzelnen Variabeln sind meist deutlich kleiner als der maximale 
Zahlenbereich. Für einen einfachen PI-Regler kommt man mit 16 oder 32 
Bit locker hin. Meistens reichen 16 Bit für die Variablen und casts auf 
32 Bit für die Berechnungen.

>Oder macht der Compiler aus meinem unansehnlichen Stück Code etwas
>Vernünftiges?

Das kann hier keiner sehen. Aber Compiler können nicht zaubern, den 
Überblick muss der Programmierer behalten.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Es gibt auch die Möglichkeit, den Wertebereich auf -1..1 zu normieren 
und dann Fixedpoint-Typen zu verwenden wie fract (16 Bits) oder long 
fract (32 Bits) aus stdfix.h.

von Per P. (mullwark)


Lesenswert?

be stucki schrieb:
> Hier füge ich Prüfungen und Korrekturen ein, die bei einem allfälligen
> Fehler den Wertebereich der Variable garantiert. So können bei darauf
> folgenden Berechnungen, die sich auf den Wertebereich der Variable
> verlassen, keine Überläufe entstehen.
>
> Diese Vorgehensweise hat den Nachteil, dass du bei einer Anpassung des
> Wertebereichs alle betroffenen Codezeilen manuell prüfen und evt.
> anpassen musst.

Gut, also doch Werte im Zweifelsfall eingrenzen, das hatte ich im Ansatz 
ja überlegt.


PittyJ schrieb:
> Ne, 16 Bit - 16 Bit wird höchstens 17 Bit. Zur Speicherung wird dann
> eine 32 Bit Variable benötigt, bei der aber 15 Bit unbenutzt sind.
>
> Multipliziert mit 8 Bit sind das jetzt 25 Bit. Dafür wird immer nur noch
> eine 32 Bit Variable benötigt, bei der dann immer noch 7 Bit unbenutzt
> sind.
>
> Also kein Zwang zu 64 Bit.

Argh, natürlich. Schön daß Du mich mit der Nase drauf stößt, da hatte 
ich ein Brett vorm Kopf. Danke.


Falk Brunner schrieb:
>> Wenn ich
>>dann die Abweichung berechne, und diese mit der P-Regler-Konstanten
>>multipliziere, will ich ja einen Wert bekommen, der auf die OCR1n
>>addiert, immer in die 8bit der OCR1n passen muss.
>
> Wieso addiert? Du berechnest doch den PWM-Wert für die Stellgröße. D.h.
> OCR1n wird mit einem neuen Wert geladen.

Ja, doch ich addiere da, erreiche aber dabei das Gleiche, was Du meinst.
Ich habe die Abweichung, die um 0 schwankt, mal positiv, mal negativ. 
Die mit meiner Konstanten multipliziert ergibt meine Veränderung des 
PWM-Wertes, nicht den PWM-Wert selber. Die Veränderung wird also auf den 
aktuellen Wert in OCR1n angewand.


Falk Brunner schrieb:
> Um Gottes Willen, bloß nicht! Da geht der AVR ganz ordentlich in die
> Knie!
> 64 Bit Arithmetik ist nicht sonderlich leistungsfähig auf dem AVR
> umgesetzt.

Genau, das hatte ich auch gelesen, deswegen kam ich ja auch ins Grübeln. 
Aber dank der Antworten habe ich jetzt gesehen, was ich falsch gemacht 
habe und wo noch Lernbedarf herrscht.


Falk Brunner schrieb:
> Man muss es nicht immer übertreiben und mal abschätzen, welche maximalen
> Zahlen auftreten können. Man wird selten 255 * 2^(32-1) rechnen, sondern
> die einzelnen Variabeln sind meist deutlich kleiner als der maximale
> Zahlenbereich. Für einen einfachen PI-Regler kommt man mit 16 oder 32
> Bit locker hin. Meistens reichen 16 Bit für die Variablen und casts auf
> 32 Bit für die Berechnungen.

Japp, danke, das ist eine klare Antwort, die hilft weiter. Also 
tatsächlich muss ich nicht den theoretisch möglichen Mathematischen Raum 
abdecken, es reicht, sich vorher vernünftig zu überlegen, welche Werte 
überhaupt mit der gegebenen Hardware auftreten können.


Johann L. schrieb:
> Es gibt auch die Möglichkeit, den Wertebereich auf -1..1 zu normieren
> und dann Fixedpoint-Typen zu verwenden wie fract (16 Bits) oder long
> fract (32 Bits) aus stdfix.h.

Danke für den Tipp, auch wenn ich glaube, daß ich in meinem konkreten 
Fall keine Normierung anwenden werde, die stdfix.h sollte ich mir bald 
ansehen.


:-)

: Bearbeitet durch User
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.