Pages: 1 [2] 3 4   Go Down
Author Topic: multiple maxbotix LVEZ on one UNO?  (Read 5185 times)
0 Members and 1 Guest are viewing this topic.
Ontario, Ohio
Offline Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
/* 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 Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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.

Code:
    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

Ontario, Ohio
Offline Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 :-).
Code:

/* 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

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

oops, need to change arraysizeL to asiz inside function

void read_sonar(int txpin, int rxpin, int* ary, int asiz)
Logged

Ontario, Ohio
Offline Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
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

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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.

Code:
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

Ontario, Ohio
Offline Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.html
The 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.


* maxbotix.PNG (154.38 KB, 633x239 - viewed 18 times.)
« Last Edit: March 15, 2012, 08:30:37 pm by TeslaIaint » Logged

Ontario, Ohio
Offline Offline
Full Member
***
Karma: 1
Posts: 208
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

oric_dan,
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?

Quote
   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 Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Quote
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;

Quote
Could you post the code that you're using now? Thanks

See post #20.
Logged

the land of sun+snow
Offline Offline
Faraday Member
**
Karma: 158
Posts: 2890
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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.

Code:
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

Pages: 1 [2] 3 4   Go Up
Jump to: