## Intro

I was a little surprised that no one had written a linear regression function and posted it on the web (perhaps I didn't look hard enough). I spent a little time researching and wrote the code displayed below.

A few comments:

Because the Arduino language cannot pass arrays to sub-functions, the code passes pointers instead. That's why the lrCoef variable is passed to the function but isn't passed back.

Also, it is apparently difficult to find the length of an array in the Arduino language. It's not impossible, but it appears to be easier to handle if the length is provided up front.

## The Linear Regression Code

// example

float y[4]={3, 4, 5, 6};

float x[4]={2.9875,3.9937,4.9925,5.9925};

float lrCoef[2]={0,0};

void setup(){

Serial.begin(9600);

delay(1000);

// call simple linear regression algorithm

simpLinReg(x, y, lrCoef, 4);

Serial.print(lrCoef[0],8);

Serial.print(" ");

Serial.println(lrCoef[1],8);

}

void loop(){

}

void simpLinReg(float* x, float* y, float* lrCoef, int n){

// pass x and y arrays (pointers), lrCoef pointer, and n. The lrCoef array is comprised of the slope=lrCoef[0] and intercept=lrCoef[1]. n is length of the x and y arrays.

// http://en.wikipedia.org/wiki/Simple_linear_regression

// initialize variables

float xbar=0;

float ybar=0;

float xybar=0;

float xsqbar=0;

// calculations required for linear regression

for (int i=0; i<n; i++){

xbar=xbar+x[i];

ybar=ybar+y[i];

xybar=xybar+x[i]*y[i];

xsqbar=xsqbar+x[i]*x[i];

}

xbar=xbar/n;

ybar=ybar/n;

xybar=xybar/n;

xsqbar=xsqbar/n;

// simple linear regression algorithm

lrCoef[0]=(xybar-xbar*ybar)/(xsqbar-xbar*xbar);

lrCoef[1]=ybar-lrCoef[0]*xbar;

}

Just what I was looking for :)

ReplyDelete(Doing some work with a robotic arm and potentiometer, and turns out it'd be better to calibrate the potentiometer at pre-set positions every once in a while. It can be done with a simple Excel-like program, but it's way more convenient for it to be inside the ardu to calibrate itself, a.k.a, get the Linear Regression Coefficients).

i'd like to suggest a minor change to your code, though.

if you need to do

a = a + somethingtoadd ;

it can be done simply as:

a += somethingtoadd ;

Shorter and a bit more elegant

http://arduino.cc/en/Reference/IncrementCompound

I'm glad you were able to make use of it. Thanks for the programming tip!

DeleteHi John,

ReplyDeleteGreat code

How can I use this code in real time to get the tendance of a potentiometer ?

Thanks you

I'm not sure what "tendance" is.

DeleteDuring compilation I have got an error:

ReplyDeletelin-regression_01.ino: In function 'void simpLinReg(float*, float*, float*, int)':

lin-regression_01.ino:76:1: error: unable to find a register to spill in class 'POINTER_REGS'

After long investigation I have moved the block:

// initialize variables

float xbar=0;

float ybar=0;

float xybar=0;

float xsqbar=0;

before void setup() section.

And now the code is compiled without problems.

I have found errors in the code.

ReplyDeleteAccording to

http://en.wikipedia.org/wiki/Simple_linear_regression

and compared to results from another math soft, the regression algorithm must be a bit changed.

Complete improved code:

// The Wikipedia example

// http://en.wikipedia.org/wiki/Simple_linear_regression

//

float x[15]={1.47, 1.5, 1.52, 1.55, 1.57, 1.6, 1.63, 1.65, 1.68, 1.7, 1.73, 1.75, 1.78, 1.8, 1.83};

float y[15]={52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46};

float lrCoef[2]={0,0};

// initialize variables

float sum_x=0;

float sum_y=0;

float sum_xy=0;

float sum_xx=0;

void setup()

{

Serial.begin(9600);

delay(200);

// call simple linear regression algorithm

simpLinReg(x, y, lrCoef, 15);

Serial.println("Wikipedia example should give: beta= 61.272, alpha= -39.062");

Serial.println();

Serial.println("Calculated coefficients:");

Serial.println(" BETA ALPHA");

Serial.print("f(x)= ");

//Serial.print("BETA: ");

Serial.print(lrCoef[0], 4);

//Serial.print(", ALPHA");

if (lrCoef[1]>0)

{

Serial.print("*x + ");

}

else

{

Serial.print("*x ");

}

Serial.println(lrCoef[1], 4);

}

void loop()

{

}

void simpLinReg(float* x, float* y, float* lrCoef, int n)

{

// pass x and y arrays (pointers), lrCoef pointer, and n. The lrCoef array is comprised of the slope=lrCoef[0] and intercept=lrCoef[1]. n is length of the x and y arrays.

// http://en.wikipedia.org/wiki/Simple_linear_regression

// calculations required for linear regression

for (int i=0; i<n; i++)

{

sum_x = sum_x+x[i];

sum_y = sum_y+y[i];

sum_xy = sum_xy+x[i]*y[i];

sum_xx = sum_xx+x[i]*x[i];

}

// simple linear regression algorithm

lrCoef[0]=(n*sum_xy-sum_x*sum_y)/(n*sum_xx-sum_x*sum_x);

lrCoef[1]=(sum_y/n)-((lrCoef[0]*sum_x)/n);

}

hello, i have some nonlinear calibration points for which i need output 0 to 5 vdc linear . can you please give me code.

ReplyDeleteThis comment has been removed by the author.

ReplyDelete