Wait time in between autonomous "stages"

Are “wait” times necessary in between autonomous stages?

For example:
If my autonomous routine was written like this…



//declared functions
void moveForward() {
While (Encoder < Desired Encoder)
{ RunDriveTrain }

}

void liftArm() {
While (Pot < Desired Pot)
{ RunArmMotor }

}

void runIntake() {
RunIntakeMotor()

}

Autonomous Routine:
moveForward();
liftArm();
runIntake();

Would I have to add wait times like this?


moveForward();
wait(1000)
liftArm();
wait(1000)
runIntake();
wait(1000)

From my understanding, C is a procedural programming language, so the next line shouldn’t execute until the line before it finishes. Is this right?

As you are using functions and not “tasks”, you are correct. The next function will not be executed until the previous one is finished.

~Jordan

Now how do I go about getting the robot to perform multiple actions simultaneously? This is being done in EasyC v4 by the way.

Do ou mean something like making the robot move forward and raise it’s arm qt the same time? I too would like to know how to do this in easy v4.

Apparently it’s a little more complicated than doing it in ROBOTC, and I haven’t worked with easyC nearly enough to know how … sorry! :o In ROBOTC you’d simply use a “task”, but I’m not sure how in easyC, as I don’t think there is any equivalent… .

Maybe someone else who knows easyC could help out here?

~Jordan

Yes, C is procedural. Yes next lines execute in order.
But in practice, it depends on definition of “finishes”.
eg: SetMotor(1,127); is finished when it launched a memory write to a register.
It takes some time for that value to be sent over serial to another processor, which eventually changes a PWM signal going to motor H bridge, which powers the motor,
which applies a step function to a dynamic inertial load, which response per control system theory.
For Vex, the electrical part of the process probably takes 20-50ms, and the mechanical part of the process may vary.

The usual example of wait() between steps is “timed driving” simple auton:
“drive forward 1 second” means setmotor forward; wait 1s ; stop motors; wait 200ms for motors to stop.

The answer (to me) is simple - Design the code to run in an iterative loop, and process all subsystems in each loop iteration.

I use a hybrid method where all mechanisms are handled asynchronously and navigation is handled with blocking commands. I use RobotC, so my process functions are all poked from another task, but there is no reason (other than simplicity) why I couldn’t call them from my nav functions.

Do this:

-Write a few functions to process your mechanisms (P or PID control for arms, state-machines to run rollers) in such a way that they rely on simple input via globally-defined variables from other systems - Roller has a single power variable, arm has single setpoint variable. These “poke” functions do not contain while loops, but are designed to be called in while loops.

-Navigation commands (drive_straight, turn) have the only while loops in autonomous, and they handle the drivetrain directly. They also call the poke functions of the other subsystems.

In autonomous, you can do something like:

roller_state = 0;//Roller Off
arm_setpoint = 2000;//Let's say this is an analog value representing the height we want to go to
nav_drive_straight(42);//Let's say this drives inches
roller_state = 127;//Spit

nav_drive_straight would be something like this:

void nav_drive_straight(float dist){
//Find counts
dist *= 84;//This is the encoder counts per inch, to turn inches to counts
signed char speed = sign(dist) * 127;//+ or - 127 depending on sign of distnace
while(1)
{
SetMotor(1,speed);SetMotor(2,speed);///Set all of the drive motors
if(Abs(GetQuadEncoder(1,2)) >= Abs(dist)) break;
arm_process();
roller_process();
Wait(20);//Try to get a consistent loop time in all loops so PID can function properly
}
SetMotor(1,0);SetMotor(2,0);//Set all motors to 0
}

And arm_process() could be something like a Proportional controller:

void arm_process() {
//Local variables
int arm_sensor,arm_error,arm_output;

arm_sensor = GetAnalogValueHR(1);//Read arm sensor - HR reads in 12 bit resolution instead of 10
arm_error = arm_setpoint - arm_sensor;//Remember that setpoint is globally defined

arm_output = arm_error * ARM_KP;//ARM_KP is a #defined gain constant

SetMotor(5,arm_output);//Set motor
}

I have even more modular code where I define a state-machine which sets the setpoint, and a typedef enum which defines the desired state, and a PID controller which holds the state, and all I have to do at any time is say

arm_write_state(stateCOLLECT);//Floor collection position
arm_map_dig_to_state(GetJoystickDigital(2,8,1),stateCOLLECT);//This would set stateCOLLECT only when that button is pressed

and the rest of it is handled for me by the processing functions automagically. The arm_map_dig_to_state command is really awesome, as I can define my whole HMI in like 5 lines, just by calling that function 5 times in a row with each of the button values and the state that button maps to.

You can also look for Titan 1103 code from Roundup, maybe in the code forum?
He has 100’s of hours of coding put into EasyC, so there are lots of examples of fairly complex coding.
If I recall, he has one main routine he calls with different parameters, for each autonomous step; to implement things like this:

  • Follow line forward at speed X for max Y distance and max Z time, or until bump, or until cross line, while targetting arm to Q position.

I don’t have time to work up any examples right now, I’m sure this question has come up before so perhaps do a search. The only real choice in EasyC is to call a series of functions in a round robin fashion (as apalrd explained), you can have some very small/quick functions running on what is called a repeating timer but be careful in the use of that. I would generally use a finite state machine within each function to control it’s actions, again search the forums, I’m sure this was discussed a couple of months back.

Edit. Take a look at this thread, it has a repeating timer example.

If you take the while loop out of your arm function, you can add that to all other while loops (ie your drive). After doing that, make a function that sets the desiredpot -
-also make the desiredpot a global variable-

-This is a simplified version of apalrd’s explanation


void SetDesPot(int pot)
{
     desiredpot = pot;
}
void ArmControl()
{
   *This should use a proportional loop* but you can use your original code
      if (Pot < Desired Pot)
     { 
         RunArmMotor(0) //up
     }
}

void DriveEncoder(int DesEncoder)
{
    PresetEncoder(0);
    while(encoder < DesEncoder)
    {
          RunDrive();
          ArmControl();
    }
}

void EndUpdate()  
{
     while(1)
    {
        ArmControl();  - This is used for situations where your driving finishes before your arm
    }
}
void WaitTimer(int milliseconds)  -The EasyC wait will pause everything, so you should make a custom wait so your arm will continue.
{
     ResetTimer;
     while(timer < milliseconds)
     {
        ArmControl(); - your arm will continue to update while you wait
     }
}   

So an autnonomous routine could look like this:


Intake(1); - Intake
SetDesPot(2300); - set arm height
DriveEncoder(1000); - will drive and move arm
WaitTimer(1000);   - you may still use waits to wait for your arm to finish raising
Intake(0); - Outake
SetArm(0) - set arm to ground
DriveEncoder(-1000) - will go back and move arm down - you need to add functionality for going backwards in your drive function.
EndUpdate(); - continue to move arm if drive finishes first