Welcome to Synthiam!

Program robots using technologies created from industry experts. ARC is our free-to-use robot programming software that makes features like vision recognition, navigation and artificial intelligence easy.

Get Started
Australia
Asked — Edited

Turning By Us (Ultra Sonic)

Hello,

Forgive my newby question, but I am attempting to make my robot a little smarter at turning that just turn for x mseconds.

Below is my adaptation of one of the sample scripts, but what I really want to do is if object A is 30 in front turn to a clear side until object A is> 120, then rescan and proceed forward.

What I cant work out is how to give a turn (left or right) command based on a US signal value increasing in value to a designated "ok to proceed" value. I have the which way to turn done, but not turn for how much.

Any Help would be greatly apreciated.
# Set the servo left position
$PosL = 90

# Set the servo center position
$PosC = 50

# Set the servo right position
$PosR = 30

# Set the mininum distance
$MinDist = 30
$SlowDist = 100
$RDMin = 50

:Loop
# Begin Scanning IO

# Middle Stationary US
$PingMid = GetPing(d2, d3)

# Scan left
Servo(d7, $PosL)
Sleep(500)
$PingL = GetPing(d0, d1)

# Scan Center
Servo(d7, $PosC)
Sleep(500)
$PingC = GetPing(d0, d1)

# Scan Right
Servo(d7, $PosR)
Sleep(500)
$PingR = GetPing(d0, d1)



#Begin Move

if ($PingMid $PingL)
$Ping1 = $PingR
endif
If ($Ping1 < $MinDist)
Reverse(255, 1000)
Elseif ($Ping1 < $PingL)
Right (255,1000)
Else
Left (255,500)
Endif
forward(20)
Return()

:RightBad
left(255, 500)
sleep(1000)
Forward()
Return()

United Kingdom
#1  
Unfortunately my time today is very limited however if you are following my Introduction To Scripting topic the next one will be explaining how to do advanced avoidance much like you have explained. I hope to be able to write that up in the next couple of days:)

Also, take a look at this topic
Australia
#2  
Thanks very much and I am sure the community will appreciate your input, especially newbies like me.
Ty:D
Australia
#3  
Hi Rich,

Thanks for your tutorials, I hadn't seen them I was using the example code in EZ-B software. I wanted to have two different move speeds globally set. ie Slow for tight spaces and Fast for open area's.
I tried setting a global of $MF1 = 20 and another of $MF2 = 255, but when ever I used them I would get an error, I think based on your tutorials it was because I didn't use a time value as well. I used Forward($MF1), I omitted the time base. So I will go back and play with this.
The challenge I want to address though is the time base value, I want to be able to manipulate this value based on ping signal. I don't want the robot to turn for 300msec, I want it to turn until ping>X stop turning move forward.

I have a Irobot Scooba, and it is completely stupid (read bump logic, for robot direction control, and random floor patterns). I am trying to build a brain for it that will have it map the room and progressively mop the floor moving accross the room (know I am goign to have to hack the main board as the scooba has no plugin). My test bot is the EZ-B components and 5 additional US and 1 IR sensors, mounted on a meccano frame. One thing I have noticed and will have to work out how to code is wheel slip. My robot does an ARC accross the room. I have adjusted the servo speeds to reduce the arc, but it is still not perfectly straight.
This is a big project for a newby, hence I am starting off simple with the kit when I have it functioning then I will put it into Scooby, for now he still has floor's to mop.

Thanks
for your help, I will tidy up my code with some more Text explanations and keep my declarations together and stated. Keep up the good work this stuff is great fun.
Ghosty
United Kingdom
#4  
Very quickly, and I'll admit I only read half of your post as I need to get a wriggle on and should be in bed (will probably revisit this in 15 minutes though...)

the Forward, Reverse, Left & Right don't need a time. Forward(255) will just go forward at full speed forever (or until something else stops it).

A very quick script, it may not work since this is done entirely from memory with no ARC in sight...

Code:


# Turn until ping is > x

# Determine X - for these purposes will set at a static number
$x = 120

# Turn left at speed (speed could be removed if not required)
LEFT($speed)
:loop
# avoid saturating communications
Sleep(50)

# Loop until ping sensor reports a value above x
IF(GetPing($trig,$echo) < $x)
GOTO(loop)
ENDIF

# Move forward or continue script
FORWARD()


If my scripting knowledge lives up to it's reputation that should turn left, while turning it should check the ping sensor, if under the defined ping will continue checking and turning until it is over the value, then move forwards.
United Kingdom
#5  
As I said may be the case, revisiting:)

You said your code had an error when using the $MF1 variable? Errors usually give explanations as to why they are errors. What does it say in the dialogue when it stops on error? It should work provided the variable is an integer.

A common error I get when I am not checking things as I type them is numbers are stored not as integers but as decimals for some calculations i.e. earlier I was writing code to find the center point of a sweeping servo by using max postion - min position / 2. it would calculate a decimal number on occasion and throw an error. To solve, it was just a case of using the $variable = Round($variable,0) command

The other common error on this is I make a mistake on the variable name. For instance putting $varialbe rather than $variable. However on $MF1 I can't see how that would be missed.

While I'm at it, a quick change to the above code...

Code:


:turnaction
# Turn until ping is > x

# Determine X - for these purposes will set at a static number
$x = 120

# Turn left at speed (speed could be removed if not required)
LEFT($speed)
:loop

# Loop until ping sensor reports a value above x
IF(GetPing($trig,$echo) < $x)
# avoid saturating communications
Sleep(50)
GOTO(loop)
ENDIF

# Continue script
Return()


Just made it a sub routine so it can be called easier and returned from which is how I prefer to script (makes it easier for future modifications) and moved the sleep to a better position. The $x could be part of a different routine, at least I would make it as part of one, then the main script could call the routine to determine $x based on whatever parameters you want, then call the turning routine.
Australia
#6  
Thanks Rich

Is there a way to increase the scan time or have the EZB performing an action (sub routine) and yet still scan the "loop" section of the program.

I have changed my logic greatly and reduced it to the bare min, but have found I need to go back and add "Estop" lines in through the sub routines as whilst it is performing a sub routine, it appears it is not going through the "Loop" section every X mseconds.

I amused to ladder logic and industrial PLC's that scan the ladder every 20ms regardless of what timers or routines are happening at the same time.

I have put my robot onto a Hbridge based model and this moves significantly faster, so I have changed the PWM setting to 8 to make it slow enough, for the US on the servo to get the three readings and react in time. But what I am finding is if the front bumper, a single Digi in activates, the logic does nothing until it has completed the "goto" it is currently in and then works back through the "loop" section of the logic. Hence I have ended up putting the Estop routine in every goto and the main loop.

Maybe I am missing something and taking the wrong approach?

I also want to look at using a data array that can store the last "change of direction" routine output so if the next routine is the same one we simply make the same outcome again. On occasions my robot will get into a spot and turn left, re-verify its position and turn right back to were it was. I have gotten around this by setting the turn time to two different values so it turns a diffrent amount, still not perfect but it does eventually extract it self. The code you have above for turning until $X should solve the issue I think.

here is my current script, evolved from the first post
# Object Avoid Script

# Global Defined
$PosL = 75 # Set the servo left position
$PosC = 65 # Set the servo center position
$PosR = 50 # Set the servo right position
$ESTP1 = 0 # Digital in to record a bumper var pressed -preset to 0
$ESTP2 = 0 # Digital in to record a bumper var pressed -preset to 0

# Set the mininum distanceces
$MinDist = 30 # Min distance to must do something
$SlowDist = 150 # slow down distance - ie tight space
$RDMin = 50 # Radar US range that action is required
$lastD = "Forward" # want use as an array to hold a value for corner trap

:Loop
# Begin Scanning IO

$ESTP1 = GetDigital(D8) # Bumper Front
$ESTP2 = GetDigital(D9) # Bumper Top

If ($ESTP1 = 1 or $ESTP2 = 1) # Act imediatly if bumper is pressed
$ESTP = 1
Goto(Estop)
endif

# Scan left
Servo(d0, $PosL)
Sleep(500)
$PingL = GetPing(d1, d2)

# Scan Center
Servo(d0, $PosC)
Sleep(500)
$PingC = GetPing(d1, d2)

# Scan Right
Servo(d0, $PosR)
Sleep(500)
$PingR = GetPing(d1, d2)

# Scan Rear
$PingB = GetPing(d12, d13)

#Begin Move
PWM (D15, 8)
Forward (8)

If ($ESTP1 = 1 or $ESTP2 = 1)
$ESTP = 1
Goto(Estop)
endif

# check if we can increase speed
If ($PingC > $RDMin) and ($PingC < $SlowDist)
PWM (D15, 8)
forward(8)
Else
forward(20)
Endif

# Check for obstical left
If ($ESTP = 0) and ($PingL < $RDMin)
goto (Changedirect)
Elseif ($PingL < $SlowDist)
PWM (D15, 8)
Else
Forward(20)
Endif

# Check for obstical Right
If ($ESTP = 0) and ($PingR < $RDMin)
goto (Changedirect)
Elseif ($PingR < $SlowDist)
PWM (D15, 8)
Else
Forward()
Endif

If ($ESTP1 = 0 and $ESTP2 = 0)
$ESTP = 0
Endif

Goto(Loop) # Return to the top and start over

:Changedirect # Find out which direction can be taken ** needs more refining work**
# Slects the more clear space left or right and stores it in Place holder
If ($PingR < $PingL)
$Ping1 = $PingL
Elseif ($PingR > $PingL)
$Ping1 = $PingR
endif
# Checks place holder value against the min dist value and then acts
If ($ESTP = 0) and ($Ping1 < $MinDist) and ($PingB > $MinDist)
Reverse(50,500)
Elseif ($ESTP = 0) and ($Ping1 = $PingL)
Left (10,450)
Elseif ($ESTP = 0)
Right (10,350)
Else
Goto (Estop)
Endif
Return()

:Estop # one of the bumpers is pressed, back away and turn left
Reverse(10,1000)
Left (10,250)
$ESTP1 = GetDigital(D8)
$ESTP2 = GetDigital(D9)
If ($ESTP1 = 0 and $ESTP2 = 0)
$ESTP = 0
Endif
Goto (Changedirect)
Return()
United Kingdom
#7  
That's far too much for me to process when I've not long woken up... I'll address this better later.

But, to run more than one routine simultaneously you can write the sub routine as a new script and use a ControlCommand to start and stop it, the script will start the routine but also continue on it's own code too rather than waiting for the sub routine to stop.

This is also an issue that needed addressing in the ping roaming script I was working on with R2D2 (look in the cloud for my ping roam project). We needed to move the robot, sweep the servo and scan the sensors, then avoid etc. so quite a few little things happening at once. It may pay to look at that code to see how it was done. Although ControlCommand is probably the easiest way, especially now we can pause a script too.

Moving based on previous movement is something that I plan to address in the ping roam script too. The previous movements can be stored as $variables or written to file, I prefer variables. But then that can be used when it checks again, and an if put in place to check the previous move against the new move and if they cancel each other out (i.e. a move right is decided when the last move was a move left) it can check again or run in some boxed in code or whatever. (Boxed in is the part I have been working on lately in this script but currently can't test as Melvin doesn't have a sweeping sensor and testbot is being upgraded to wifi)
United Kingdom
#8  

Quote:

Is there a way to increase the scan time or have the EZB performing an action (sub routine) and yet still scan the "loop" section of the program.

Yes, in the code is a sleep (I believe it's set to 50) just adjust this. I have since updated the code but I don't think I've uploaded it, the delay is a variable in the top portion of the script on the newer code though. It's currently untested and has a few bugs I need to work out hence not updating it.

To have it performing another action at the same time as the scanning loop the best way would be to use

Code:

ControlCommand("Scriptname",ScriptStart)


Quote:

I have put my robot onto a Hbridge based model and this moves significantly faster, so I have changed the PWM setting to 8 to make it slow enough, for the US on the servo to get the three readings and react in time. But what I am finding is if the front bumper, a single Digi in activates, the logic does nothing until it has completed the "goto" it is currently in and then works back through the "loop" section of the logic. Hence I have ended up putting the Estop routine in every goto and the main loop.

This is why I will be having front, left and right sensors rather than a sweep servo on Melvin, he moves too fast to sweep. Sometimes you need to alter the setup for better results.
As for the front bumper not registering due to being in a goto, you could always make the front bumper code a separate script that loops around on it's own constantly, regardless of other scripts and code. You can add in ControlCommands to stop other scripts when the bumper is activated too, and then another to restart it. Or you could use a variable so when the front bumper is activated the other scripts wait for the bumper script to finish;

Code:


:bumpercheck
If($bumperactivated=1)
WaitForChange($bumperactivated)
EndIF

It may need to be called a lot throughout the script though so ControlCommand would be my prefered option.

Quote:

I also want to look at using a data array that can store the last "change of direction" routine output so if the next routine is the same one we simply make the same outcome again. On occasions my robot will get into a spot and turn left, re-verify its position and turn right back to were it was. I have gotten around this by setting the turn time to two different values so it turns a diffrent amount, still not perfect but it does eventually extract it self. The code you have above for turning until $X should solve the issue I think.

My updated PingAvoidance uses the turning until $x > $y routine. I've made it public now however be aware it is incomplete and still has some bugs that need working out. But it should prove useful for you to see how I've approached different issues. Download it here
Australia
#9  
Hi Rich,
I like your escape routing, I was wondering if you can put a goto within a goto until result = different,

Thanks for the info, I was also thinking that the finished product is going to need 3 sensors for my robot, but on the iRobot scooba a sweep would be fine as it travels very slow.

Do you know if there is a PID type control loop function in Ez, in a plc I would use this function to have a setpoint, and then give values to the P and I so the output would adjust continuously until the output matches the setpoint.

In my wall hug I was thinking of using two sensors front and rear, and have a setpoint of say 10 (distance from the wall) because one is at the front and one is at the rear I could then use logic to speed up or slow the outer wheel to maintain the Setpoint (10) away from the wall.

Next I would need a counter, so when the robot gets to the end of the room it would turn 90 deg, move the width of the robot, then turn 90 again. then progress on a new path were the setpoint is now setpoint +1 the +1 being the width of the robot. The counter would store the 90 degree turns and thus tell me how much the + value should be.

Anyway again thanks for all your help.

Ghost
United Kingdom
#10  
A goto in a goto doesn't make sense but I think I know what you mean. Like;

Code:


:loop
If($x < $y)
Goto(process1)
Else
Goto(loop)
EndIf

:process1
If($a > $b)
Goto(process1)
ElseIf($a<$b)
Goto(loop)
Else
Goto(process2)
EndIf

:process2
If($x > $y and $a < $b)
Goto(selfdestruct)
EndIf
Return()

:selfdestruct
# Kaboom!


Obviously that code does nothing but if that's what you mean by having a goto in a goto then yes you can.

PID, so 2 results that need to meet the third? Like;

Code:


If($p = $d and $i = $d)
Goto(success)
Else
Goto(fail)
EndIf

Or have I interpreted it incorrectly?
Australia
#11  
Hi Rich,
I was referring to your code were one of the IF results in going back to the top of the "goto() that it is already in

A snippet from your code.
:detect
IF ($onlyforward = 1)
IF ($Direction = "Forward")
GOTO (detectstart)
ELSE
GOTO (detect) <----------------------------------
ENDIF
ELSE

I didnt know you could do this and it will make my code a lot simpler in some places and help greatly with my turn by ping code.

On the PID - Yep totally missed it. Although your answer is probably the way I am going to have to deal with it.

A PID is block of logic, that you give a set-point to the P is the gain or rate at which the output is changed trying to the set-point. Inevitably it over shoots some as it is always trying to correct. So some smart engineer invented the I part or integral which is repeats of outputs per period X this over comes the over shoots by making minute repeated adjustments until the output meets the input.
This is used in PLC for flow control temp control, pressure control etc of processing plants, most logic controller I have ever had to deal with have this functionality and thus I am used to being able to call a PID block plug in the IO points and the three PID values and have it do its job. .... Wishfull thinking I guess

It could be done with an if statement but it would need to be running all the time cycling as quick as possible.

regards
Ghosty
United Kingdom
#12  
Ah yes, if only I had remembered that code I could have saved typing out the example above.

That part of the code works perfectly. When the variable for only when moving forwards is set to 1 it just loops around the IF statement until it is true. I could have done it a different way, with a WaitForChange on the direction variable but I don't think that command existed when I was writing it, plus I prefer this way personally.

The PID IF statement could be run as a separate script always running. Variables are global so $x in script y is also $x in script z.

So 2 scripts, one with the PID IF statement;

Code:


:loop
If($d = $p and $d = $i)
# Do whatever if it fails
ControlCommand("success",ScriptStart)
$success = 1
Else
# Do whatever if it fails
ControlCommand("failed",ScriptStart)
$success = 0
EndIf
Goto(loop)

That script will run pretty darn fast.

Then in the script that finds P & I have them set the variables, check the $success variable, run a third, fourth, fifth script, whatever needs to happen.

I've not hit a limit on how many scripts can be running at the same time and I run a lot of them at the same time on Melvin. Some interact with each other, others are stand alone.
United Kingdom
#13  
Just a heads up if you plan to reference the script I linked to, I'm having a slight nightmare getting it to work, somewhere there is a bug which is preventing it from looping forever. Something I am trying to figure out, it may be I am trying something that is not supported, I don't know.

However, feel free to attempt to debug it for me (set $testmode to 1 in the options if you don't have a robot that will work and it'll grab random numbers for the ping sensor readings - it's how I test scripts while not at home). I think it's a problem with the returns but not 100% sure and not focused enough to follow it all through at the moment.

Ignore the script manager, that's another plan I'm playing with.

FYI, the script now checks the last move and the penultimate move, if it has moved left then right and wants to move left again, or right then left and wants to move right again it wont, it'll run the escape code so it has a clear path in front of it. But as there is a bug in the code I've not seen how well this works.