Shop OBEX P1 Docs P2 Docs Learn Events
PING))) on servo - strange behaviour — Parallax Forums

PING))) on servo - strange behaviour

xphxph Posts: 8
edited 2015-04-13 11:08 in Robotics
[size=+1]Hello community![/size]

I'm still having fun with my ActivityBot and tried some tutorials with the PING))) sensor, that worked fine. But then i mounted the PING))) on a standard servo and suddenly i'm stuck with some weird behaviour that i can't explain. I hope someone could show me the mistake i made. Hours of trial-and-error didn't help to understand this at all...

Here is what i'm trying to do:
I'm just working with the sensor feedback and the servo motion. No driving around, nothing else. Basically, i want to program a full 180° scan, looking for the closest obstacle and getting its distance and servo angle.

So the Bot should do nothing until an object is detected within a defined range. Then, the servo should do a full 180° move, while constantly scanning for distances. The lowest distance and the matching angle should be saved. This values should be used after the scan, to let the PING))) "look" at the closest object until it's distance is greater than the definend minimum (...or it's removed). I'm using a LED to indicate this.


The code i'm working with:
(I hope the PHP highlighting will do, even if it's C)

[php]
/*
PING))) mounted on servo
*/

#include "simpletools.h"
#include "ping.h"
#include "servo.h"

// pins for ping and servo
int srv = 16;
int png = 17;

// distance to init scan
int obj_distance_scan = 20;

// sensor feedback
int png_distance, png_angle, png_closest_distance, png_closest_angle;

void print_stuff(void);

int main()
{
// signal on power-on
high(1);
pause(1000);
low(1);

while(1)
{
// center servo
servo_angle(srv, 900);

// wait for servo position
while ((servo_get(srv)-500) != 900)
{
pause(2);
}

// get servo angle and ping distance
png_distance = ping_cm(png);
png_angle = servo_get(srv) - 500;

// if object in distance, scan closest distance and angle
if (png_distance < obj_distance_scan)
{
servo_setramp(srv, 100);

// some initial value, greater than the scanned distance
png_closest_distance = obj_distance_scan*2;

// scan angle and steps
for (int a=0; a<=1800; a+=20)
{
servo_angle(srv, a);

// wait for servo position
while ((servo_get(srv)-500) != a)
{
pause(2);
}

png_distance = ping_cm(png);

// get lowest values
if (png_distance < png_closest_distance)
{
png_closest_angle = a;
png_closest_distance = png_distance;
}
}

png_distance = ping_cm(png);
png_angle = servo_get(srv) - 500;
print_stuff();

// aim servo at closest object
servo_angle(srv, png_closest_angle);

// wait for servo position
while ((servo_get(srv) - 500) != png_closest_angle)
{
pause(2);
}

// hold position and alert until object is out of sight
while (png_distance < obj_distance_scan)
{
// blink LED
high(1);
pause(500);
low(1);
pause(500);

png_distance = ping_cm(png);
png_angle = servo_get(srv) - 500;

print_stuff();
}

}
// no object in range
else
{
// do something...
print_stuff();
}
pause(500);
}
}


void print_stuff(void)
{
print("%c%c%d deg\n%d cm\n%d closest deg\n%d closest cm\n\n\n", CLS, HOME, png_angle, png_distance, png_closest_angle, png_closest_distance);
}

[/php]


...and here is my Problem:
The code above works fine - as long as i place an object in the sensor/servo range between 90 and 180 degrees. If i do that, the scan is performed, then the sensor is facing the object until i remove it. All fine.

But if i place an object in the range between 0 and 90 degrees, it just acts weird. Most of the time, the scan is repeated in an endless loop. The motion starts at 0 degrees, scanning and moving up to 180 degrees. After that, for a fraction of a second, the sensor is "looking" at the object (not executing the LED code!), but then jumps back to 0 degrees - repeating all over again. If stuck in such a loop, sometimes i can just move the object slightly (...a few centimeters) which will stop the loop and lead to the expected results - works 1 out of 10 times, max. But that's not all. Sometimes, (maybe 3 out of 10 times), if an object is put at around 45 degrees in the "not-working" zone, the sensor would "look" at 0 degrees afterwards (executing the LED part), and then start all over again.

It actually did happen that an object in the "not-working" zone would be recognized correctly. Didn't happen very often - but i've definitely seen it!


Additional infos:

Please note that it's not a matter of material or size of the object - if i use the very same object in the sensor range between 90 and 180 degrees, it's all fine.

I lost count of the changes i made and the things i tried, but i do know that if i'd change the sensing direction (from 180 to 0 degrees), the "working" and "not-working" areas are switching. When starting at 180 degrees, objects from 90 to 0 degrees will be recognized as intended, but objects in the range between 180 and 90 degrees will just lead to the mentioned endless loop.


  • ...why is there a "working" and a "not working" area?
    I'm just working with the full 180 degree range of the servo. I have no idea why the behaviour splits that area in two segments. And of course i have no clue why i could switch that areas, depending on the servo movement direction.
  • ...why is there an endless loop when something is placed in the "not-working" area?
    If i just place an object to init the scan and remove it right after that, the scan is executed only once. After that, the sensor is "looking" straigt forward, servo at 90 degrees. Conclusion: Scan one time > nothing is found > stop scan, act normal.
    If i place an object in the "working" zone, the code works as it should: Scan one time > found obstacle > stare at it until it's gone > act normal.
    But if i place an object in the "not-working" zone: Scan one time > find obstacle > just have a quick look at it > immediately forget that object > better scan again. or just look at 0 degrees. > whatever, repeat.
  • ...is it possible that something is wrong with my hardware? The PING)))? The servo? Something else?
    I tend to believe that there must be something wrong. Could that be the case? If so - where do i get replacement of my almost new bought stuff? Parallax directly - or the place i bought the bot? How to make sure to get working stuff?


[size=+1]Thank you[/size] for taking your time to read this - and of course thanks for any help.


Edit: ...sorry if this is posted in the wrong section. If "Sensors" or something else would be a better category, please move the thread.

Comments

  • NWCCTVNWCCTV Posts: 3,629
    edited 2015-04-11 17:10
    What are you using for power? I would venture a guess that you may be experiencing an issue with "Brown Outs".
  • xphxph Posts: 8
    edited 2015-04-12 06:57
    Hi NWCCTV,

    usually i power the Bot using 5 almost new AA batterys - but as the bot isn't moving while testing this, i have him connected via USB - that means it's powered from my PC. I also made sure to use an USB port that is providing enough power to connected devices (...at least the mainboard specs said that two of it's USB sockets should be used for power-hungry devices and i use one of them).

    I've never heared the term "Brown Outs" - what's that? I'll need to look that up, i guess.


    Besides from that...

    I'm still trying to program a 180° scan and to find the nearest obstacle - but meanwhile i totally changed my method to do this. Turns out: ...my hardware is working fine! If i'm using another approach to do this scan, it works!

    But still, i have no idea what's going on using the code shown above. As the servo and sensor are working fine, it has to be something in the code. But i don't know what. It would still be nice to understand why the mentioned behaviour occurs, using this code.


    One thing i'm not fully understanding right now, but i became aware of, is: ...timings.

    In "normal" programming, i almost never had to take care of timings. A programmed command would be executed, and then the next one. Simple as that. Working with sensors and electronics now, timings seem to be way more important - as the devices just need some time to get a response.

    I'm still learning about this (mostly by just trying various things), but could it be that the code shown above has some more or less "critical" timing issues, that could lead to the observed behaviour? If so: ...where? What could that be?
  • ercoerco Posts: 20,256
    edited 2015-04-12 07:35
    Per NWCCTV, it's most likely a power issue. Servos can pull over an amp, and your USB supply & cable can't keep up. Try new batteries.
  • xphxph Posts: 8
    edited 2015-04-12 08:45
    erco wrote: »
    Per NWCCTV, it's most likely a power issue. Servos can pull over an amp, and your USB supply & cable can't keep up. Try new batteries.

    I would - if that would make any sense to me. Right now, it doesn't - at all. Let me explain why.

    First, i'm only using "good" PC hardware. High efficient power supplies, mainboards with special built-in power out controls - as i'm using a lot of USB devices, all very hungry. Most of them are switched off when trying this scan, but actually, i could switch on everything and use the mentioned other approach - the scan still works fine.

    Second:
    1. I connect the bot via USB
    2. I use the shown code
    3. Strange things happen
    4. I use other code to perform the scan
    5. All fine, working great!
    6. I use the above code, again
    7. Strange things happen, again
    8. I use the other code
    9. Working fine
    10. (and so on...)
    Note that in both cases, the servo first moves to 0° and then up to 180° in small steps. I fail to see where there's any difference in the servo usage, which could explain why there's an issue in only one of the scripts.

    If you could explain to me why a power problem would only occur in combination with the shown code, i'd thank you and happily replace my batteries. But until then, i''ll just continue to use the other (working) code i've come up with.

    ...and the other reason why i'm not trying this anyways, simply is: right now i don't have replacement at hand ;-)
  • ratronicratronic Posts: 1,451
    edited 2015-04-12 08:54
    By design USB ports on all computers do not supply enough current even high current modes on the USB port is not enough

    to operate a servo on an activity board. I've seen this from experience.
  • ercoerco Posts: 20,256
    edited 2015-04-12 09:20
    xph wrote: »
    I would - if that would make any sense to me. Right now, it doesn't - at all.

    Re: Not wanting to try batteries after requesting help and getting several recommendations to do so.

    By jove, you're right. That is strange behavior!
  • GordonMcCombGordonMcComb Posts: 3,366
    edited 2015-04-12 09:36
    xph wrote: »
    I would - if that would make any sense to me. Right now, it doesn't

    Then consider this: even if you use batteries you'd be running your servo from regulated 5V, rather than Vin. That's obvious because you've connected your Ping to the same header that shares a jumper with P16/17. You're relying on the PAB's 5V linear regulator to hold up to a servo's startup spike, which can be several amps, depending on the servo. Even a few milliseconds of excessive draw can cause a brownout. It makes no difference what your power SOURCE is. Nearly all USB 2.0 ports are rated to 500 mA only. Servos can draw more than that for brief periods, causing the PAB to reset. I've seen this many, many times,

    From the PAB documentation: "CAUTION: When using servo ports set to 5V, be sure the total current load does not exceed the 5 V regulator’s maximum rated current. See 4) 3.3 V & 5 V Regulators on page 3 for details. " Then read the caveats about power consumption under USB.

    Rework your connection to use Vin for the servo, powering from 6V batteries, and not USB.
  • ercoerco Posts: 20,256
    edited 2015-04-12 11:32
    IIRC the PING also serves up a current spike to send the outbound pulse which further compounds the power problem.
  • xphxph Posts: 8
    edited 2015-04-12 12:24
    @ erco:
    ...as it seems that you didn't read what i wrote, let me repeat this:
    ...and the other reason why i'm not trying this anyways, simply is: right now i don't have replacement at hand ;-)

    It's sunday. Where i live, that means i can't go and buy new batterys (if i'd like to avoid paying the double price, or even more).

    @ GordonMcComb:

    Thank you! That are some informations to consider.

    But i still don't understand one thing: ...why is the 180° scan working perfectly, if executed in a slightly another way? Same power connection, all fine.

    To me, it seems like "a power problem" should occur independent from the exact code, if the same hardware parts are used in the same way. I'm still rotating the servo and still using PING))), but now it's working. Shouldn't there be the same, or at least similar, problems when moving the servo? Fact is i'm not experiencing any problems at all, now.

    That's the part i don't get.

    But thanks for your explanations!

    Edit:

    First, about the power-thing: I still don't have new batteries, so i couldn't check that. Allright, will do, okay, got that. Reading ratronics answer (again), i realized one thing: I didn't unplug the power supply from the batteries, even when connected to the PC. So i guess the bot gets its power from the USB as well as from the batteries. And again, the battery pack is as good as new, didn't do much until now. I just wanted to add this.

    Second, about the wiring GordonMcComb mentioned: I think i understand the issue - one PAB regulator, two devices (servo and ping), plus the mentioned servo startup spike - this could be a little too much for the connection to P16/P17. This seems to be plausible, except one thing: ...when running another code doing the scan, it works! Is this just "luck"..? I mean, of course i can rewire the servo, but "just do it, anyways" is a very small reason for that. Again, i can understand the issue you're adressing, but then again, if i just run other code, it's fine and i'm not experiencing any problems at all.

    Btw, i don't know if it's important, but here is the scan-code i'm working with right now:

    [PHP]
    /*
    PING))) mounted on servo

    */

    #include "simpletools.h"
    #include "servo.h"
    #include "ping.h"

    int main()
    {
    // signal pins servo/ping
    int srv = 16;
    int png = 17;

    // print debug output
    int debug = 1;

    // more presets
    int scan_dist = 20;
    int p = 5;

    int a_start = 0;
    int a_stop = 1800;
    int a_step = 20;

    int c = 0, c_dist = 0, range = 0, results = 0;
    int tmp_obj_d = 0, tmp_obj_r = 0, tmp_obj_c = 0;

    int sections = (a_stop - a_start)/a_step+1;

    int scan_a[sections], scan_d[sections], scanned[sections][5];

    memset(scan_a,0,sizeof(scan_a));
    memset(scan_d,0,sizeof(scan_d));
    memset(scanned,0,sizeof(scanned));

    // build array of angles to scan
    for (int a = 0; a < sections; a++)
    {
    scan_a[a] = a*a_step;
    }

    // scan angles and get distances
    for (int a = 0; a < sections; a++)
    {
    servo_angle(srv, scan_a[a]);
    while ((servo_get(16)-500) != scan_a[a])
    {
    pause(p);
    }
    scan_d[a] = ping_cm(png);
    if (debug == 1)
    {
    print("%d\t%d\n", scan_a[a], scan_d[a]);
    }
    }

    /* check scan results to identify objects

    - an object is considered one or more recognized
    obstacles within the given distance
    - a consecutive sequence of same distances is
    considered to be the same object
    */
    for (int a = 0; a < sections; a++)
    {
    if ((scan_d[a] > scan_dist) || (scan_d[a] == c_dist))
    {
    continue;
    }

    c_dist = scan_d[a];

    int n = 1;
    while ((a+n) < sections)
    {
    if ((scan_d[a+n] > scan_dist) || (scan_d[a+n] != scan_d[a]))
    {
    break;
    }
    range += a_step;
    n++;
    }

    scanned[c][0] = scan_a[a]; // starting angle of object
    scanned[c][1] = scan_a[a]+range; // ending angle of object
    scanned[c][2] = range; // object angle range
    scanned[c][3] = scan_a[a]+range/2; // center angle of object
    scanned[c][4] = scan_d[a]; // object distance

    range = 0;
    c++;
    }

    // keep only found objects (get a shorter array)
    for (int a = 0; a < sections; a++)
    {
    if (scanned[a][4] != 0)
    {
    results++;
    }
    }
    int obj[results][5];
    memset(obj,0,sizeof(obj));
    for (int a = 0; a < results; a++)
    {
    obj[a][0] = scanned[a][0];
    obj[a][1] = scanned[a][1];
    obj[a][2] = scanned[a][2];
    obj[a][3] = scanned[a][3];
    obj[a][4] = scanned[a][4];
    }
    if ((debug == 1) && (obj[0][4] > 0))
    {
    print("\n\nstart\tstop\trange\tcenter\tdist\n\n");
    for (int a = 0; a < results; a++)
    {
    print("%d\t%d\t%d\t%d\t%d\n", obj[a][0], obj[a][1], obj[a][2], obj[a][3], obj[a][4]);
    }
    }



    /* --- locate nearest object (nearest and biggest if multiple) --- */
    if (obj[0][4] > 0)
    {
    // some big distance value
    tmp_obj_d = scan_dist*2;
    for (int a = 0; a < results; a++)
    {
    // object is closer than distance value
    if (tmp_obj_d > obj[a][4])
    {
    tmp_obj_d = obj[a][4]; // get distance
    tmp_obj_r = obj[a][2]; // get range
    tmp_obj_c = obj[a][3]; // get center
    }
    // object with same distance
    else if (tmp_obj_d == obj[a][4])
    {
    // check for bigger range
    if (tmp_obj_r < obj[a][2])
    {
    tmp_obj_d = obj[a][4];
    tmp_obj_r = obj[a][2];
    tmp_obj_c = obj[a][3];
    }
    }
    }
    if (debug == 1)
    {
    print("\n\n(biggest) nearest: %d cm, angle %d, range: %d", tmp_obj_d, tmp_obj_c, tmp_obj_r);
    }
    servo_angle(srv, tmp_obj_c);
    high(1); pause(2000); low(1);
    }



    /* --- locate biggest object (biggest and nearest if multiple) --- */
    if (obj[0][4] > 0)
    {
    // some small range value
    tmp_obj_r = 0;
    for (int a = 0; a < results; a++)
    {
    // object range is bigger than range value
    if (tmp_obj_r < obj[a][2])
    {
    tmp_obj_d = obj[a][4]; // get distance
    tmp_obj_r = obj[a][2]; // get range
    tmp_obj_c = obj[a][3]; // get center
    }
    // object with same range
    else if (tmp_obj_r == obj[a][2])
    {
    // check distance for nearest
    if (tmp_obj_d > obj[a][4])
    {
    tmp_obj_d = obj[a][4];
    tmp_obj_r = obj[a][2];
    tmp_obj_c = obj[a][3];
    }
    }
    }
    if (debug == 1)
    {
    print("\n\n(nearest) biggest: %d cm, angle %d, range: %d", tmp_obj_d, tmp_obj_c, tmp_obj_r);
    }
    servo_angle(srv, tmp_obj_c);
    high(1); pause(2000); low(1);
    }

    // move to center position
    servo_angle(srv, 900);
    }
    [/PHP]


    ...and that's doing the job. Every single time. With the same power and connections as the code shown in the opening post - but this first code still behaves strange.


    (Oh, and: Programming in C is still unfamiliar to me, so please don't blame me for bad coding - unless you're showing me how to do better.)
  • GordonMcCombGordonMcComb Posts: 3,366
    edited 2015-04-12 16:04
    xph wrote: »
    To me, it seems like "a power problem" should occur independent from the exact code, if the same hardware parts are used in the same way. I'm still rotating the servo and still using PING))), but now it's working. Shouldn't there be the same, or at least similar, problems when moving the servo? Fact is i'm not experiencing any problems at all, now.

    It can be quite random. Also remember that the further a servo has to travel, the faster it goes to get there, and the more current it will consume.

    Power may or may not be the issue, but you troubleshoot these things in order. What's the first thing you do if your car cranks but won't start? Personally, I'd check to see if it has gas. You could always tear the engine apart looking for some arcane problem, but that's going the hard way about it.

    You can always check if your code is restarting by adding a once-only portion at the start of the code (before the while loop) that does something specific, like flash the P26 LED three times, or sounds a couple of buzzes on a piezo speaker you have connected. If this keeps happening, your board is getting reset because of a brownout. You sorta have that in your first example, but I'd personally make it much more obvious and lengthy. You want to be sure you can notice it. Use a separate LED for your start-of-program indicator than anything else in your code.

    I have to admit I didn't look at your code closely. You could super-simplify this to verify proper wiring and power, then work up the code once you know the platform supports your intentions.
  • GenetixGenetix Posts: 1,749
    edited 2015-04-13 01:45
    Your 2 commands are completely different so how do you expect the same results.

    Your first program has Servo_Get all over the place while the 2nd only has 1.

    Your 1st program also increments the angle by 20 compared to 1 for the 2nd.
    // scan angle and steps
    for (int a=0; a<=1800; a+=20)

    Also, only the 1st program has this command
    servo_setramp(srv, 100);

    Here is the reference to those functions.
    https://propsideworkspace.googlecode.com/hg/Learn/Simple%20Libraries/Motor/libservo/Documentation%20servo%20Library.html
  • xphxph Posts: 8
    edited 2015-04-13 11:08
    [size=+2]Update - fresh tests[/size]

    [size=+1]At first,[/size]

    i bought new batteries! I used the code from the opening post - without success. Didn't change anything.

    [size=+1]Then,[/size]

    i followed GordonMcCombs advice and re-wired my connections, using Vin for the servo and breadboard for the PING))) now. Turns out that didn't change anything.

    I think we can put that "It's the power, why don't you do that"-thing aside - or is there still anything else to consider?

    [size=+1]The next thing i did[/size]

    was reading the post from Genetix - finally someone was looking at the code.

    A short reply:

    1: The for-loop doing the actual scan is using only one call of servo_get() in both scripts. The other occurrences in the first code are before or after doing the scan. I wouldn't have guessed so, but could that still influence the scan itself? If so - could someone please elaborate on this?

    2: Both scripts move the servo in steps of 20. Yes, the for-loop in the second code is incremented by 1 - but that's not the value i use for servo motion. Sorry if some short and similar variable names might cause some trouble here - i'm just not good at naming things.

    3: Originally, the first code was longer and did other things - including the usage of lower servo ramp speeds. I put the call of servo_setramp() there to perform the scan faster than this other things. Basically, i wanted to reset other, lower ramp settings (BTW, how could/should that be done properly?). For testing purposes, i just removed that line - and it didn't change anything.

    But thanks for the mentioned differences! I guess i'll have to spend more time on trial and error using variations of the first code to understand what causes this strange behaviour.



    Of course, thanks for all help, hints and ideas where to look for the problem!

    And as i'm still getting this weird behaviour using the first code, i'd welcome more ideas of what could have gone wrong. Thanks!
Sign In or Register to comment.