STM32 NaN-Wert für Gleitkommaoperationen auf globalen Variablen

Ich versuche, eine einfache Odometrie-Berechnungsschleife zu implementieren. Was in einer While-Schleife ausgeführt wird. Nachfolgend der Code:

double dx=0,dy=0,dw=0;
double v,w;

void computeOdom(){

    uint32_t ticknow=ticksms;  //global timer variable (gives current ticks in milliseconds)

    v=0.0;
    w=0.0;

    double dt = (ticknow - tickprev)/1000.0; //time elapsed since last odom update
    double res = (double)ENCODER_RESOLUTION; //Encoder Resolution ticks per revolution
    double leftvel = (double) ((((Left.position)/res) * 2 * M_PI))/dt; // Angular velocity of left wheel (rad/s)

    Left.position=0;
    double rightvel=(((double)Right.position)/ENCODER_RESOLUTION)* 2 * M_PI/dt; //Angular velocity of right wheel (rad/s)

    Right.position=0;

    double r_L= 0.260/2;
    double r_R= 0.260/2;

    v += (r_L/2.0) * leftvel; //Linear velocity of the robot
    v += (r_R/2.0) * rightvel;
    w += (r_R/0.610) * rightvel; //Angular velocity of the robot
    w -= (r_L/0.610) * leftvel;
    double tempx=0.0,tempy=0.0,tempz=0.0;

    if(!(w==0.0f)){
        dx += (double)(dt * v * arm_cos_f32(dw + dt*(w/2.0))); //The line where dx becomes NaN
        dy += (double)(dt * v * arm_sin_f32(dw + dt*(w/2.0))); //The line where dy becomes NaN
        dw += (dt * w);
    }
    else{
        dx += (double)(dt * v * arm_cos_f32(dw));
        dy += (double)(dt * v * arm_sin_f32(dw));
    }

    tickprev = ticknow;

    printf("prev_x: %f\n\r",dx);
}

Ich habe den Code mit GDB debuggt. Ich habe nach Ausnahmen gesucht, die ausgelöst werden, aber alles sieht gut aus. Und wenn ich den Code so ändere:

dx = (double)(dt * v * arm_cos_f32(dw + dt*(w/2.0))); 
dy = (double)(dt * v * arm_sin_f32(dw + dt*(w/2.0)));

Das NaN verschwindet. Ich habe versucht, temporäre lokale Variablen zu verwenden. cosIch habe die Funktionen und überprüft sin, indem ich sie auf lokalen Variablen ausgeführt habe, und sie funktionieren wie beabsichtigt. Da gibt es keine Probleme. Nur wenn ich die globalen Variablen inkrementieren möchte, tritt das Problem auf. dx, dyund dwsind persistente globale Variablen, die die Position verfolgen.

Dies ist die Version von arm-gcc, die ich verwende.

arm-none-eabi-gcc 5.4.1 20160609 (Veröffentlichung) [ARM/embedded-5-branch revision 237715]

Ich verwende das STM32F429 Discovery Board mit Arm-gcc-Toolchain und FPU wurde aktiviert. Das Deaktivieren der FPU hat keinerlei Auswirkung.

Irgendwelche Hinweise oder Vorschläge?

Sorry vergessen zu erwähnen. Ich verwende das STM32F429 Discovery Board mit Arm-gcc-Toolchain und FPU wurde aktiviert. Das Deaktivieren der FPU hat keinerlei Auswirkung.
Verwenden Sie floatnot double, wenn Sie FPU haben. doubleist kein Fließkommawert, deshalb hat FPU keine Wirkung.
Aktualisieren Sie Ihre Frage (Sie können sie bearbeiten), wenn Sie alle doublein geändert haben float.
@BenceKaulics doublekann auch 32-Bit-Single-Precision sein; Der Compiler könnte den C-Standard umgehen und doubledasselbe wie implementieren float.
@m.Alin Bist du sicher?
Ich habe gesehen, wie Compiler das getan haben. Aber ich habe es gerade überprüft und arm-gcc behandelt Doubles als 64-Bit-Daten.
Das ist der Grund für meinen Rat. "Gleitkommazahlen können einfache Genauigkeit ("float" in C) oder doppelte Genauigkeit ("double" in C) haben. Die FPU im Cortex-M4-Prozessor unterstützt Operationen mit einfacher Genauigkeit, aber nicht mit doppelter Genauigkeit. Wenn eine Berechnung mit doppelter Genauigkeit vorhanden ist, Der C-Compiler verwendet Laufzeitbibliotheksfunktionen, um die Berechnung in der Software durchzuführen. Um die beste Leistung zu erzielen, ist es am besten, die Berechnung nach Möglichkeit mit einfacher Genauigkeit durchzuführen. aus
@BenceKaulics das habe ich schon probiert. Vor der Verwendung von "doubles" habe ich "float32_t" verwendet (typedef'd als float in "arm_math.h"). Nachdem ich die Frage gestellt hatte, portierte ich den Code auf imx6q und es funktionierte dort wie beabsichtigt. Also keine ungültige Gleitkomma-Arithmetik. Außerdem muss ich erwähnen, dass die Werte, um die ich dx, dy und dw inkrementiere, häufiger Null sind. Wenn die Encoder nicht ticken.
@Asusrog Entschuldigung, ich habe es nicht als Lösung vorgeschlagen, deshalb habe ich es nur als Kommentar gepostet. Ich wollte Sie nur darauf aufmerksam machen, dass Sie floatTypen verwenden sollten, um die Vorteile der FPU zu nutzen.

Antworten (2)

@MarkoBursic Vielen Dank, dass Sie darauf hingewiesen haben, Mann. Weißt du, tatsächlich habe ich seit gestern so viele Änderungen an diesem winzigen Stück Code vorgenommen, dass ich anfing, den Überblick über die Idiotizität zu verlieren :). Jedenfalls habe ich es irgendwie geschafft, das Problem zu lösen. Gedulde dich eine Weile mit mir. Mit gdb ging ich den Code noch einmal durch und überprüfte jede Variable vor und nach jeder Ausführungszeile. leftvel war ursprünglich ein nan (0x400000), als dt null war, da während der ersten Iteration tickprev und ticknow gleich waren (0). Da leftvel und rightvel lokale Variablen sind, blieben die nan-Werte nicht erhalten. Und dieser Nan-Wert wurde mit meinen globalen Variablen multipliziert, die persistent sind. Also habe ich diese Zeile hinzugefügt:

if(isnan(leftvel) || isnan(rightvel))
    return;

Kurz vor der Multiplikation mit dx,dy und dw. Vielen Dank für Ihre Vorschläge @MarcoBursic @BenceKaulics

Lesen Sie es als längeren Kommentar:

Warum analysieren Sie die Ergebnisse ständig, um sie zu verdoppeln? Viele Zwischenergebnisse könnten auf const gesetzt werden.
double res = (double)ENCODER_RESOLUTION;?? 1. Zeitverschwendung,
double rightvel=(((double)Right.position)/ENCODER_RESOLUTION)* 2 * M_PI/dt;2. Zeitverschwendung,

Das sollte das gleiche sein, aber es ist:

double leftvel = (double) ((((Left.position)/res) * 2 * M_PI))/dt;   

2 * M_PI/ENCODER_RESOLUTION könnte eine Konstante sein.

uint32_t ticknow=ticksms;   
double dt = (ticknow - tickprev)/1000.0; //time elapsed since last odom update  

Sieh mal, du hast uint32 , dann dividierst du durch Float-Nummer 1000.0 und du erwartest das Doppelte. double((ticknow - tickprev))/1000.0. Sie parsen wo nicht unbedingt, aber Sie vergessen zu parsen wo Sie sollten.

Daran ist nichts auszusetzen (ticknow - tickprev)/1000.0. Die Subtraktion erfolgt auf uint32_t, was in Ordnung zu sein scheint. Das Ergebnis der Subtraktion ist vom Typ uint32_t. Dies wird dann auf doppelt ausgeglichen, da 1000.0es sich um ein doppeltes Literal handelt (nicht float). Sie müssen nichts ändern. Und ich habe keine Ahnung, was das double((ticknow - tickprev))/1000.0bedeuten soll, aber es ist kein gültiges C.