Problem with Plotting a 10th order polynomial

Hi, I am trying to plot a time-dependent 10th order polynomial on an Arduino Uno. I have two issues:

  1. When trying to use the time as the x values of the polynomial, it breaks down almost immediately, giving infinite value.

  2. when running a periodical for loop (786ms duration) the first part of the polynomial gets printed fine, but it suddenly breaks down in the last part of the plot.

I can't locate the issue and any help would be much appreciated.

My code is below:

// Initialise array with polynomial coefficients
float p [11] = {4.29416358858623e-22,  -1.69783608630414e-18, 2.81414641213559e-15,  -2.53121208332960e-12, 1.33462703081654e-09,  -4.14674269957196e-07, 7.19480339465934e-05,  -0.00606130114242071,  0.194386444345812, -6.33900955158736, 71.4696409901174};
// Set current to 0
float current = 0;
// Initialise time

unsigned long last_timestamp;
unsigned long time_now  = 0;
unsigned long elapsed_time = 0;

void setup() {
  // put your setup code here, to run once:

  last_timestamp = millis();

  Serial.begin(9600);


}

void loop() {


  last_timestamp = time_now;

  time_now = millis();

  elapsed_time = time_now - last_timestamp;

  //  if (elapsed_time >= 1) {

  for (float i = 0.0;  i <= 786.0; i++) {
    //current = p[0] * pow(time_now, 10) + p[1] * pow(time_now, 9) + p[2] * pow(time_now, 8) + p[3] * pow(time_now, 7) + p[4] * pow(time_now, 6) + p[5] * pow(time_now, 5) + p[6] * pow(time_now, 4) + p[7] * pow(time_now, 3) + p[8] * pow(time_now, 2) + p[9] * time_now + p[10];

    current = p[0] * pow(i, 10.0) + p[1] * pow(i, 9.0) + p[2] * pow(i, 8.0) + p[3] * pow(i, 7.0) + p[4] * pow(i, 6.0) + p[5] * pow(i, 5.0) + p[6] * pow(i, 4.0) + p[7] * pow(i, 3.0) + p[8] * pow(i, 2.0) + p[9] * i + p[10];

    Serial.println(current);
  }

The float data type with an Uno has only 6 or 7 digits of precision. See

Paragraph #4

and using a double won't help either as on a UNO it's also 32 bits only

you probably need a more capable 32 bit Arduino (MKR, ESP, Teensy, ...) and use a double instead of a float

not seeing NaN

does this help

   2  0 10         0.00000000000000000044             1023.999   0.000000000000000000000429416350
   2  1  9        -0.00000000000000086885              512.000  -0.000000000000000001697836100000
   2  2  8         0.00000000000071955240              256.000   0.000000000000002814146500000000
   2  3  7        -0.00000000032327546000              128.000  -0.000000000002531212000000000000
   2  4  6         0.00000008509282900000               64.000   0.000000001334627100000000000000
   2  5  5        -0.00001318447900000000               32.000  -0.000000414674280000000000000000
   2  6  4         0.00113798380000000000               16.000   0.000071948030000000000000000000
   2  7  3        -0.04735241500000000000                8.000  -0.006061301100000000000000000000
   2  8  2         0.73019320000000000000                4.000   0.194386440000000000000000000000
   2  9  1       -11.94782600000000000000                2.000  -6.339009800000000000000000000000
   2 10  0        59.52181600000000000000                1.000  71.469643000000000000000000000000
// Initialise array with polynomial coefficients
float p [] = {
     4.29416358858623e-22,
    -1.69783608630414e-18,
     2.81414641213559e-15,
    -2.53121208332960e-12,

     1.33462703081654e-09,
    -4.14674269957196e-07,
     7.19480339465934e-05,
    -0.00606130114242071,

     0.194386444345812,
    -6.33900955158736,
    71.4696409901174
};
const int Np = sizeof(p) / sizeof(float);

float sum;
float x;
int   q;
float nF;
float qF;

char s[80];
char t[50];
char u[50];
char v[50];

// -----------------------------------------------------------------------------
void setup() {
    Serial.begin(115200);

#define I  1
    for (int i = 1;  i <= 786; i++) {
        Serial.println ();

        sum = 0;
        for (int n = 0; n < Np; n++)  {
            q    = Np-1-n;
            x    = pow((float)i, (float)q);
            sum += p[n] * x;

            dtostrf (sum,  30, 20, t);
            dtostrf (x,    20,  3, u);
            dtostrf (p[n], 34, 30, v);

            sprintf (s, "%4d %2d %2d %s %s %s", i, n, q, t, u, v);
            Serial.println (s);
        }
    }
}

void loop() {
}

You might get better results with a spreadsheet program. It looks like the first 100 results are very near to 71.4696409901and then it starts climbing. At 786 you get to 38646026.6176135

How did you arrive at those coefficients?

Even using type double, with 15 significant digits, the polynomial is not "10th order". For example, when t=1, adding 10^-22 to 100, the result is still 100.

For plotting on the Uno, be realistic and treat it as 5th order, taking only the last five terms, and for better accuracy, use Horner's method to evaluate the result.

Only taking the last five terms doesn't solve it unfortunately, it gives a different polynomial that decays infintely.

Why not tell us what you are really trying to do? The current approach will never work.

I'm trying to do exactly what I described in the post. Basically, I fitted a polynomial to a set of data collected by a current sensor. the 10th order polynomial fit is the one I found to be more accurate and with the lowest degree possible (that I could accept for accuracy). Now I want to turn that polynomial into a signal to send from the arduino to a motor driver. As a first step, and to double check that my polynomial fit is correct, I am just trying to plot it periodically. Hopefully that explains it better.

What data? How did you perform the fit?

the 10th order polynomial fit is the one I found to be more accurate

How did you evaluate the polynomial and determine the accuracy?

Current sensor data, it senses the mA going through a motor coil during periodic operation. I used matlab polyfit function to find the fit and took the coefficients from there. I did some trial and error sweeping through polynomials from 5th degree to 15th. I have to specifiy that when I plot the polynomial in the for loop it plots it fine for the first portion and it seems to go "noisy" during the last portion of the calculation, to then go back to being fine and "noisy" again periodically.

If you want to implement something that actually works on Arduino, you will have to change a number of your expectations.

Practically speaking, data collected from consumer grade sensors is usually limited in accuracy to 1% or 0.1% of full scale value, and for motor control, that level of accuracy is usually fine.

The data is not the problem here, the sensor of the motor driver is very accurate, I just want to be able to simulate a periodic signal with Arduino, and I'm under the impression that that is realistic?

Her's a plot of what the Uno is currently ouputting, for reference. And as you can see, the problem only seems to be for the last portion of the cycle, which to me is weird.

No, it is not.

The problem you are having with evaluating that polynomial is unrealistic expectations.

As you can see from the graph, the polynomial is being evaluated fine for the first portion, so I don't think it's that unrealistic. The fit I obtained in matlab is very close to the data I collected so there's some evidence that it can be done, but thanks for your time anyway

can you show us that data?
How did you acquire it?
I assume the value and time are both accurate to over 25 decimal places?

Yes, as we've tried to explain. Then it blows up due to lack of numerical precision and the inappropriate method of constructing and evaluating the polynomial.

If this is for a class, your instructor is failing to prepare you for real world applications.

If this is a 10th order polynome, you are in big trouble...
It cannot have more than 10 tops and dales.
You have hundreds of them....
And I guess you need to find another way to do your calcs. You can rewrite a polynome to a multiplication of 10 factors (instead of 10 additions). Maybe that can help you...
Since you say it is periodic... why not add a sine function in the mix?
To fit a periodic function you need an infinite amount of polynomes... (some mathematicians have proven that and it is known as Taylor expansion). Perhaps one of them is named Taylor...

Besides all the excellent advice you've already been given, another approach would be to break up your data into smaller sets and fit a separate curve to each set.

Edit to add: that would probably allow you to use polynomials of much lower order (in case that wasn't obvious :slight_smile: )

No, you need a better method!!!