Ping sensors on arduino uno board running Servo2Timer library
Hello! I am using a ping sensor with an arduino uno board. I bought the kit withping sensor, servo and mount. I am trying to sweep the servo back and forth ~180 deg. and ping between moves. I have tried two approaches with strange output. The sketch is very small so I will post the whole thing.
I have tried time delays in various amounts and places in the code. I have written many variations but these two seem the most promising, if I can get them to work;-) Thanking you in advance for any help you may offer.
// sketch to move servo and ping a distance// two different ways with different results #include <ServoTimer2.h> const int pin6 = 6; //attaches servo to pin 6 const int ping1 = 7; //attaches ping sensor to pin 7 long duration1; // used in ping function long cm1; // distance value int num1; // direction value out of servo1 int dir; // var for switch to sweep servo back & forth in move ServoTimer2 servo1; // declare objects for servos void setup() { Serial.begin(9600); // initialize serial communication: servo1.attach(pin6); // attach pins to the servos dir = 0; // set direction of servos to zero int num1 = 0; // initialize direction of servo movement duration1 = 0; } long ping() //function to drive the ping sensors { pinMode(ping1, OUTPUT); digitalWrite(ping1, LOW); delayMicroseconds(4); //was 2 digitalWrite(ping1, HIGH); delayMicroseconds(10); //was 5 digitalWrite(ping1, LOW); pinMode(ping1, INPUT); duration1 = pulseIn(ping1, HIGH); cm1 = microsecondsToCentimeters(duration1); return cm1; } long microsecondsToCentimeters(long microseconds) { // The speed of sound is 340 m/s or 29 microseconds per centimeter. // The ping travels out and back, so to find the distance of the // object we take half of the distance travelled. return microseconds / 29 / 2; } // end of ping long out() //function to print distances { Serial.print(cm1); Serial.println(" cm1 "); } int move_another(){ // this gets good distance values from i = 45 down to i = 12 then bad distance int count = 0; int i; for ( i = 1; i != 60; i++){ // servo starts in the middle so for the first loop it hangs from i = 30 until i = 60 if ( i <= 30 ){ // then it sweeps back and forth ~180 deg correctly num1 = (servo1.read() - 50); delay(5); servo1.write(num1); delay(400); ping(); out(); count = count + 1; Serial.print(i); Serial.println(" i < 15 "); } else{ num1 = (servo1.read() + 50); delay(5); servo1.write(num1); delay(400); ping(); out(); count = count + 1; Serial.print(i); Serial.println(" i > 15 "); } } } int move(){ // This one gets 29 good distances then 29 bad ones ( always 15 cm) switch (dir) { case 0: ping(); out(); num1 = (servo1.read() + 50); delay(5); servo1.write(num1); delay(250); if (num1 >= 2250){ dir = 1; } break; case 1: ping(); out(); num1 = (servo1.read() - 50); delay(5); servo1.write(num1); delay(250); if (num1 <= 800){ dir = 0; } break; } } void loop() { move(); // move_another(); }
I have tried time delays in various amounts and places in the code. I have written many variations but these two seem the most promising, if I can get them to work;-) Thanking you in advance for any help you may offer.
Comments
Does the Ping work when you're not using the servo?
What does "strange output" mean?
The servos move as expected, back and forth for ~180 deg. The ping sensors work when the servo is off. The strange output is described in my comments.
the function int move_another(){ // this gets good distance values from i = 45 down to i = 12 then bad distance
Now a good distance is the maximum range- 350 cm and the bad distance is 15 cm. Oddly enough, if I hold
my hand in front of the bad reading, it gives me a good reading under 15 cm.
The function int move(){ // This one gets 29 good distances then 29 bad ones ( always 15 cm)
This measures good values, i.e 350 cm, which I can influence with my hand to give accurate values. The bad values start
in the middle of a sweep, i.e. num1 < 1550 go down to 750 and back up to 1650 then get good again.
I wrote another function which has good and bad readings and a few dodgy reading, i.e. 120 cm when it should be 350.
I am trying to keep the sketch as simple as possible in order to understand why ping works sometimes and not others.
I thought perhaps the servo motor gives an interference sound and I tested another servo motor with the same result.
I also tested another ping sensor with the same results.
Thanks for having a look. Any suggestions are welcome!
Regards,
Cappy
I'm just grasping at straws here, but how many bits are in an "int" variable? When things work with some numbers and not others, I wonder if it could be because some vairable has rolled over. I couldn't see anything in your code that would cause this problem though.
There are a few Arduinno users around here and you're very welcome to ask questions here, but you might get better help at an Arduino or AVR forum.
You might also try a much higher baud rate for the Serial connection for debugging in case there's an issue with the Arduino fumbling a Ping reading while sending data to the Serial Monitor window.
-- Gordon
http://learn.parallax.com/KickStart/28015
shows proven Ping code using the Arduino Uno.
Try first without your servo even connected. Then when that part works, add the servo and instantiate its object, but don't move it yet (yes, it does matter). Still working? Then write simple sweep code -- left, center, right, center -- and take Ping readings. Finally, create your sweep.
Power supply glitches can indeed cause problems, so as a matter of course add a 47-470 uF cap across the power supply for the servo, and a smaller one across the Ping's supply just as an added measure.
I've used the Ping with servo sweeps successfully on a number of Uno-based projects without a problem, but I've only used the standard Servo library. You will absolutely need an interrupt-based approach for running your servo, as once you add in the Wave Shield code your Arduino will have precious little time to "manually" update servo pulses. At even 22k sampling there's a lot of data that's getting shuttled first from the SD card, then to the DAC.
-- Gordon
Thanks!
The the value it doesn't matter too greatly, but is for experimenting anyway. The capacitor is used for soaking up ripples and spikes in the power supply. Find the smallest one that works.
-- Gordon
I get a low reading (15 cm) in the middle of the sweep for 9 moves then good ones. I can get the low readings to measure smaller distances so it appears that the ping is working. If I take the servo out of the sketch I get the same thing by turning the servo by hand. Just in the middle of the servo sweep the readings are consistently 15 cm. I have 2 servos and 2 ping sensors so I activated the servo that has the disconnected ping and pinged with the other one on the other disconnected servo. Same thing. Even on the disconnected servo, the center of the sweep is reading low. The other servo can run and not effect the ping readings. I have permutated through delays and when the delays take place but I always have low readings somewhere.
Here is the sketch-
// Sweep
// by BARRAGAN <http://barraganstudio.com>
// This example code is in the public domain.
#include <Servo.h>
const int ping1 = 7;
const int ping2 = 9;
int cm;
Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created
int pos = 0; // variable to store the servo position
void setup() {
// initialize serial communication:
Serial.begin(115200);
myservo.attach(8); // attaches the servo on pin 9 to the servo object
}
int ping(int pingPin){
// establish variables for duration of the ping,
// and the distance result in inches and centimeters:
long duration;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
// The same pin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);
cm = microsecondsToCentimeters(duration);
return cm;
}
long microsecondsToInches(long microseconds)
{
// According to Parallax's datasheet for the PING))), there are
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
// second). This gives the distance travelled by the ping, outbound
// and return, so we divide by 2 to get the distance of the obstacle.
// See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}
void loop()
{
for(pos = 0; pos < 180; pos += 10) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(50);
ping(ping1);
Serial.print(cm);
Serial.println(" cm up");
delay(50);
ping(ping2);
}
for(pos = 180; pos>=1; pos-=10) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(50); // waits 15ms for the servo to reach the position
ping(ping1);
Serial.print(cm);
Serial.println(" cm down");
delay(50);
ping(ping2);
}
}
I am wondering if my pin selection, 7 & 9 for pings and 6 & 8 for servos, has anything to do with this? I am hoping there is a solution for this. Any suggestions are welcome! Thanks, again.
Regards,
Cappy Jack
Also be sure there are no surfaces above, below, or to the sides of the sensor that may be causing early echo reflections. The fact that you can get correct shorter distances suggest this may be an issue.
As I recommended above I think you should test a simpler sweep. Just write code to look left, center, right, center, repeat. Put in delays long enough for the servo to transit there, PLUS time for any vibration to stop settling. You can't reliably take ultrasonic readings while a servo is transiting the sensor, or you could get errors from Doppler effects.
Write your code with some constants
const int servoDelay = 200; // just an example
so that you can easily change the delay amounts. Again, be sure the delay is long enough for the servo transit, plus maybe 25-50ms for any settling. Start with values much higher than you think you need to see if those work, and if they do reduce as needed.
-- Gordon
Regards,
Cappy Jack
Anyway, this is why a systematic approach to development and troubleshooting is so important. Recall that I earlier suggested you temporarily remove the Wave Shield. It's not possible to electrically disconnect a shield from the Arduino unless you physically remove it. The shield interacts even when there's no prgramming to drive it. It will still consume current, contribute to electrical noise, etc. For this reason it's always best to test one thing at a time.
For every project I do I always build from smaller blocks, making sure each sub-system works. I then add components and test along the way. When a problem arises -- and it always seems to with these things -- I then backtrack to determine the root cause.
By its nature, high frequency sound tends to be directional, so it's not surprising that simply by moving off-axis the problem is reduced or eliminated.
On fixing the problem, I'd test with and without the Wave Shield. If it's the Wave Shield, some soldering rework might be called for. Inspect all the solder joints to make sure they're perfecto -- no cold or "peaked" joints.
-- Gordon