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.
ReplyDeleteHi,
ReplyDeleteThank you for the code.
I am not familiar with coding. I need help in getting the code for the following linear regression equation. y=(8*10^-5)x^2+0.1873x+46.131 for arduino program. x is an analog value to give an ouput y.
You assistance will be helpful
Thank you