C command 'volatile' & why my head hurts

GPC2 script programming for Titan Two. Code examples, questions, requests.

C command 'volatile' & why my head hurts

Postby Yearoh » Sun Aug 30, 2020 8:58 pm

I can usually get a sense of what I need to do, but not with this issue..

Context: This is (hopefully) for gyro control. To cancel out noise & smooth the signal out low/high pass filters apparently can't do the job as well as a method called sensor fusion. (One of my high school classmates builds freaking missiles or something now, and he said so,, so that's good enough for me :innocent_smile_1: ).
ANYWAY it also has the benefit of stopping drift in the gyro or accelerometer in the DS4, and it seems to be what all the quadcopter drone guys and the robot building guys and some other guys who I forget now use. :smile0517:

Below is just a paste of (what I think is the C code). I'm thinking it may be a bit too much to expect my titan two to run it, but my plan is to cherry pick from it and jam it all together until it does something close to what I want.
Oh, and I was enthused to see it had PID loops in it as I'm familiar with them & it was what I thought I'd try to use when I first thought about trying gyro control.
So my method has been "Throw the code into Gtuner and see if it compiles, and comment out errors till it does, then go back and re-enable the problem lines one by one".
Firstly I commented out the #include ""MahonyAHRS.h" & the #include <math.h> lines, I've got the MahonyAHRS.h file and I'll paste it just below, but it's a separate file, and besides my 1st step is to comment all the error lines out until GtunerIV is happy & compiles it.
Next error is this 'volatile' command, and I googled it and when I read "The proper use of C's volatile keyword is poorly understood by many programmers." I got a bit scared :shocked: because if actual programmers have problems with it, then I know I'm going to cause a small fire somehow minimum.. But I think it's worth trying, because while it seemed daunting to me, they also run this on arduino's and alot of small form factor things..

But first here is a video that shows what the results can be. Also this is LESS impressive than most of them, it's just the only one that's not an hour long with some guy at a whiteboard writing so much math out that he goes thru 2 or 3 dry-erase markers.
Also thanks for your time/input, now I'm going to have a beer :smile0704: and cry myself to sleep :smile0525:



Here's the paste of theMahonyAHRS.h file (which I'm guessing is a header file or similar)
(I also was going to use code tags or similar, but I didn't know if it would mess with the formatting. :unsure
Code: Select all
//=====================================================================================================
// MahonyAHRS.h
//=====================================================================================================
//
// Madgwick's implementation of Mayhony's AHRS algorithm.
// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
//
// Date            Author            Notes
// 29/09/2011    SOH Madgwick    Initial release
// 02/10/2011    SOH Madgwick    Optimised for reduced CPU load
//
//=====================================================================================================
#ifndef MahonyAHRS_h
#define MahonyAHRS_h

//----------------------------------------------------------------------------------------------------
// Variable declaration

extern volatile float twoKp;            // 2 * proportional gain (Kp)
extern volatile float twoKi;            // 2 * integral gain (Ki)
extern volatile float q0, q1, q2, q3;    // quaternion of sensor frame relative to auxiliary frame

//---------------------------------------------------------------------------------------------------
// Function declarations

void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz);
void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az);

#endif
//=====================================================================================================
// End of file
//=====================================================================================================


Here's the paste of the MahonyAHRS.c file
Code: Select all
//=====================================================================================================
// MahonyAHRS.c
//=====================================================================================================
//
// Madgwick's implementation of Mayhony's AHRS algorithm.
// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
//
// Date            Author            Notes
// 29/09/2011    SOH Madgwick    Initial release
// 02/10/2011    SOH Madgwick    Optimised for reduced CPU load
//
//=====================================================================================================

//---------------------------------------------------------------------------------------------------
// Header files

#include "MahonyAHRS.h" 
#include <math.h>

//---------------------------------------------------------------------------------------------------
// Definitions

#define sampleFreq    512.0f            // sample frequency in Hz
#define twoKpDef    (2.0f * 0.5f)    // 2 * proportional gain
#define twoKiDef    (2.0f * 0.0f)    // 2 * integral gain

//---------------------------------------------------------------------------------------------------
// Variable definitions

volatile float twoKp = twoKpDef;                                            // 2 * proportional gain (Kp)
volatile float twoKi = twoKiDef;                                            // 2 * integral gain (Ki)
volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;                    // quaternion of sensor frame relative to auxiliary frame
volatile float integralFBx = 0.0f,  integralFBy = 0.0f, integralFBz = 0.0f;    // integral error terms scaled by Ki

//---------------------------------------------------------------------------------------------------
// Function declarations

float invSqrt(float x);

//====================================================================================================
// Functions

//---------------------------------------------------------------------------------------------------
// AHRS algorithm update

void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) {
    float recipNorm;
    float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; 
    float hx, hy, bx, bz;
    float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz;
    float halfex, halfey, halfez;
    float qa, qb, qc;

    // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation)
    if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) {
        MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az);
        return;
    }

    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
    if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {

        // Normalise accelerometer measurement
        recipNorm = invSqrt(ax * ax + ay * ay + az * az);
        ax *= recipNorm;
        ay *= recipNorm;
        az *= recipNorm;     

        // Normalise magnetometer measurement
        recipNorm = invSqrt(mx * mx + my * my + mz * mz);
        mx *= recipNorm;
        my *= recipNorm;
        mz *= recipNorm;   

        // Auxiliary variables to avoid repeated arithmetic
        q0q0 = q0 * q0;
        q0q1 = q0 * q1;
        q0q2 = q0 * q2;
        q0q3 = q0 * q3;
        q1q1 = q1 * q1;
        q1q2 = q1 * q2;
        q1q3 = q1 * q3;
        q2q2 = q2 * q2;
        q2q3 = q2 * q3;
        q3q3 = q3 * q3;   

        // Reference direction of Earth's magnetic field
        hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
        hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1));
        bx = sqrt(hx * hx + hy * hy);
        bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));

        // Estimated direction of gravity and magnetic field
        halfvx = q1q3 - q0q2;
        halfvy = q0q1 + q2q3;
        halfvz = q0q0 - 0.5f + q3q3;
        halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2);
        halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3);
        halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); 
   
        // Error is sum of cross product between estimated direction and measured direction of field vectors
        halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy);
        halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz);
        halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx);

        // Compute and apply integral feedback if enabled
        if(twoKi > 0.0f) {
            integralFBx += twoKi * halfex * (1.0f / sampleFreq);    // integral error scaled by Ki
            integralFBy += twoKi * halfey * (1.0f / sampleFreq);
            integralFBz += twoKi * halfez * (1.0f / sampleFreq);
            gx += integralFBx;    // apply integral feedback
            gy += integralFBy;
            gz += integralFBz;
        }
        else {
            integralFBx = 0.0f;    // prevent integral windup
            integralFBy = 0.0f;
            integralFBz = 0.0f;
        }

        // Apply proportional feedback
        gx += twoKp * halfex;
        gy += twoKp * halfey;
        gz += twoKp * halfez;
    }
   
    // Integrate rate of change of quaternion
    gx *= (0.5f * (1.0f / sampleFreq));        // pre-multiply common factors
    gy *= (0.5f * (1.0f / sampleFreq));
    gz *= (0.5f * (1.0f / sampleFreq));
    qa = q0;
    qb = q1;
    qc = q2;
    q0 += (-qb * gx - qc * gy - q3 * gz);
    q1 += (qa * gx + qc * gz - q3 * gy);
    q2 += (qa * gy - qb * gz + q3 * gx);
    q3 += (qa * gz + qb * gy - qc * gx);
   
    // Normalise quaternion
    recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;
}

//---------------------------------------------------------------------------------------------------
// IMU algorithm update

void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) {
    float recipNorm;
    float halfvx, halfvy, halfvz;
    float halfex, halfey, halfez;
    float qa, qb, qc;

    // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)
    if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {

        // Normalise accelerometer measurement
        recipNorm = invSqrt(ax * ax + ay * ay + az * az);
        ax *= recipNorm;
        ay *= recipNorm;
        az *= recipNorm;       

        // Estimated direction of gravity and vector perpendicular to magnetic flux
        halfvx = q1 * q3 - q0 * q2;
        halfvy = q0 * q1 + q2 * q3;
        halfvz = q0 * q0 - 0.5f + q3 * q3;
   
        // Error is sum of cross product between estimated and measured direction of gravity
        halfex = (ay * halfvz - az * halfvy);
        halfey = (az * halfvx - ax * halfvz);
        halfez = (ax * halfvy - ay * halfvx);

        // Compute and apply integral feedback if enabled
        if(twoKi > 0.0f) {
            integralFBx += twoKi * halfex * (1.0f / sampleFreq);    // integral error scaled by Ki
            integralFBy += twoKi * halfey * (1.0f / sampleFreq);
            integralFBz += twoKi * halfez * (1.0f / sampleFreq);
            gx += integralFBx;    // apply integral feedback
            gy += integralFBy;
            gz += integralFBz;
        }
        else {
            integralFBx = 0.0f;    // prevent integral windup
            integralFBy = 0.0f;
            integralFBz = 0.0f;
        }

        // Apply proportional feedback
        gx += twoKp * halfex;
        gy += twoKp * halfey;
        gz += twoKp * halfez;
    }
   
    // Integrate rate of change of quaternion
    gx *= (0.5f * (1.0f / sampleFreq));        // pre-multiply common factors
    gy *= (0.5f * (1.0f / sampleFreq));
    gz *= (0.5f * (1.0f / sampleFreq));
    qa = q0;
    qb = q1;
    qc = q2;
    q0 += (-qb * gx - qc * gy - q3 * gz);
    q1 += (qa * gx + qc * gz - q3 * gy);
    q2 += (qa * gy - qb * gz + q3 * gx);
    q3 += (qa * gz + qb * gy - qc * gx);
   
    // Normalise quaternion
    recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 *= recipNorm;
    q1 *= recipNorm;
    q2 *= recipNorm;
    q3 *= recipNorm;
}

//---------------------------------------------------------------------------------------------------
// Fast inverse square-root
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root

float invSqrt(float x) {
    float halfx = 0.5f * x;
    float y = x;
    long i = *(long*)&y;
    i = 0x5f3759df - (i>>1);
    y = *(float*)&i;
    y = y * (1.5f - (halfx * y * y));
    return y;
}

//====================================================================================================
// END OF CODE
//====================================================================================================
User avatar
Yearoh
Master Sergeant
Master Sergeant
 
Posts: 38
Joined: Fri Jul 10, 2020 9:32 am

Re: C command 'volatile' & why my head hurts

Postby Scachi » Sun Aug 30, 2020 9:24 pm

The T2 gpc2 language isn't 100% c compatible, it is gpc2 .. not C.
Not sure why you expect gtuner iv to compile it at all.
User avatar
Scachi
Brigadier General
Brigadier General
 
Posts: 3044
Joined: Wed May 11, 2016 6:25 am
Location: Germany

Re: C command 'volatile' & why my head hurts

Postby Yearoh » Mon Aug 31, 2020 2:45 am

Yearoh wrote: I'm thinking it may be a bit too much to expect my titan two to run it, but my plan is to cherry pick from it and jam it all together until it does something close to what I want.
...
So my method has been "Throw the code into Gtuner and see if it compiles, and comment out errors till it does, then go back and re-enable the problem lines one by one".
User avatar
Yearoh
Master Sergeant
Master Sergeant
 
Posts: 38
Joined: Fri Jul 10, 2020 9:32 am

Re: C command 'volatile' & why my head hurts

Postby J2Kbr » Mon Aug 31, 2020 10:46 pm

With C/C++ the volatile keyword is intended to prevent the compiler from applying optimizations on variables. The GPC2 compiler does not perform such optimizations, as such the volatile is not needed and can be removed from the code.

You can even use a null macro to automate this process:
Code: Select all
#define volatile 
ConsoleTuner Support Team
User avatar
J2Kbr
General of the Army
General of the Army
 
Posts: 20323
Joined: Tue Mar 18, 2014 1:39 pm

Re: C command 'volatile' & why my head hurts

Postby Yearoh » Wed Sep 09, 2020 6:44 am

J2Kbr wrote:With C/C++ the volatile keyword is intended to prevent the compiler from applying optimizations on variables. The GPC2 compiler does not perform such optimizations, as such the volatile is not needed and can be removed from the code.

You can even use a null macro to automate this process:
Code: Select all
#define volatile 


Great explanation, thanks!

Your definition on what it actually does in C/C++ is about a billion times clearer than anything else on the net as well :) :smile0517:
User avatar
Yearoh
Master Sergeant
Master Sergeant
 
Posts: 38
Joined: Fri Jul 10, 2020 9:32 am


Return to GPC2 Script Programming

Who is online

Users browsing this forum: No registered users and 118 guests