Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #15 on: March 09, 2012, 10:05:05 am » |
Thanks to Scott Wielenberg's timely reply to my email enquiry to Maxbotix tech support, there is no need for the calibration subroutine when the RX pin is used. Scott said, "On the first commanded range reading after power-up, the sensor will calibrate."
|
|
|
|
|
Logged
|
|
|
|
|
Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #16 on: March 14, 2012, 08:16:30 pm » |
Here is code for two Maxbotix sensors with a mode filter. It seems a little slow, and even with the mode filter the sensors read 2" to 4" short. Any input is appreciated. /* Two LV Maxsonar EZ1 sensors with mode filter by Dan Shields Adapted from Jason Lessels, Bruce Allen, and Bill Gentles. */ int button = 7; int PIRleft = A3; int LEDleft = 12; int PIRright = A4; int LEDright = 8; int motorL = 9; //forward left int motorR = 10; //forward right int SonarR = 5; int TriggerR = 2; int SonarL = 3; int TriggerL = 4; int val; // variable for reading the pin status int val2; // variable for reading the delayed status int buttonState = 0; // variable to hold the button state int robotMode = 0;
int arraysizeR = 9; //quantity of values to find the median (sample size). Needs to be an odd number //declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer int rangevalueR[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; long pulseR; int modER; long valueR = 0; int arraysizeL = 9; //quantity of values to find the median (sample size). Needs to be an odd number //declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer int rangevalueL[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; long pulseL; int modEL; long valueL = 0;
void setup() { pinMode(button, INPUT); pinMode(LEDright, OUTPUT); pinMode(LEDleft, OUTPUT); pinMode(TriggerL, OUTPUT); pinMode(TriggerR, OUTPUT); pinMode(motorR, OUTPUT); pinMode(motorL, OUTPUT); Serial.begin(9600); }
void loop() {
distCalcL(); distCalcR(); if (modEL >= 14 && modEL <= 36) { digitalWrite(LEDleft, HIGH); } else if (modEL < 14 || modEL > 36) { digitalWrite(LEDleft, LOW); } if (modER >= 14 && modER <= 36) { digitalWrite(LEDright, HIGH); } else if (modER < 14 || modER > 36) { digitalWrite(LEDright, LOW); } }
int distCalcL() {
pinMode(SonarL, INPUT);
for(int i = 0; i < arraysizeL; i++) { digitalWrite(TriggerL, HIGH); pulseL = pulseIn(SonarL, HIGH); rangevalueL[i] = pulseL/147; delay(10); } // Serial.print("Unsorted: "); // printArrayL(rangevalueL,arraysizeL); isortL(rangevalueL,arraysizeL); //Serial.print("Sorted: "); // printArrayL(rangevalueL,arraysizeL); modEL = modeL(rangevalueL,arraysizeL);
Serial.print("The mode/medianL is: "); Serial.print(modEL); Serial.println(); delay(100); digitalWrite(TriggerL, LOW); } /*-----------Functions------------*///Function to print the arrays. void printArrayL(int *a, int n) {
for (int i = 0; i < n; i++) { Serial.print(a[i], DEC); Serial.print(' '); }
Serial.println();
} //Sorting function // sort function (Author: Bill Gentles, Nov. 12, 2010) void isortL(int *a, int n){ // *a is an array pointer function
for (int i = 1; i < n; ++i) { int j = a[i]; int k; for (k = i - 1; (k >= 0) && (j < a[k]); k--) { a[k + 1] = a[k]; } a[k + 1] = j; }
} //Mode function, returning the mode or median. int modeL(int *x,int n){
int i = 0; int countL = 0; int maxCountL= 0; int modeL = 0; int bimodalL; int prevCountL = 0;
while(i<(n-1)){
prevCountL=countL; countL=0;
while(x[i]==x[i+1]){
countL++; i++; }
if(countL>prevCountL&countL>maxCountL){ modeL=x[i]; maxCountL=countL; bimodalL=0; } if(countL==0){ i++; } if(countL==maxCountL){//If the dataset has 2 or more modes. bimodalL=1; } if(modeL==0||bimodalL==1){//Return the median if there is no mode. modeL=x[(n/2)]; }
return modeL; } } int distCalcR() {
pinMode(SonarR, INPUT);
for(int i = 0; i < arraysizeR; i++) { digitalWrite(TriggerR, HIGH); pulseR = pulseIn(SonarR, HIGH); rangevalueR[i] = pulseR/147; delay(10); } // Serial.print("Unsorted: "); // printArrayR(rangevalueL,arraysizeL); isortR(rangevalueR,arraysizeR); //Serial.print("Sorted: "); // printArrayR(rangevalueL,arraysizeL); modER = modeR(rangevalueR,arraysizeR);
Serial.print("The mode/medianR is: "); Serial.print(modER); Serial.println(); delay(100); digitalWrite(TriggerR, LOW); } /*-----------Functions------------*///Function to print the arrays. void printArrayR(int *a, int n) {
for (int i = 0; i < n; i++) { Serial.print(a[i], DEC); Serial.print(' '); }
Serial.println();
} //Sorting function // sort function (Author: Bill Gentles, Nov. 12, 2010) void isortR(int *a, int n){ // *a is an array pointer function
for (int i = 1; i < n; ++i) { int j = a[i]; int k; for (k = i - 1; (k >= 0) && (j < a[k]); k--) { a[k + 1] = a[k]; } a[k + 1] = j; }
} //Mode function, returning the mode or median. int modeR(int *x,int n){
int i = 0; int countR = 0; int maxCountR= 0; int modeR = 0; int bimodalR; int prevCountR = 0;
while(i<(n-1)){
prevCountR=countR; countR=0;
while(x[i]==x[i+1]){
countR++; i++; }
if(countR>prevCountR&countR>maxCountR){ modeR=x[i]; maxCountR=countR; bimodalR=0; } if(countR==0){ i++; } if(countR==maxCountR){//If the dataset has 2 or more modes. bimodalR=1; } if(modeR==0||bimodalR==1){//Return the median if there is no mode. modeR=x[(n/2)]; }
return modeR; } }
|
|
|
|
|
Logged
|
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #17 on: March 14, 2012, 10:05:28 pm » |
I find your code a little cumbersome to read, as there seems to be a lot of extraneous matter in there. IE, you seem to have taken the code for one sonar and simply cloned it and given it a different name for the other, same for the sorts, the prints, and the mode functions. I did notice what appears to be an error, twice repeated, due to cloning. if(countL>prevCountL&countL>maxCountL){
s/b:
if( countL > prevCountL && countL > maxCountL ) {
or as I prefer:
if( (countL > prevCountL) && (countL > maxCountL) ) {
Difficult to read when all cluttered together, plus I hate precedence tables. As I mentioned earlier, my Maxsonars also read about 2" too low. I don't think filtering or averaging will solve that problem. It's possibly/probably a problem with the units themselves.
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #18 on: March 14, 2012, 10:13:06 pm » |
I know what you're saying about being difficult to read. I apologize for that. Also about the cloned code with different names, you have to do that or it doesn't work for the second sensor, the right in this case.
|
|
|
|
|
Logged
|
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #19 on: March 14, 2012, 10:31:25 pm » |
As 3 examples, you're passing your data arrays into the functions and using only local variables, the only difference is L or R in the function name.
void printArrayR(int *a, int n) int modeR( int *x,int n ) void isortR(int *a, int n)
The following can also be changed to pass in the sonar i.d. and int rangevalue[] R,L arrays
int distCalcL()
It can be simplified.
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #20 on: March 14, 2012, 11:17:46 pm » |
FWIW, I rewrote your sketch, since I plan to try it on my own robot, which has both EZ1 and EZ4 - tomorrow, as I'm beat tonight. I need to change the I/O pins, etc. It compiles ok. The only thing I'm not too sure about is your holding the txpin high for the entire duration of ALL sonar pings. The usual method is to pulse it low after triggering, but I imagine the code will get around the loop ok, as it is, before the next pulse comes out. Also, thanks for the sort code, I needed that :-). /* Two LV Maxsonar EZ1 sensors with mode filter by Dan Shields Adapted from Jason Lessels, Bruce Allen, and Bill Gentles. */ int button = 7; int PIRleft = A3; int LEDleft = 12; int PIRright = A4; int LEDright = 8; int motorL = 9; //forward left int motorR = 10; //forward right int SonarR = 5; int TriggerR = 2; int SonarL = 3; int TriggerL = 4; int val; // variable for reading the pin status int val2; // variable for reading the delayed status int buttonState = 0; // variable to hold the button state int robotMode = 0;
int arraysizeR = 9; //quantity of values to find the median (sample size). Needs to be an odd number //declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer int rangevalueR[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; //long pulseR; int modER; //long valueR = 0;
int arraysizeL = 9; //quantity of values to find the median (sample size). Needs to be an odd number //declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer int rangevalueL[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; //long pulseL; int modEL; //long valueL = 0;
void setup() { pinMode(button, INPUT); pinMode(LEDright, OUTPUT); pinMode(LEDleft, OUTPUT); pinMode(TriggerL, OUTPUT); pinMode(TriggerR, OUTPUT); pinMode(motorR, OUTPUT); pinMode(motorL, OUTPUT); Serial.begin(9600); }
void loop() { distCalcL(); distCalcR(); if (modEL >= 14 && modEL <= 36) { digitalWrite(LEDleft, HIGH); } else if (modEL < 14 || modEL > 36) { digitalWrite(LEDleft, LOW); } if (modER >= 14 && modER <= 36) { digitalWrite(LEDright, HIGH); } else if (modER < 14 || modER > 36) { digitalWrite(LEDright, LOW); } }
int distCalcL() { read_sonar( TriggerL, SonarL, rangevalueL, arraysizeL); isort( rangevalueL, arraysizeL); modEL = mode( rangevalueL, arraysizeL); display_output("The mode/medianL is: ", modEL); delay(100); }
int distCalcR() { read_sonar( TriggerR, SonarR, rangevalueR, arraysizeR); isort( rangevalueR, arraysizeR); modER = mode( rangevalueR, arraysizeR); display_output("The mode/medianR is: ", modER); delay(100); }
/*******************************************/ void display_output( char* msg, int mode) { Serial.print(msg); Serial.print(mode); Serial.println(); }
/**********************************************************/ void read_sonar(int txpin, int rxpin, int* ary, int asiz) { pinMode(rxpin, INPUT);
for(int i = 0; i < arraysizeL; i++) { digitalWrite(txpin, HIGH); ary[i] = pulseIn(rxpin, HIGH) / 147; delay(10); } digitalWrite(txpin, LOW); }
/*-----------Functions------------*/ //Function to print the arrays. //void printArray(int *a, int n) //{ // for (int i = 0; i < n; i++) { // Serial.print(a[i], DEC); // Serial.print(' '); // } // Serial.println(); //}
// sort function (Author: Bill Gentles, Nov. 12, 2010) void isort(int *a, int n) { int i, j, k; // *a is an array pointer function for( i=1; i < n; ++i) { j = a[i]; for( k = i - 1; (k >= 0) && (j < a[k]); k--) { a[k + 1] = a[k]; } a[k + 1] = j; } }
// Mode function, returning the mode or median. int mode( int *x, int n ) { int i=0, countL=0; int prevCountL=0, maxCountL=0; int modeL=0; int bimodalL;
while( i < (n-1) ) { prevCountL=countL; countL=0;
while( x[i] == x[i+1]) { countL++; i++; } if( (countL > prevCountL) & (countL > maxCountL) ) { modeL=x[i]; maxCountL=countL; bimodalL=0; } if(countL==0) { i++; } if(countL==maxCountL) { //If the dataset has 2 or more modes. bimodalL=1; } if( (modeL==0) || (bimodalL==1) ) { //Return the median if there is no mode. modeL=x[(n/2)]; } return modeL; } }
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #21 on: March 14, 2012, 11:22:48 pm » |
oops, need to change arraysizeL to asiz inside function
void read_sonar(int txpin, int rxpin, int* ary, int asiz)
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #22 on: March 15, 2012, 12:14:35 pm » |
Yea, I wondered about where exactly and low long to hold the TX pins high. I'm gonna play with it a little more tonight when I get home from work. Also about the mode filter, you're right that it doesn't improve the accuracy of the sensors at all, but it does do a pretty good job of getting rid of the random readings that I get that are way off. I'm also adding more code to this for my robot. For me, I add one sensor at a time. It's easier to debug. I'm adding a couple PIR sensors and a push button to turn the robot on and to turn it in standby mode.
|
|
|
|
|
Logged
|
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #23 on: March 15, 2012, 02:03:57 pm » |
Ok, I learned a lot about Maxsonars today, via courtesy of your code. :-) I have both EZ1 and EZ4. The target is a cardboard box located exactly 24" from the front of the sonars. It's a small robot, so the sonars are only 7" above the floor. I did change the ping routine so the trigger pin is only held high for 20-usec. First the data. Using 10-msec delay between samples (original) ----------------------------------- EZ1 mode/medianL is: 21 0 0 0 0 21 21 21 21 21 EZ4 mode/medianR is: 20 0 0 0 0 20 23 23 23 23
Using 100-msec delay between samples ------------------------------------ sonars head-on to 8"x8" box: EZ1 mode/medianL is: 21 21 21 21 21 21 21 21 21 21 EZ4 mode/medianR is: 23 20 23 23 23 23 23 23 23 23
sonars aimed up approx 20-degrees: EZ1 mode/medianL is: 24 24 24 24 24 24 24 24 24 24 EZ4 mode/medianR is: 25 22 25 25 25 25 25 25 25 25
First, using your original 10-msec delay between samples gives 0 for the first 4 readings, then the other readings are "close". I assume what happens is the sonar doesn't have time to self- calibrate properly, and you have to wait longer before resampling. Also, the spec sheet indicates the total processing time = 50-msec or so. Apparently, after 4 samples it finally gets the self-calibration done. ???? Second, changing to 100-msec sampling delay works much better, but still is not perfect. The EZ1 always reads low, and the 1st EZ4 reading is always < the others. I think this must be a self-calibration issue again - so looks like: ALWAYS have to sample the EZ4 at least twice, and throw away the frst sample. Doh! Thirdly, if I aim the sonars upwards, instead of perfectly horizontally, then the readings get better. Apparently, due to the wide beams, especially of the EZ1, they pick up ground clutter because the robot is small and the sonars are too close to the ground. Pointing the sonars away from the ground fixes the problem, except now the narrow-beam EZ4 is apparently pointing too high to give a correct reading. LOL, it's always something. All in all, a lot of issues involved with using these sonars.
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #24 on: March 15, 2012, 03:17:06 pm » |
Ok, it occurred to me I screwed up last time, forgot that the sort re-orders the samples in the original data array. So now I am displaying data both before and after sorting. Things make a little more sense, as the ranges jump between 0 and /almost/ correct with the 10-msec sampling delay, but don't have this problem for 100-msec delay. Apparently, the sample = 0 is due to not enough time to calibrate. Or whatever. The EZ4 still ranges low the first time, in either case. 10-msec sampling delay: ---------------------- EZ1 22 0 22 0 22 0 22 0 22 EZ1 mode/medianL is: 22 0 0 0 0 22 22 22 22 22
EZ4 20 0 23 0 23 0 23 0 23 EZ4 mode/medianR is: 20 0 0 0 0 20 23 23 23 23
100-msec sampling delay: ----------------------- EZ1 22 22 22 22 22 22 22 22 22 EZ1 mode/medianL is: 22 22 22 22 22 22 22 22 22 22
EZ4 20 23 23 23 23 23 23 23 23 EZ4 mode/medianR is: 23 20 23 23 23 23 23 23 23 23
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #25 on: March 15, 2012, 08:18:42 pm » |
I was under the assumption that the sensor only had to calibrate once. Does it calibrate every time the trigger pin is held high? For my robot, + or - 2" is fine, but it would be nice to get an accurate reading. You don't seem to get the same spikes that I get. Are you using this noise filter pictured in the attachment? That came from here: http://www.maxbotix.com/tutorials.htm#New_Flash_UAV_and_Mobile_Robotic_Users Also I saw where an engineer from Maxbotix suggested even using a 100 OHM resistor on the negative pin as well. It seems that they monitor forums for questions and try to help. http://arduino.cc/forum/index.php/topic,14301.0.htmlThe reply from a Maxbotix engineer is posted on the second page I think. I wonder if the XL line of sensors would be more accurate and stable, but they cost twice as much. And I hate to just throw money at the problem without understanding what is causing it. My spikes in readings occur even when the motors aren't on and with the noise filter in place. My sensors are also close to the ground but still occur when I raise the robot off the ground and use different objects to detect.
|
|
|
|
« Last Edit: March 15, 2012, 08:30:37 pm by TeslaIaint »
|
Logged
|
|
|
|
|
Ontario, Ohio
Offline
Full Member
Karma: 1
Posts: 195
|
 |
« Reply #26 on: March 15, 2012, 10:02:40 pm » |
oric_dan, How do you have the sensors wired? Are they in the daisy chain? Is that why you have TX and RX? void read_sonar(int txpin, int rxpin, int* ary, int asiz) { pinMode(rxpin, INPUT);
Why is rx an input? if(countL>prevCountL&countL>maxCountL){
s/b:
if( countL > prevCountL && countL > maxCountL ) {
or as I prefer:
if( (countL > prevCountL) && (countL > maxCountL) ) { Did you change this in your code that you're using now? Could you post the code that you're using now? Thanks
|
|
|
|
« Last Edit: March 15, 2012, 10:04:21 pm by TeslaIaint »
|
Logged
|
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #27 on: March 15, 2012, 10:11:40 pm » |
Thinking more on this, the alternating 0 with non-0 readings were probably due to having the trigger pulses so close together that the units hadn't finished the previous "full" cycle before the next trigger came along. So increasing delay from 10 to 100-msec fixed that.
As far as noise goes, there isn't much noise in the good sets of readings, the only anomaly is the first EZ4 reading is much lower than the others. I don't know why, but probably should take more than 1 reading and throw the first away every time. ????
I don't know what spikes you are seeing. I don't have the 100-100 filter you show, but do actually have a 100-uF cap on the Vdd buss in the area that feeds the sonars.
Also, as noted last time, I think the short readings are largely due to ground clutter pickup. I have one more test to try.
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #28 on: March 16, 2012, 12:22:18 pm » |
How do you have the sensors wired? Are they in the daisy chain? Is that why you have TX and RX?
Quote void read_sonar(int txpin, int rxpin, int* ary, int asiz) { pinMode(rxpin, INPUT);
Why is rx an input?
No daisy-chain, I am simply using your code, and separate sonars. If you check your original code with my editing, you'll see tx,rx are just your same pins: int SonarR = 5; int TriggerR = 2; int SonarL = 3; int TriggerL = 4; Could you post the code that you're using now? Thanks
See post #20.
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
the land of sun+snow
Offline
Edison Member
Karma: 78
Posts: 2089
|
 |
« Reply #29 on: March 16, 2012, 12:26:51 pm » |
Well, I did some more testing, and now I am stumped. Tried to figure out why the EZ4 was reading low on the first measurement. First, I changed to the order of reading EZ1 and EZ4, and lo and behold, then the EZ1 was the one reading low. See first data set below. So, it's not the sonar device that's the problem, it's the manner of reading. I've tried various things so far, but can't figure it out. First, I typically use 57600 bps, so the Serial.print() displays come out very fast. By playing around, I found I could get the 2nd sonar to read ok by either using 9600 bps [which takes about 50-msec to send the data lines], or inserting a minimum 10-msec delay between sonar readings, see code. I've also tried other things like zeroing the data arrays before reading, and also changing the trigger pulsewidths, etc, but so far none of that works, so I'm stumped as to why adding a little 10-msec delay fixes the problem. Especially given that there is already a 100-msec delay at the end of the measurement loop in the read_sonar() routine, shown earlier. Duh? Scratches his head. 57600 bps, no delay [bad] ------------------- 23 23 23 23 23 23 23 23 23 EZ4 mode/medianR is: 23 20 23 23 23 23 23 23 23 23 EZ1 mode/medianL is: 23
9600 bps, or 57600 bps & delay(10) [good] ---------------------------------- 23 23 23 23 23 23 23 23 23 EZ4 mode/medianR is: 23 23 23 23 23 23 23 23 23 23 EZ1 mode/medianL is: 23
void loop() { Serial.println(" "); distCalcR(); delay(10); distCalcL(); ..... }
|
|
|
|
|
Logged
|
Murphy's Corollary: the "real" problem is usually what they don't tell you about, which leads to endless second-guessing. m
|
|
|
|
|