So I started with a library program that read data from my sharp IR distance sensor. I modified the program to print the data to teraterm, with some advice from Erik O. Then I modified it to turn a servo based on the sensor data. Then I modified it with a servo calibration routine. So far so good. I noticed a lot of chatter in the readings and the servo movement, so I figured I should filter the sensor data. I am shooting from the hip on this one. First I added what I call a spike filter which detects and averages out sensor data spikes. That filter data goes into an averaging filter which averages the last 5 values. PWM is then calculated with that despiked averaged reading. It all seems to work, though I do still get some chatter and spikes. I could smooth it out by averaging more values, and changing the threshold of my spike filter, increasing overall latency. I am wondering if somebody could have a quick look at my code and see if I am doing a good job so far :) I am curious if there is a more standard way to do this. After I created my method I decided to search the library for a filter routine. I did find an analog filter of some sort, but don't really understand it at this point. So far I am proud of my little achievements here! I will post my code below. Thanks!
/* Sharp Sensor Test
* Connections:
* PIN15 (White) Signal
* 5.0V USB Out Sensor Power (+)
* GND Sensor Gnd (-)
* Place a 10uf cap close the the sensor between VCC nad GND
*/
#include "mbed.h"
/* Standard LED's on the mbed */
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
Serial pc(USBTX, USBRX); // tx, rx /
/* Our signal line is PIN15 */
AnalogIn Sensor(p15);
PwmOut servo(p21);
int main() {
servo.period(0.020); // servo requires a 20ms period
/**********Begin Servo Range Calibration******************/
float UPL = 0.0021; /*Initialize PWM Upper Limit*/
float LPL = 0.0009; /*Initialize PWM Lower Limit*/
char c = 'z'; /*Initialize char for calibtation inputs*/
/*Calibration Loop for Upper PWM Limit*/
servo.pulsewidth(UPL);
pc.printf("\n***Calibration of Upper PWM Limit***");
pc.printf("\nPress 'u' to increase, 'd' to decrease, 'q' to calibrate Lower PWM Limit\n");
while( c != 'q')
{
c = pc.getc();
if(c == 'u') UPL += 0.0001;
if(c == 'd') UPL -= 0.0001;
servo.pulsewidth(UPL);
}
/*Calibration Loop for Lower PWM Limit*/
servo.pulsewidth(LPL);
pc.printf("***Calibration of Lower PWM Limit***");
pc.printf("Press 'u' to increase, 'd' to decrease, 'q' to quit\n");
c = 'z';
while( c != 'q')
{
c = pc.getc();
if(c == 'u') LPL += 0.0001;
if(c == 'd') LPL -= 0.0001;
servo.pulsewidth(LPL);
}
pc.printf("\nUpper PWM Limit: %f Lower PWM Limit: %f",UPL,LPL);
/*converting Analog pin reading (0-1) to servo PWM range as calibrated*/
/*sensor producing pin range from .06 to .85*/
float x; /*extrapolated lower limit of PWM range*/
float y; /*extrapolated upper limit of PWM range*/
float UAL = 0.850; /*observed upper limit of pin reading from sensor*/
float LAL = 0.060; /*observed lower limit of pin reading from sensor*/
x = UPL-(((UAL)*(UPL-LPL))/(UAL-LAL)); /*extrapolation for x*/
y = LPL+(((1-LAL)*(UPL-LPL))/(UAL-LAL)); /*extparolation for y*/
/*checking if the extrapolation is correct*/
pc.printf("\nFor UAL=.850, UPL: %f=%f",UPL,(0.850*(y-x)+x));
pc.printf("\nFor LAL=.060, LPL: %f=%f",LPL,(0.060*(y-x)+x));
c = 'z';
while (c != 'y')
{
pc.printf("\nenter 'y' to continue\n");
c = pc.getc();
}
/****************End Servo Range Calibration********************/
/****************Begin filtering sensor readings****************/
int count = 4; /*count for spike filter*/
float ss = 0.60; /*spike error threshold percentage*/
float sp[5]={}; /*initial array for sensor values for spike filter*/
float cum = 0; /*initial cumulative value for moving average filter*/
float old = 0; /*initial value for placeholder used in moving average filter*/
int i = 0; /*initial value for counter in moving average filter*/
int as = 5; /*size of array for moving average filter*/
float sensvals[5]={};
float sensvalavg;
float avg0134;
float sensval;
while(count>0) /*loop to fill sp[0-3] with sensor values, done to initialize the Spike filter*/
{
sensval=Sensor.read();
sp[count-1]=sensval;
count-=1;
}
float PWMVal;
while (1) {
/*****filter continues here inside main loop**********/
sensval=Sensor.read();
sp[4]=sp[3]; /*moving values down the array and filling sp[0] with the newest data point*/
sp[3]=sp[2];
sp[2]=sp[1];
sp[1]=sp[0];
sp[0]=sensval;
avg0134 = (sp[0]+sp[1]+sp[3]+sp[4])/4; /*taking avg of the array values except position 2*/
if (sp[2] > (1+ss)*avg0134 || sp[2] < (1-ss)*avg0134) /*comparing position 2 with the avg to see*/
{ /*if it is > or < the ss percentage, if so, use avg0134*/
sp[2]=avg0134;
pc.printf("\n***********SPIKE************SPIKE************");
}
old = sensvals[i];
/* debug pc.printf("\ncum1= %f old1=%f",cum,old); */
cum = cum+sp[2]-old; /*moving average filter*/
sensvals[i]=sp[2]; /*moving average filter*/
i+=1; /*moving average filter*/
if (i>as-1) i=0; /*moving average filter*/
/* debug pc.printf("\n%f %f %f %f %f",sp[0],sp[1],sp[2],sp[3],sp[4]);
pc.printf("\n%f %f %f %f %f %f %f %f %f %f",sensvals[0],sensvals[1],sensvals[2],sensvals[3],sensvals[4]
,sensvals[5],sensvals[6],sensvals[7],sensvals[8],sensvals[9]);
pc.printf("\ncum= %f old=%f i=%f sensvals[i]=%f sp2= %f",cum,old,i,sensvals[i],sp[2]); */
sensvalavg = cum/as; /*moving average filter*/
/****************End filtering sensor readings******************/
PWMVal=(sensvalavg*(y-x)+x); /*conversion to PWM*/
if (PWMVal >=UPL) PWMVal=UPL; /*limits servo movement per calibration routine*/
if (PWMVal <=LPL) PWMVal=LPL; /*limits servo movement per calibration routine*/
servo.pulsewidth(PWMVal); /*sending out the final PWM value*/
/* debug pc.printf("\n SenseValAvg: %f PWMVal: %f\n",sensvalavg,PWMVal); */
/*
* Efficient way as the object gets closer to the
* sensor the LED's will turn on, as the object
* moves away they count back down. Original by Dan Ros
*/
led1 = (Sensor > 0.2) ? 1 : 0;
led2 = (Sensor > 0.4) ? 1 : 0;
led3 = (Sensor > 0.6) ? 1 : 0;
led4 = (Sensor > 0.8) ? 1 : 0;
/*
* A cool effect when the full range of the sensor
* has been reached, all LED's flash
*/
if (Sensor >= 0.9) {
led1 = led2 = led3 = led4 = 1;
wait(0.02);
led1 = led2 = led3 = led4 = 0;
wait(0.02);
}
wait(0.002);
}
}
So I started with a library program that read data from my sharp IR distance sensor. I modified the program to print the data to teraterm, with some advice from Erik O. Then I modified it to turn a servo based on the sensor data. Then I modified it with a servo calibration routine. So far so good. I noticed a lot of chatter in the readings and the servo movement, so I figured I should filter the sensor data. I am shooting from the hip on this one. First I added what I call a spike filter which detects and averages out sensor data spikes. That filter data goes into an averaging filter which averages the last 5 values. PWM is then calculated with that despiked averaged reading. It all seems to work, though I do still get some chatter and spikes. I could smooth it out by averaging more values, and changing the threshold of my spike filter, increasing overall latency. I am wondering if somebody could have a quick look at my code and see if I am doing a good job so far :) I am curious if there is a more standard way to do this. After I created my method I decided to search the library for a filter routine. I did find an analog filter of some sort, but don't really understand it at this point. So far I am proud of my little achievements here! I will post my code below. Thanks!