More automonous help:ending stuff

OK! I got a line sensor code (verrrrrry basic) and I would like to now a few things:

  1. I know the robot will arrive at the T in under 15 secs, so how do I stop it and do something different?

  2. If I stop it, we would like to raise our arm (on winch), which is on a PWM.

  3. How would I stop the arm? Same way as # 1?

 
if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
			{
				pwm13 = 127;
				pwm15 = 127;
			}
			else if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 1)
			{
				pwm13 = 139;
				pwm15 = 175;
			}
			else if (rc_dig_in01 == 1 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
			{
				pwm13 = 175;
				pwm15 = 139;
			}
			else if (rc_dig_in01 == 0 && rc_dig_in02 == 1 && rc_dig_in03 == 0)
			{
				pwm13 = 249;
				pwm15 = 249;
			}

Ok…well it’s difficult to tell you exactly what to do without knowing your robot, but I’ll try. There isn’t a timing component in your code, it looks like, it just follows the line as far as it can, turning motors to different values when certain sensors see the line. Without some form of timing you won’t be able to tell it to stop when you want, or time when the arm raises up/stops.

The simplest form of timer you can have is a program cycle counter, which will operate around 40hz, or about 25ms per count (its actually more like 28.something). So, add in a variable “count” (int count = 0; in your initialization) somewhere, and at the end of the loop say “count++;”. Then, check when the count is higher than a certain number (the time you want to stop, or raise the arm, or stop raising the arm), and set the pwm values to something different that you want. Make sure to set those values lower in the loop than the sensor code, or they will get overridden.

You’ll probably just have to time it yourself, or count the number of times a certain sensor sees the line (as in did it just turn?), and then raise the arm.

There are better ways of controlling the robot based on time and other factors, but its probably more complicated than you really have time for right now. Hope I helped.

Thanks, but can you be more specific. Put timer where in ins? Could you give and example, I can change the code but I can’t write it…++ means add twice to me

In your User_Initialization create a variable called count (you can create it anywhere but that’s probably the best spot for now), with this syntax:

int count = 0;

Then at the bottom of the while(autonomous_mode) loop in your code, right above Putdata(&txdata), put in

count++;

++ in C means the same thing as “count = count + 1;”, so you are incrementing it by one every time you pass through that loop. Multiply count by 40 and you have approximately the amount of time autonomous has been running. You’ll have to time it and do the math yourself, but when you want it to break off, say something like (below your line following code):

if(count > 200){
pwm01 = 254;
pwm02 = 254;
}
else if(count > 300){
pwm01 = 0;
pwm02 = 0;
}
else if (count > 320){
pwm01 = 127;
pwm02 = 127;
}

That won’t do much, just go forward and stop after a little over 2 seconds, but hopefully you get the picture on what you can do. Adjust values to your desire, and good luck.

I think you’ll need to declare that int as ‘static’ – otherwise it won’t be remembered from one cycle to the next:

static int count = 0;

Also, if you want to declare it in one place and increment it in another you will need to declare it as global, which means outside of all functions:

// this is global:
static int count = 0;

User_Initialization()
{
  static int anotherCount = 0;  // this is local to this function, 
                                         // and cannot be seen outside of
                                         // this fuction.

  
  count++;  // okay
  anotherCount++;  // okay
  
}

anotherFunctionInTheSameFile()
{
  count++;  // okay -- it's global
  anotherCount++;  // won't compile
}

Barry,

Let me reply to your first question. I created an auto mode with a “sequence of events” that were timed. I created two files, ‘myAuto.c’ and ‘myAuto.h’ (not really – I’m simplifying). (BTW, the code examples below haven’t been compiled and they probably have at least typo bugs.)

In the .h file I made a list of named states using the C ‘enum’ construct:


// myAuto.h

#ifndef MYAUTO_H  // don't include it more than once!
#define MYAUTO_H

enum  // auton state machine states
{
  FIRST_CYCLE,
  RUN_TO_TEE,
  RAISE_ARM,
  STOP
}

C assigns each one of those state names an integer value, probably starting with zero. I show them in time-sequence but that doesn’t matter. By convention, these are all UPPERCASE, but they don’t have to be.

Also in myAuto.h I #define some constants.

// machine cycles at about 38 Hz.
// exact rate is 10 MHz / 2^18

#define RUN_TO_TEE_TIME  380  // about 10 seconds ( 38 cycles / sec)
#define RAISE_ARM_RUN_MOTOR_TIME  80  // ~2+ secs

void
runAndRaise( void );  // advertise the function we will create in myAUto.c
                           // important for calling this function from another file
                           // that file will need "#include myAuto.h" so it
                          // knows the form of "runAndRase"

#endif  // MYAUTO_H

(Naming constants is good practice for organizing your code. It’s bad practice to put “naked constants” in your code, for at least three reasons: first, if one constant needs to appear more than once and you need to change it, you have to change it at each place (and you might forget one). Using #define you can change it in just one place and all uses change at once. Second, if you mis-type a constant, the compiler won’t catch it (all numbers are valid) but if you mis-type a *word * you’ll probably create something that the compiler can’t figure out, and the compiler will catch your mistake for you. And the third reason is the word can say what the number represents (RUN_TO_TEE_TIME), so it is self-documenting.)

Okay, myAuto.c has a static int counter that counts each timed phase. Something like this:

// myAuto.c
//

#include "myAuto.h"  // pick up the enum and #defines

void
runAndRaise( void )  // call this function from 
                   // the file user_routines_fast.c 
                   //   inside  User_Autonomous_Code() ,
                   //     inside the  "if (Clock > Old_Clock)" conditional
                   // don't forget to put #include "myAuto.h" at the top of
                   // user_routines_fast.c 
{

  static int timer = 0;  // times each phase (state) of auton.
  static unsigned char state = FIRST_CYCLE;  // init to first state.

  switch( state )  // executes once every .0262144 seconds
  {
    case FIRST_CYCLE:  // let's initialize some things
      timer = 0;
      state = RUN_TO_TEE;  // next time around, jump to this state
      break; // that's all for this cycle.

    case RUN_TO_TEE:
      timer++;
      runMotorsAndStuff();  // your code goes here
      if ( timer > RUN_TO_TEE_TIME )
      {
        timer = 0;  // reset timer for use in next state
        stopMotors();  // your code goes here
        runArmWinch();  // your code goes here
        state = RAISE_ARM;  // next time around jump to this state
      }
      break;

    case RAISE_ARM:
      timer++;
      if( timer > RAISE_ARM_RUN_MOTOR_TIME )
      {
        timer = 0;  // for consistency: not actually needed by next state.
        stopArmWinch();  // your code goes here
        state = STOP; // next state is stop
      }
      break;

    case STOP:
      // nothing: just sit there until end of auto.
      break;

  }  // switch( state )

  
}

The “case” statements are shown in time sequence but it doesn’t matter. Don’t forget the “break” statement at the end of each case, otherwise the execution will just keep going into the code for the next case statement.

Hope that helps!

-Norm

Wow, I get a head ache reading all that… but I think I get it.

  1. Put that static count in

  2. Add the PWM for the winch on the timer

  3. Calculate the figures

One more thing. If say at 2000 I tell PWM 13 & 15 to shut off and the at 2000 tell PWM 3 (winch) to raise till (lets say) 2750 will the timer interfere with any of the line sensors? Does the position of the timer code affect it?

The static isn’t necesary if it is gloabal. Static on a gloabal variable in C is completely different, as it limits the “global” variable’s scope to only the file it is declared in. However, if you aren’t using it from a different file, you won’t be able to tell the difference.

Oh, yeah…

I haven’t really been following along completely, :wink: but unless the line sensors/PWM part try modify what the timer is at, you should be fine. :slight_smile:

Here is the final code (drum roll please)



	if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
			{
				pwm13 = 127;
				pwm15 = 127;
			}
			else if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 1)
			{
				pwm13 = 139;
				pwm15 = 175;
			}
			else if (rc_dig_in01 == 1 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
			{
				pwm13 = 175;
				pwm15 = 139;
			}
			else if (rc_dig_in01 == 0 && rc_dig_in02 == 1 && rc_dig_in03 == 0)
			{
				pwm13 = 249;
				pwm15 = 249;
			} 
			else if (count > 1000){
				pwm13 = 127;
				pwm15 = 127;
				pwm3 = 175;
			}
			else if (count > 1005){
				pwm3 = 127;
			}                        /* Hey! There are 3435 loops in 15 minutes */


If some one sees a flaw, PLEASE tell me, the Canadian Regional is on Wednesday.

One more thing… I attached a screenshot of where I placed the counter (in the user instalizationz). I this the right spot?

Thank you all who answered my questions…This site has been our only programming resource for our team. :slight_smile:

If some one sees a flaw, PLEASE tell me, the Canadian Regional is on Wednesday.

One more thing… I attached a screenshot of where I placed the counter (in the user instalizationz). I this the right spot?
Don’t think so. Instead, put it in the same function that your code above is in (where is that, BTW?) And make sure it’s static:

static int count = 0;

And don’t forget to increment count somewhere in the function. I can’t tell you how many times I wrote code like this and pulled my hair out trying to find the bug, which turned out to be that I had forgotten to increment the counter:

  count++;

Another thing: What are those count values? 1000? 1005? You comment that

/* Hey! There are 3435 loops in 15 minutes */

but you must mean seconds, not minutes. But there are only about 570 loops in 15 seconds (38 * 15). And how long were you planning to run the arm? 1005 - 1000? That’s only 5 cycles, or about 0.13 second. I doubt you’d even see it move! Especially since autonomous would be over 11 seconds before it is scheduled to start.

Let’s say you want to start the arm after 12 seconds and run it for 2 seconds. Change “1000” to “456” (38 * 12) and “1005” to 532 (38 * (12+2) ).

Does that make sense?

I take it that this is a line-following routine, and the three inputs are three line sensors(?). Consider that your timing test

else if (count > 1000){

will only be tested if all of the preceding tests fail. So if any of the sensors sees something, it will prevent the arm from ever running.

How about wrapping the tests on the sensors inside an “if” statement so that they only run if the time is not yet up to a certain point:

  if ( count < 456 )  // still less than 12 seconds have elapsed
  {
    // all the if's for the sensors go here
  }
  else if ( count < 532 )  // 12 seconds have gone by, but less than 14
  {
    // stop wheels
    pwm13 = 127;
    pwm15 = 127;

    // raise arm
    pwm3 = 175;  // I think it's pwm03, not pwm3.  check it.
  }
  else
  {
    // stop raising arm
    pwm3 = 127;  // I think it's pwm03, not pwm3.  check it.
  }

BTW, your algorithm (for line following?) seems to have a case where it will stop the motors:

	if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
			{
				pwm13 = 127;
				pwm15 = 127;
			}

Does that mean, “if you don’t see the line at all, stop.”? How will it ever find the line again? I’m probably not understanding what the sensors do.

-norm

Thanks for that correction. That’s the great thing about C: there’s always some odd thing to learn. :wink:

Makes me want to cross-post this in the “Why I hate C” thread. :ahh:

There was already a message here which showed how to do some simple state-based autonomous code. THIS IS THE RIGHT WAY TO DO THINGS.

Your autonomous code effectively moves through a series of states. You stay in a state until ready to transition out of this state. Each state have have specific outputs (like a “Moore machine”) or have outputs which directly depend on inputs (like a “Mealey/Mealy machine”). There is no reason to have to make your outputs entirely Mealy. Add a state variable – it really does help.

A simple example can also be found in one of our team’s (1014) code:

http://www.osufirst.org/twiki/bin/view/OSUFIRST/DublinTeam2004RegionalCode

If you look in the user_routines_fast.c in the autonomous section, you’ll see the whole thing is based on state. Because we have position feedback, we transit out of states once we reach a particular position. However, this could just as easily be time.

In general, it’s nice to setup an interrupted-based timer that keeps a moderately real-time clock running on your machine and to use that as feedback timing your operations and calculating important characteristics like speed.

Keep your code in terms of state and you’ll have a much nicer time coding, especially when adding states, maintaining states, and doing simple things like stopping.

Sorry that the 1014 autonomous code has little commenting – one of the nice things about the states (and the speed controllers) is that we could write our autonomous code on the fly at competition and it was easy to debug there. We never intended to have autonomous code… we just felt it was good to add it in since so few robots at our regional had any.

Just looked at my code way up there and realized that i had >'s instead of <'s…sorry for the confusion.

HI,
I don’t have time to write something lengthly, so I will write one thing:

  • I placed count++ whatever in user_routines. Is this the right spot?

Canadian regoinal starts THURS but we leave TOMORROW, but our hotel has internet so post the answer please

Try this. In user_routines_fast.c replace the User_Autonomous_Code function with the function below. It’s not a state machine, but it incorporates elements that you probably understand better.

All: Please check this for bugs!

void User_Autonomous_Code( void )
{
  static unsigned int Old_Clock = 0;

  static unsigned int time = 0;  // cycle counter
  
  while (autonomous_mode)
  {
    if (statusflag.NEW_SPI_DATA)
    {
      Getdata(&rxdata);   // bad things will happen if you move or delete this
      
      // Autonomous code goes here.
      
      if (Clock > Old_Clock) // stay synchronized to beacon data updates (this is important)
      {
        /////////////////////// CUSTOM CODE /////////////////////////
        if ( time < 456 )  // still less than 12 seconds have elapsed
        {

          // LINE FOLLOWING
          // there are 3 bits which means 8 combinations.  
          // only 4 are tested here. ??
          if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
          {
            pwm13 = 127;  // stop
            pwm15 = 127;
          }
          else if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 1)
          {
            pwm13 = 139;  // turn one way
            pwm15 = 175;
          }
          else if (rc_dig_in01 == 1 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
          {
            pwm13 = 175;  // turn the other way
            pwm15 = 139;
          }
          else if (rc_dig_in01 == 0 && rc_dig_in02 == 1 && rc_dig_in03 == 0)
          {
            pwm13 = 249;  // go straight and fast
            pwm15 = 249;
          } 
          
        }
        else if ( time < 532 )  // 12 seconds have gone by, but less than 14
        {
          // stop wheels
          pwm13 = 127;
          pwm15 = 127;
          
          // raise arm
          pwm03 = 175; 
        }
        else  // more than 14 seconds have gone by
        {
          // stop raising arm
          pwm03 = 127; 
        }
        
        time++;  //  count another cycle

        /////////////////////// END OF CUSTOM CODE /////////////////////////
        
        Old_Clock = Clock; // take a snapshot of the clock
        
      } // clock > oldclock
      
      Generate_Pwms( pwm13, pwm14, pwm15, pwm16 );
      
      Putdata(&txdata);   // even more bad things will happen if you mess with this
      
    } //  statusflag.NEW_SPI_DATA
  }  // while (auotnomous) 
}      

OK, here’s my take on what’s been said, with some minor refinements and details. I took the original code, re-arranged a bit to put it into a state machine (and fix the problem where sensor readings could lock out the code that does the end game), then plopped it all into the User_Autonomous_Code function from the default FIRST code in user_routines_fast.c.

I also added in the loop counter in the right spot and used the constants to give the transitions at 12 and 14 seconds. Since autonomous mode just spins in the one function, there’s no reason to mess with global (or static) variables.


enum
{
	stateFollowLine,
	stateRaiseArm,
	stateDone
};

void User_Autonomous_Code(void)
{
	int count; /* number of times through the "slow" loop */
	int state; /* current state */

	/* Initialize all PWMs and Relays when entering Autonomous mode, or else it
	 will be stuck with the last values mapped from the joysticks.  Remember, 
	 even when Disabled it is reading inputs from the Operator Interface. 
	*/
	pwm01 = pwm02 = pwm03 = pwm04 = pwm05 = pwm06 = pwm07 = pwm08 = 127;
	pwm09 = pwm10 = pwm11 = pwm12 = pwm13 = pwm14 = pwm15 = pwm16 = 127;
	relay1_fwd = relay1_rev = relay2_fwd = relay2_rev = 0;
	relay3_fwd = relay3_rev = relay4_fwd = relay4_rev = 0;
	relay5_fwd = relay5_rev = relay6_fwd = relay6_rev = 0;
	relay7_fwd = relay7_rev = relay8_fwd = relay8_rev = 0;
	
	count = 0;
	state = stateFollowLine;
	
	while (autonomous_mode)   /* DO NOT CHANGE! */
	{
		if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
		{
			Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */
			
			/* Add your own autonomous code here. */
			switch (state)
			{
			case stateFollowLine:
				/* we're trying to follow the line */
				if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
				{
					pwm13 = 127;
					pwm15 = 127;
				}
				else if (rc_dig_in01 == 0 && rc_dig_in02 == 0 && rc_dig_in03 == 1)
				{
					pwm13 = 139;
					pwm15 = 175;
				}
				else if (rc_dig_in01 == 1 && rc_dig_in02 == 0 && rc_dig_in03 == 0)
				{
					pwm13 = 175;
					pwm15 = 139;
				}
				else if (rc_dig_in01 == 0 && rc_dig_in02 == 1 && rc_dig_in03 == 0)
				{
					pwm13 = 249;
					pwm15 = 249;
				}
				
				/* time to advance the next state? */
				if (count > 457)
					state = stateRaiseArm;
				break;
			
			case stateRaiseArm:
				/* now stop the robit, raise the arm */
				pwm13 = 127;
				pwm15 = 127;
				pwm3 = 175;

				/* time to advance the next state? */
				if (count > 534)
					state = stateDone;
				break;
			
			case stateDone:
				/* all stop */
				pwm13 = 127;
				pwm15 = 127;
				pwm3 = 127;
				break;
			}
			
			Generate_Pwms(pwm13,pwm14,pwm15,pwm16);
			
			Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
			
			++count; // increment once every 26.3 milliseconds
		}
	}
}

In our code, we raised the arm at the beginning of autonomous, so if we got to the right spot (by dead reckoning, using encoders), the ball would drop. In case we were a little off, at the end we wiggled back and forth a little, enough to knock our ball off if we were close, but hopefully not enough to knock the opposing ball.

To do that, just add a few more states to turn left and right a couple of times.

Enjoy.

I think this code is cleaner than mine – I prefer state machines. Barry, if you can wrap your head around the state machine idea, that’s the better way to go.

And Tom, thanks for pointing out that ‘static’ is unneccesary since it all happens in one loop for 15 seconds. It’s just a habit to use a static when I want to see what the value was at the end of the previous pass.

Good luck, Barry!

Hey Hey Hey!
I have to hand it to ya, the code did good, but it was our robot who screwed up…

I got a big tip that the line sensors power supply does NOT come from the PWM… it comes directly from the breaker. So the sensers don’t have the juice to pick up anything… so useless. I found that out just before we left.

Another thing is the arm which is now removed… is useless to the winch… so no code for that useless device.
off topic but I noticed a lot of teams going for the ‘fishingrod’ style. I think our robot should have done that, less weight overall. So we play defence and offense.
So our now automonous code is drive like hell with our power window motors (I hate them) and plow the goal to the other end…look for my reason on this in strategy.

MY team and I reall yappreciate all the work you guys did. I wish I could have seen it in action, but if we switch back at least we can say were ‘multi automonistic’
Barry

PS: I’m in the hilton business/comp. room. I’ve met one the official/judges (he didn’t tell me) and the DIRECTOR of the whole deal. His wive is photocopying using all the complimentary stuff they have