PDA

View Full Version : approach to autonomy


Mike375
01-05-2003, 06:13 PM
I'm fairly familiar with C++ and the basics of programming the robot's basic functions: mapping joystick commands to relays/speed controllers, and how sensors work, but this autonomy thing is a little above my knowledge. The approach I would like to use is to have the robot roughly follow the path of the white line, but just by programming how long each motor should be powered for, rather than losing sleep over the optical sensors. The way I am hypothesizing doing this, since I'm sure I wont be able to test it for a few weeks is as follows:

The first thing I would like to do would be move forward:

1) declare a variable, I'll call it motor_on_fwd (do i need to identify it in the initialization constants section too?)
2) Create an autonomy section in the Perform Operations section (along the lines of the one presented on page 18 of IF's Programming Reference Guide)
3) This is where I get a little confused, if i was doing this in C++, I'd write it as an accumulating loop

while motor_on =! # (# equaling the amount of time i want to move divided by 26 ms)<--which i read is the amt of time for 1 cycle
{
p1_y=255
p2_y=255
motor_on + 1
}


I'm not sure how exactly the while loop works in pbasic or what it is called, so I'm going to need some help there.

If I'm just completely barking up the wrong tree here, please let me know.

Also, is there any way I could try this method out on our robot from last year in the meantime, or did that control system not have this function ?

Thanks in advance for any advice,

Mike

Skabana159
01-05-2003, 06:32 PM
What I have done in the past is quite different than your approach. Two years ago, while writing an auto-balancing routine, and last year for our gear-switching routine, I used an entirely seperate serin/serout loop. This is a sketch of what it might look like, if you were to set the motors to go forward for, say, 40 loops of time. Let us then assume that after 40 loops, it goes back to human control and your regular default program loop, called mainloop.

loopcnt var byte 'a byte to count loops by
loopcnt = 0
autoloop: 'our loop for autonomy
serin 'this takes data from sensors, get the right syntax from
the default program.
PWM1 = 254
PWM2 = 254
loopcnt = loopcnt + 1
if loopcnt = 40 then mainloop
serout 'this sends data to relays and speed controllers. get
syntax from default program
goto autoloop

I would not bother with the middle man of your joystick variables, p2_x and p2_y. Simply go straigt to your motor output variables, PWM#, or whatever meaningful alias you would give to them. The default program does warn against having more than one serin statement. It is okay as long as they are in seperate loops.

Caleb Fulton
01-05-2003, 06:39 PM
So serin/serout loops can be NESTED inside the main loop?

Ryan Meador
01-05-2003, 06:40 PM
For someone who's claiming to not know what they're doing, you're asking all the right questions and definately on the right track. Here's the PBASIC translation of what you're trying to do (although it's the old PBASIC, not this mystery new version that would make life so much easier):

' start with init code and serin statement. this goes in the main loop

' delcare variable; since this isn't tranferred from
' anywhere, you don't need to define a constant
motor_fwd var word
desired_on_time con 200 ' whatever you want...

if motor_fwd > desired_on_time then no_motor
PWM1 = 255
PWM2 = 255 ' this may need to be zero if your motors have reversed polarity

motor_fwd = motor_fwd + 1

no_motor:
' continue with other code here and serout

Ok, enough program. Now for the explanation. You can't have your own loop, because then you wouldn't be sending data out to change the motor outputs. You have to make your code able to be called repeatedly, rather than once. I hope I'm not being too condescending, but this is a good metaphor: imagine you have a function that is called once a frame for every frame in a movie. The function can't just sit there and run, it has to move the actors around on the screen just a little bit and then wait for the next frame.

And yes, you definately can simulate this on last year's controller. Last year's hardware doesn't have support for autonomous mode, but you can accomplish the same thing by just ignoring all user input in the program. My team has done experiments on past years' robots to make them perform a variety of motions. A word of warning: when you first begin testing your code, don't set the outputs to maximum... an out of control robot with the pedal to the metal is dangerous :) This warning courtesy of my formerly-bruised leg.

EDIT: Serves me right for taking such a long time to write a reply... there weren't any when I started writing :P Anyways, nested loops don't matter. The only thing the robot controller ever knows about what goes on inside your program is the input and output. As long as it arrives on schedule, it's happy.

rbayer
01-05-2003, 06:48 PM
First of all, I think using the optical sensors would actually be easier than trying to do what Woody called "Dead Reckoning". If you want some sample optical sensor code, we used them last year to track the goals. That code is available at my website and is called CogCode. http://www.robbayer.com/software.html

Jnadke
01-05-2003, 07:20 PM
Using the optical sensors is rather easy to follow the line on the ground. All you need is 2 optical sensors. 3 would be more fail-safe and would allow you to travel at a faster speed.


Basically, just mount the optical sensors fairly close to eachother so that they can hit the tape when the tape is between them. Testing will be needed to be done to determine the best "gap" between the sensors. Mount them facing the ground so that one is on the right side of the line, and one is on the left.


Then, basically program your robot to move at reduced speed. Program it to do this:

if right_sensor_senses AND left_sensor_doesn't_sense:
turn_left

if right_sensor_doesn't_sense AND left_sensor_senses:
turn_right

else (otherwise)
move_both_wheels_same_speed


Basically, the robot will turn, while still going straight, and follow the line if one sensor can't see anything, until both do. Do do the last part as an "if" statement.


This is the most basic way of programming the robot to follow the line. It allows the robot to still move while seeking out the line.

nwagers
01-05-2003, 07:23 PM
I agree... use the optical sensors. If you've ever used the lego robots you know that using time as a guide it is very inconsistent. Something as simple as battery voltage can throw you off by a few feet. You can easily practice by attaching 2 optical sensors to a board and have 2 LEDS representing left and right turns. If you do decide to use the time as a guide may I suggest using delta_t (missed packets since last "serin" line) as well

mainloop:
serin
time = time + 1 + delta_t

select case time

0 to x
run motor
x to y
next step
and so on...

endselect

serout
goto mainloop

there are supposedly some new programming commands this year, it should simplify the code. Remember to declare time big enough to handle the amount of time you need (a byte is about 6 secs, a word is about 27mins)

Jnadke
01-05-2003, 07:30 PM
Never, ever set your motor's to 255. Always 254 or lower.


This is because two 255's in a row in the serout is a reset command for the main robot controller.

rbayer
01-05-2003, 07:33 PM
One thing to be careful of: your robot starts executing code the moment the power is turned on. This means that your time will have grown to be very large by the time the match acually starts. To avoid this, test for when your robot is no longer disabled. It's one of the bits in the comp_mode byte.

Mike375
01-05-2003, 07:44 PM
another question, since I am unfamiliar with the optical system. I dont remember the lines being made of retro-reflective tape, will the sensors be able to detect the lines just on the contrast with the carpet?

Jnadke
01-05-2003, 08:09 PM
Originally posted by Mike375
another question, since I am unfamiliar with the optical system. I dont remember the lines being made of retro-reflective tape, will the sensors be able to detect the lines just on the contrast with the carpet?


The optical sensors use infrared light to detect where a reflective object is.

Carpet will/should absorb infrared light. The reflective tape, however, will reflect the infrared light back to the optical sensor.

Be careful, where you mount them, because metal is reflective too. The optical sensors might get confused. Also, note that there is a screw on the optical sensor labeled "gain". This tells whether the beam is "wider" (more spread out) or "narrower" (straight ahead). You might want to play with this to determine the optimum configuration.

Ryan Meador
01-06-2003, 07:53 AM
Originally posted by Jnadke
Never, ever set your motor's to 255. Always 254 or lower.


This is because two 255's in a row in the serout is a reset command for the main robot controller.

I've never heard of this before, and in past years I've never had a problem with it. Could you direct me to the documentation that says this? Thanks.

nwagers
01-06-2003, 08:45 AM
This is the link you may be looking for:


http://www.innovationfirst.com/FIRSTRobotics/pdfs/Programming_Reference_Guide.pdf

However, this only says that you should not use 255 as a PWM output. Jnadke is correct two bytes of 255 will signal a new packet to the output processor in the RC. This is also why both relay outputs are spaced out by the PWM outputs (relays can be 255). Your code has worked with a 255 pwm output because there were never two in order.

Mike Soukup
01-06-2003, 10:35 AM
rbayer & nwagers are correct, try to avoid dead reconing whenever possible. Many FLL students figured this our the hard way because robots behave differently as the batteries drain. Motors slow down and you don't go as far in a given time interval as you'd like. So instead use the line, sense boxes using the retro-reflective tape, or use some other sensors. But try to avoid time based (or program loop counting) whenever possible.


I forgot to mention another reason that dead reconing is bad. If a bin or robot is in the way, you won't go as fast as you will w/o objects in the way & therefore not as far in the time interval.


Mike

rbayer
01-06-2003, 10:42 AM
Originally posted by Mike Soukup
rbayer & nwagers are correct, try to avoid dead reconing whenever possible. Many FLL students figured this our the hard way because robots behave differently as the batteries drain. Motors slow down and you don't go as far in a given time interval as you'd like. So instead use the line, sense boxes using the retro-reflective tape, or use some other sensors. But try to avoid time based (or program loop counting) whenever possible.

Mike

Given our $200 of electronics, I suppose it would be possible to add an rpm counter to your wheels. This would make it possible to be sure of exactly how far you had gone/what speed you were going. It will be a pain to setup in PBASIC unless you can find a sensor that keeps track of total rotations and sends that as its "data".

Skabana159
01-06-2003, 10:42 AM
You guys are right, I just put 255 there by force of habit, an arbitrary full forward number. Also, I did not nest my serin/serout loop inside mainloop, it was completely outside of it, perhaps at the end of the program. You have a structure like this:

mainloop:
serin
code
if autoswitch = true then autoloop
serout
goto mainloop

autoloop:
serin
code
if autoswitch = false then mainloop
serout
goto autoloop

This assumes you have some condition to check if autonomous mode is active, arbitrarily called autoswitch. This is not correct pbasic syntax, just sketch code to illustrate my point.

Another way to avoid a "dead reckoning" robot would be two count wheel rotations. However, this can be a little tricky. Two years ago in my team's auto bridge balancing experiments, we tried this idea. What we did was stick magnets every x degrees around our wheel, and put a reed switch on the frame next to it. We found that this did not give us a good enough "resolution" to balance the bridge, but it may be accurate enough to do something as brutish as knock a bunch of boxes onto your side of the field.

seanwitte
01-07-2003, 08:39 AM
Originally posted by Skabana159
You guys are right, I just put 255 there by force of habit, an arbitrary full forward number. Also, I did not nest my serin/serout loop inside mainloop, it was completely outside of it, perhaps at the end of the program. You have a structure like this:

mainloop:
serin
code
if autoswitch = true then autoloop
serout
goto mainloop

autoloop:
serin
code
if autoswitch = false then mainloop
serout
goto autoloop

This assumes you have some condition to check if autonomous mode is active, arbitrarily called autoswitch. This is not correct pbasic syntax, just sketch code to illustrate my point.



If the two SERIN/SEROUT commands are identical, which they should be, you are trading off your personal style for processor resources. Its probably a small amount of downloaded code, but you are repeating commands that could be accessed by branching. You could accomplish the same thing using:

mainloop:

SERIN ...

if autoswitch = 1 then autoloop
'insert operator code here
goto loop_complete

autoloop:
'insert autonomous code here

loop_complete:

SEROUT ...

goto mainloop

<EDITED>
One additional remark. In the code snippet quoted above, a state transition would cause a double SERIN read without a SEROUT. In either loop you would grab the inputs, do some stuff, check the mode, then switch to the other loop without performing the output step. I don't know whether that will cause the chip to reset of not, but if you've done it then it would be fine.

From the programming guide, it looks like the input/output uPs will reset the controller if they have to wait more than 5 cycles (5 x 26.2 ms). So, as long as the longest path is less than that, it should be ok.
</EDITED>

Morgan Jones
01-07-2003, 08:57 AM
Originally posted by seanwitte
If the two SERIN/SEROUT commands are identical, which they should be, you are trading off your personal style for processor resources. Its probably a small amount of downloaded code, but you are repeating commands that could be accessed by branching. You could accomplish the same thing using:

mainloop:

SERIN ...

if autoswitch = 1 then autoloop
'insert operator code here
goto loop_complete

autoloop:
'insert autonomous code here

loop_complete:

SEROUT ...

goto mainloop

On the other hand, you're version is doing several extra operations that Skabana's did not. In any case, I don't think it really makes a big enough difference either way in terms of performance. It comes down to personal style. Personally I think Skabana's version is more tidy.

Jferrante
01-07-2003, 10:32 AM
Why so complex? I think there could be a much easier way to do this.

A few quick notes
1)autonomy mode doesn't need a serin.. that just checks the OI wich will be set to 127 or 0 for pwms and relays respectivly.
2)most of your program doesn't need to be repeated in both sections. Things like pump delays, drive algorithms, and sensor readings can all be done outside of the autonomy loop where they will take effect in either case.
*drive algorithms were included because you can direct control the pwm, rather than adjusting the joystick input variables.
3)The only thing you really need to be able to do in the automated section is read sensors and move wheels for most cases.
So you get a code that is much simpler:

mainloop

manual program

if autoswitch = 0 then noauto
drive (move)
look (sensors)
noauto

overriding stuff (takes precedent in both automatic and manual program because it has the last chance to edit any output)

serout

goto mainloop

rbayer
01-07-2003, 10:35 AM
Originally posted by Jferrante

1)autonomy mode doesn't need a serin.. that just checks the OI wich will be set to 127 or 0 for pwms and relays respectivly.


Are you sure? The docs from InnovationFIRST say specifically that missing 5 Serins in a row will cause the RC to reset. Not a good thing.

Jferrante
01-07-2003, 10:38 AM
Originally posted by rbayer
Are you sure? The docs from InnovationFIRST say specifically that missing 5 Serins in a row will cause the RC to reset. Not a good thing.

i missd that section, thanks for the info...

however, if you look at the code I put up you'll see that it still gets the regular serin at the beginning of the code. The autonomy is nothing more than an if statement with more embedded if's.

Nate Smith
01-07-2003, 11:03 AM
Originally posted by rbayer
Are you sure? The docs from InnovationFIRST say specifically that missing 5 Serins in a row will cause the RC to reset. Not a good thing.

Also, the SERIN is where you get your data from your robot's onboard sensors...so if you're using any sort of sensor input during autonomous mode, you would have to run SERIN...

Greg Ross
01-07-2003, 11:56 AM
Originally posted by Nate Smith
Also, the SERIN is where you get your data from your robot's onboard sensors...so if you're using any sort of sensor input during autonomous mode, you would have to run SERIN...
Not only that, but the serin is where you get the auton_mode variable. Without the serin, you wouldn't know when the autonomous phase was over.

nwagers
01-07-2003, 02:32 PM
I've never played with this, and it's never been needed, but can the Master uP be initialized more than once without reseting the robot controller? This may help save a few precious bytes that are no longer needed after auton_mode is over. Also, does anyone know the purpose of user_display_mode?

1337 /\/\4573|2
01-07-2003, 08:59 PM
This is code i wrote for the edurobot to follow a line with two optical sensors, that are supposed to initially straddle the line. When a sensor detects the line, it will reposition the robot to straddle the line.

*note, there are some previously declared variables in here like drive_r and drive_l. These are the motor values. Also, the time it runs is adjustable through the 'last_iter' variable. If you still don't get it, consider it more or less pseudocode*


autonomous:

foo VAR word
low_val CON 0
hi_val CON 254
correction_count CON 2
total_iters VAR word
last_iter CON 20
keep_truckin:

drive_R = hi_val
drive_L = hi_val




if left_sensor = 0 then lefts_fine

for foo = 0 to correction_count
drive_L = low_val
drive_R = hi_val
next
lefts_fine:



if right_sensor = 0 then rights_fine

for foo = 0 to correction_count
drive_L = hi_val
drive_R = low_val
next

rights_fine:

if total_iters < last_iter then keep_truckin

goto MainLoop

rbayer
01-07-2003, 09:06 PM
Good idea, but it isn't going to work as I think you are planning. The for/next loops really serve no purpose as motor speeds won't change until you do a SEROUT. You can always just copy and paste your serout from the bottom of your code to the necessary places (immediately before every NEXT), but it would be much slicker if you could find a way to do it that doesn't require multiple SEROUTS.

On the other hand, this code will follow a line. It just has some extra overhead.

Caleb Fulton
01-07-2003, 09:11 PM
That looks like it'd work, but are you missing a total_iters=total_iters+1 line in there somewhere?


Anyway, I had a question about autonomous mode, and I hope you guys would be able to answer it...

The default code says this:
"' Bit 6 of the PB_mode byte (aliased as auton_mode below) indicates the status
' of the Autonomous Mode, either Autonomous or Normal. This indicates when
' the robot must run on its own programming. When in Autonomous Mode, all
' OI analog inputs are set to 127 and all OI switch inputs are set to 0 (zero).
' Auton_mode is indicated by a blinking "Disabled" LED on the Operator Interface.
' Auton_mode = 1 for Autonomous, 0 for Normal.
'
' Autonomous Mode can be turned ON by setting the RC to Team 0 (zero)."

Is there any other way to get it to think it's in autonomous mode (i.e. automatically setting all inputs to 0 (or 127) and making the LED blink)?

I realize that a switch input can set the auton_mode variable, but how do you get to the "built-in" autonomous mode?!

1337 /\/\4573|2
01-07-2003, 09:13 PM
Originally posted by rbayer
The for/next loops really serve no purpose as motor speeds won't change until you do a SEROUT.


The for/next loops are there so time will pass while the robot is moving/changing direction. I had this thoroughly commented, but the coments wouldn't fit in this text box with out making the code ugly.


Originally posted by Caleb Fulton
That looks like it'd work, but are you missing a total_iters=total_iters+1 line in there somewhere?

i was in a hurry, and the caffiene was wearing off :).

rbayer
01-07-2003, 09:35 PM
Originally posted by 1337 /\/\4573|2
The for/next loops are there so time will pass while the robot is moving/changing direction.

You're definately going to have to explain that one to me. Unless you put a Serout inside your loops, motor values are never going to change.
Also,
if total_iters < last_iter then keep_truckin
will go through your loop 20 times, right? However, until you do a SERIN, you won't get any new data and your code will always follow the same execution path.

This is just a guess, but you could probably delete everything except


autonomous:

if left_sensor = 0 then lefts_fine
drive_L = low_val
drive_R = hi_val
lefts_fine:


if right_sensor = 0 then rights_fine
drive_L = hi_val
drive_R = low_val
rights_fine:



And it would still work exactly as you expect.

Oops. I meant to leave the goto MainLoop in there. It won't do much otherwise :D

1337 /\/\4573|2
01-08-2003, 12:06 PM
Um... I posted the code for a kind of idea of how to follow a line, i know i missed a lot of details so umm.... yeah. You get the general idea though, right?

Jferrante
01-08-2003, 12:08 PM
if drive_L and drive_R are also the variables used in the manual section AND they are in the single serout at the end (which they would need to be to effect anything) then shouldn't they still affect the serout as in the same manner as long as they come after the user part of the code? I noticed alot of people are saying you need multiple serouts or serins... The single serin already finds out the sensor readings on the robot which will return real values. Your autonomous code MUST use only those values and timing to determine the values for the variables in the serout. Simply use the same variables in the autonomous mode and user mode and a single serout will suffice... However this means that the autonomous code must be the last thing to edit the variables before the serout in order for it to work.

rbayer
01-08-2003, 12:09 PM
The general idea: yes.
The purpose of the for loops, the total_iter loop:no

seanwitte
01-08-2003, 01:04 PM
The code posted to follow the line would probably work ok, but it depends on very careful positioning of the sensors. Since the robot will either go forward full speed or rotate in place full speed, there is the chance it would oscillate side to side if it catches the line wrong. Try it and you'll see what I mean.

I worked with the edubot to try and apply a control loop to the line following problem, with mixed success. There were two wheels at the front of the bot with a caster in back. Each wheel was controlled independently using the same subroutine. There were three sensors, one on the line and one on either side. It kept track of the previous reading and the number of cycles the sensor had been in that position. The speed was then increased or decreased proportionally to the amount of time the sensor was either on or off the line. The formula was something like:

Speed = Current Speed +- ((Cycles in State) * Gain)

Speed is initially set to neutral (127) for both wheels. The number of cycles was some subdivision of the loop counter. I think it was something like 5 loops = 1 Cycle. Gain was tweaked manually until the robot worked well. If the sensor was on the line it would decrease the speed, off the line it would increase it.

The idea was to apply only small changes when the robot is off the line, gradually increasing the amount it tried to compensate. The state it did not count on was overshoot for sharp angles.

It works pretty well for long straight sections, the robot would eventually compensate for a sloppy drivetrain by driving the wheels at different speeds. Gentle curves worked pretty well also, but it could not handle sharp corners.

Following a line while moving slow is easy - doing it fast is the trick.