Timer.Delay stops motor movement?

I’m currently trying to program autonomous and it’s working, I’ve isolated this issue to be with the Timer.Delay(seconds) command. When hovering over the command, it states that it allows: “Motors [to] continue to run at their last assigned values,” but it simply stops them for the time allotted. Are there any alternative timing methods that allow motors to continue to run and change the command after an allotted time?

code:


case leftAuto:
			System.out.print("YOU'RE LEFT");
			Timer.delay(2.5);
			arcadeDrive.drive(1, 0);
			Timer.delay(5);
			arcadeDrive.drive(0.75, .6);
			Timer.delay(3);
			solenoid2.set(DoubleSolenoid.Value.kForward);
			Timer.delay(2);
			solenoid2.set(DoubleSolenoid.Value.kReverse);
			arcadeDrive.drive(-1, .8);
			Timer.delay(4.8);
			arcadeDrive.drive(-.25, 0);
			Timer.delay(2);
			break;

Is “arcadeDrive” an instance of a RobotDrive? If so, did you disable the motor safety feature? If not, the speed controllers will think that they are no longer being controlled when your Timer.delay() pauses. This will cause the watchdog to time out and disable all of the drive motors.

Here is an example of turning off the motor safety feature while driving autonomously:



case leftAuto:
                        // Disable watchdog when autonomous driving
                        arcadeDrive.setSafetyEnabled(false);

			System.out.print("YOU'RE LEFT");
			Timer.delay(2.5);
			arcadeDrive.drive(1, 0);
			Timer.delay(5);
			arcadeDrive.drive(0.75, .6);
			Timer.delay(3);
			solenoid2.set(DoubleSolenoid.Value.kForward);
			Timer.delay(2);
			solenoid2.set(DoubleSolenoid.Value.kReverse);
			arcadeDrive.drive(-1, .8);
			Timer.delay(4.8);
			arcadeDrive.drive(-.25, 0);
			Timer.delay(2);

                        // WARNING! It looks like you leave the motors running! Is that what
                        // you want? Or, would it be better to stop at this point?
                        //arcadeDrive.drive(0, 0);

                        // Enable watchdog on driving again
                        arcadeDrive.setSafetyEnabled(true);
			break;

I’m not sure exactly what you’re trying to do, but this many Timer.delay() calls in a single routine is definitely not the code you’re looking for. Which robot model are you using (SampleRobot, IterativeRobot, CommandBasedRobot, etc)?

This seemed to fix my problem. Thanks.

I’m trying to make some basic autonomous codes to modify later. We’re using an Iterative model, and if there’s any way you could think to make the code a little bit cleaner/more efficient, or even just some tips on transferring from RobotC to Java, that would be amazing :smiley:

We used this auto format in 2014 and it worked well. This format works just fine for simple autos.

If you are going to use IterativeRobot, you do not want to have calls to Timer.delay(). In the iterative model, your autonomousPeriodic() method will get called for you every 20ms (roughly). You want to write code that quickly does something and then returns, only to be executed 20ms later. You could use timing and the iterative model like so (though this is not how we code our autonomous):

import edu.wpi.first.wpilibj.Timer;

public class Robot extends IterativeRobot {

  private double autoStartTime;

  public void autonomousInit() {
    autoStartTime = Timer.getFPGATimestamp();

    //other stuff as needed
  }

  public void autonomousPeriodic() {

    double currTime = Timer.getFPGATimestamp();
    double timeElapsed = currTime - autoStartTime;

    if( timeElapsed < 2 ) {
      //put code here...whatever you want to do for the first 2 seconds
      arcadeDrive.drive(1, 0);
    }
    else if( timeElapsed < 5) {
      //put code here that you want to do for the next 3 seconds
      arcadeDrive.drive(0.75, 0.6);
    }
    
    //etc.    

  }

  //the rest of your Robot class

}

Hope that makes sense and/or helps. That could all be in a case statement as well:

public void autonomousPeriodic() {
  double currTime = Timer.getFPGATimestamp();
  double timeElapsed = currTime - autoStartTime;

  switch( AUTO_TYPE ) {
  
  case LEFT:
    if( timeElapsed < 2 ) {
      //put code here...whatever you want to do for the first 2 seconds
      arcadeDrive.drive(1, 0);
    }
    else if( timeElapsed < 5) {
      //put code here that you want to do for the next 3 seconds
      arcadeDrive.drive(0.75, 0.6);
    }
    
    //etc.

    break;

  case RIGHT: 
    //etc.    

}

I’ve tried using your code with the if and else if statements, as well as the time elapsed variable, and i believe it works. however, my robot just beyblades in place when enabled. do you have any idea why this might happen?

That sounds like you have something very wrong that is entirely unrelated to the issues being discussed in this thread.

Fair, but I can’t imagine it’s anything mechanical. Our teleop drive works, as does everything else.

It would really help if you posted your code…the whole class, not just a snippet.

Here it is:

package org.usfirst.frc.team2221.robot;

import edu.wpi.first.wpilibj.Compressor;
import edu.wpi.first.wpilibj.DoubleSolenoid;
import edu.wpi.first.wpilibj.GenericHID.Hand;
import edu.wpi.first.wpilibj.GenericHID.RumbleType;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.RobotDrive;
import edu.wpi.first.wpilibj.Talon;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.RobotDrive.MotorType;


public class Robot extends IterativeRobot {
	/*--------------------------------------------------------------------*/
	/*----------------------Setup Talons---------------------*/
	Talon frontLeft = new Talon(0);
	Talon rearLeft = new Talon(1);
	Talon frontRight = new Talon(2);
	Talon rearRight = new Talon(3);
	
	Talon Spare = new Talon(4);
	/*--------------------------------------------------------------------*/
	/*---------------------CONTROLLERS-----------------------------*/
	XboxController xbox1 = new XboxController(0); // Driver Xbox (xboxController)
	XboxController xbox2 = new XboxController(1); // Manipulator Xbox (xboxController)
	/*--------------------------------------------------------------------*/
	/*----------------------Controller Settings----------------------------------*/
	double xbox1_value_x; // Xbox1 x-values
	double xbox1_value_y; // Xbox1 y-values 
	
	double xbox2_value_triggerRight = xbox2.getTriggerAxis(Hand.kRight);
	double xbox2_value_triggerLeft = xbox2.getTriggerAxis(Hand.kLeft);	
	
	/*--------------------------------------------------------------------*/
	/*---------------------Setup Arcade Drive-----------------------------*/
	RobotDrive arcadeDrive = new RobotDrive(frontLeft, rearLeft, frontRight, rearRight);
	/*--------------------------------------------------------------------*/
	/*----------------------Autonomous Chooser---------------------------*/
	SendableChooser<String> chooser = new SendableChooser<>();
	final String leftAuto = "Drive Left";
    final String straightAuto = "Drive Straight";
    final String rightAuto = "Drive Right";
    final String spyAuto = "Spybot";
    
	/*--------------------------------------------------------------------*/
	/*-----------------------Setup Compressor----------------------*/
	Compressor comp = new Compressor(0); // the Zero is the PCM NODE ID in the Web Environment
	/*--------------------------------------------------------------------*/
	/*---------------------Setup Solenoids (And Triggers)---------------*/
	DoubleSolenoid solenoid1 = new DoubleSolenoid(0, 1);
	DoubleSolenoid solenoid2 = new DoubleSolenoid(2, 3);
	DoubleSolenoid solenoid3 = new DoubleSolenoid(4, 5);
	boolean solenoid1_trigger;
	boolean solenoid2_trigger;
	boolean solenoid3_trigger;
	
	
	
	final String auto1 = "autoOne";
	String autoSelected;
	private double autoStartTime;
	public Robot() {
		arcadeDrive.setExpiration(0.1);
	}

	@Override
	public void robotInit() {
		comp.setClosedLoopControl(true);
		
		arcadeDrive.setInvertedMotor(MotorType.kFrontLeft, false);
		arcadeDrive.setInvertedMotor(MotorType.kFrontRight, false);
		arcadeDrive.setInvertedMotor(MotorType.kRearLeft, false);
		arcadeDrive.setInvertedMotor(MotorType.kRearRight, true);
		
	
		  
	}

	
	
	@Override
	public void autonomousInit(){
		autoStartTime = Timer.getFPGATimestamp();
		autoSelected = (String) chooser.getSelected();
    	System.out.println("Auto selected: " + autoSelected);
    	Timer timer = new Timer();
    	timer.start();
	}
	
	@Override
	public void autonomousPeriodic() {
		
		double currTime = Timer.getFPGATimestamp();
		double timeElapsed = currTime - autoStartTime;
		System.out.println("Auto selected: " + autoSelected);

		switch (autoSelected) {
		
		case leftAuto:
			
            // Disable watchdog when autonomous driving
            arcadeDrive.setSafetyEnabled(false);
            	
            System.out.print("You're left");
            if ( timeElapsed < 2)
            {
            arcadeDrive.drive(1, 0);
            }
            
            else if (timeElapsed < 4)
            {
            arcadeDrive.drive(0.75, .6);
            }
            
            else if (timeElapsed < 6)
            {
            arcadeDrive.drive(0, 0);
            solenoid2.set(DoubleSolenoid.Value.kForward);
            }
            
            else if (timeElapsed < 8.5)
            {
            solenoid2.set(DoubleSolenoid.Value.kReverse);
            arcadeDrive.drive(-1, .8);
		    } 
            
            else if (timeElapsed < 9.5)
            {
            arcadeDrive.drive(-.25, 0);
            }
         
            
            else if (timeElapsed > 11)
            {
            arcadeDrive.drive(0, 0);
            }
         
            
            arcadeDrive.setSafetyEnabled(true);
            break;
            
		case "straightAuto":
			
			 arcadeDrive.setSafetyEnabled(false);
			 System.out.print("you're straight");
			
			if(timeElapsed < 2)
			{
			arcadeDrive.drive(.5, 1);
			}
			
			else if(timeElapsed < 3)
			{
			arcadeDrive.drive(0, 0);
			}
			
			else if (timeElapsed < 3.5)
			{
			solenoid2.set(DoubleSolenoid.Value.kForward);
			}
			
			else if (timeElapsed < 4.5)
			{
			solenoid2.set(DoubleSolenoid.Value.kReverse);
			}
			
			else if (timeElapsed < 5)
			{
			arcadeDrive.drive(-.75, 0);
			}
			
			else if (timeElapsed > 8)
			{
			arcadeDrive.drive(0, 0);
			}
			
			else if (timeElapsed > 10)
			{
			arcadeDrive.setSafetyEnabled(true);
			}
			
			break;
			
		case "rightAuto":
			System.out.print("you're right");
			
			arcadeDrive.setSafetyEnabled(false);
			
			if(2.5 < timeElapsed && timeElapsed < 5)
			{
			arcadeDrive.drive(1, 0);
			}
			else if (timeElapsed < 7)
			{
			arcadeDrive.drive(0.75, -.6);
			solenoid2.set(DoubleSolenoid.Value.kForward);
			}
			else if (timeElapsed < 9){
			solenoid2.set(DoubleSolenoid.Value.kReverse);
			arcadeDrive.drive(-1, -.8);
			}
			else if (timeElapsed < 11 )
			{
			arcadeDrive.drive(-.25, 0);
			}
			else if (timeElapsed > 12)
			{
			arcadeDrive.drive(0, 0);
			arcadeDrive.setSafetyEnabled(true);
			}
			
			break;
			
			default:
				
	            arcadeDrive.setSafetyEnabled(false);
	            	
	            System.out.print("YOU'RE LEFT");
	            if ( timeElapsed < 2)
	            {
	            arcadeDrive.drive(1, 0);
	            }
	            
	            else if (timeElapsed < 4)
	            {
	            arcadeDrive.drive(0.75, .6);
	            }
	            
	            else if (timeElapsed < 6)
	            {
	            arcadeDrive.drive(0, 0);
	            solenoid2.set(DoubleSolenoid.Value.kForward);
	            }
	            
	            else if (timeElapsed < 8.5)
	            {
	            solenoid2.set(DoubleSolenoid.Value.kReverse);
	            arcadeDrive.drive(-1, .8);
			    } 
	            
	            else if (timeElapsed < 9.5)
	            {
	            arcadeDrive.drive(-.25, 0);
	            }
	         
	            
	            else if (timeElapsed > 11)
	            {
	            arcadeDrive.drive(0, 0);
	            }
	         // Enable watchdog on driving again
	            
	            arcadeDrive.setSafetyEnabled(true);
	            break;
				
		}
	}
	@Override
public void teleopInit(){
	}

	@Override
	public void teleopPeriodic() {
		arcadeDrive.setSafetyEnabled(true);
//-----------------------Driver Controller---------------------------------//
			if(xbox1.getAButton()){
				xbox1_value_x = xbox1.getX(Hand.kLeft) * 0.75;
				xbox1_value_y = xbox1.getY(Hand.kLeft) * 0.75;
			}
			else{
				xbox1_value_x = xbox1.getX(Hand.kLeft);
				xbox1_value_y = xbox1.getY(Hand.kLeft);
			}
			arcadeDrive.arcadeDrive(xbox1_value_x, xbox1_value_y);
			
//-----------------------Manipulator Controller---------------------------------//
			if(xbox2.getBButton()){
				if(solenoid1_trigger != true){
				solenoid1.set(DoubleSolenoid.Value.kForward);
				solenoid1_trigger = true;
				xbox2.setRumble(RumbleType.kRightRumble, 1);
				Timer.delay(.5);
				xbox2.setRumble(RumbleType.kRightRumble, 0);
				}
				else
				{
					solenoid1.set(DoubleSolenoid.Value.kReverse);
					solenoid1_trigger = false;
					Timer.delay(.5);
				}}
				
				else if(xbox2.getXButton()){
				if(solenoid2_trigger != true){
					solenoid2.set(DoubleSolenoid.Value.kForward);
					solenoid2_trigger = true;
					xbox2.setRumble(RumbleType.kRightRumble, 1);
					Timer.delay(.5);
					xbox2.setRumble(RumbleType.kRightRumble, 0);
					}
					else
					{
						solenoid2.set(DoubleSolenoid.Value.kReverse);
						solenoid2_trigger = false;
						Timer.delay(.5);
					}			
				}
				
			else if(xbox2.getYButton()){
				if(solenoid3_trigger != true){
					solenoid3.set(DoubleSolenoid.Value.kForward);
					solenoid3_trigger = true;
					xbox2.setRumble(RumbleType.kRightRumble, 1);
					Timer.delay(.5);
					xbox2.setRumble(RumbleType.kRightRumble, 0);
					}
					else
					{
						solenoid3.set(DoubleSolenoid.Value.kReverse);
						solenoid3_trigger = false;
						Timer.delay(.5);
					}			
				}
				
			if(xbox2.getBumper(Hand.kLeft) == true){
				Spare.set(xbox2_value_triggerLeft);
			}
			else if (xbox2.getBumper(Hand.kRight) == true){
				Spare.set(xbox2_value_triggerRight);
			}
		}
	}


I’m not sure that I can answer your autonomous behavior question, but I do have some suggestions that might help clean up some issues.

If you want to turn the motor safety feature on in teleop and off in autonomous, I would recommend that you move the invocation into the teleopInit() and autonomousInit() methods (and then remove the calls from your periodic methods).


  @Override
  public void autonomousInit() {
    autoStartTime = Timer.getFPGATimestamp();
    autoSelected = (String) chooser.getSelected();
    System.out.println("Auto selected: " + autoSelected);
    Timer timer = new Timer();
    timer.start();
    arcadeDrive.setSafetyEnabled(false);
  }

  @Override
  public void teleopInit() {
    arcadeDrive.setSafetyEnabled(true);
  }

You may want to double check some of your if/else logic, I see things like:



        if (2.5 < timeElapsed && timeElapsed < 5) {
          arcadeDrive.drive(1, 0);
        } else if (timeElapsed < 7) {
          arcadeDrive.drive(0.75, -.6);
          solenoid2.set(DoubleSolenoid.Value.kForward);
        } else if (timeElapsed < 9) {
          solenoid2.set(DoubleSolenoid.Value.kReverse);
          arcadeDrive.drive(-1, -.8);
        } else if (timeElapsed < 11) {
          arcadeDrive.drive(-.25, 0);
        } else if (timeElapsed > 12) {
          arcadeDrive.drive(0, 0);
          arcadeDrive.setSafetyEnabled(true);
        }


I would expect the following behavior from the code above (is this what you want to happen?):

  • For the first 2.5 seconds, the first condition would be false and the second condition would be true (arcadeDrive.drive(0.75, -.6) and solenoid set).
  • For time 2.5 to 5.0 the first condition would be true (arcadeDrive.drive(1, 0):wink:
  • For time 5.0 to 7.0 the second condition would be true again (arcadeDrive.drive(0.75, -.6) and solenoid set)
  • For time 7.0 to 9.0 the third condition would be true (solenoid2.set(DoubleSolenoid.Value.kReverse); arcadeDrive.drive(-1, -.8);
  • For time 9.0 to 11.0 the forth condition would be true arcadeDrive.drive(-.25, 0);
  • For time 11.0 to 12.0 NO condition would be true, motors would be uncontrolled and continue running arcadeDrive.drive(-.25, 0) unless motor safety is enabled and kills them.
  • For time 12.0 and later the last condition would be true (motors would be stopped).

Finally, you really want to avoid any use of Timer.delay() in the teleop portion of your robot. If the driver happens to be moving the robot at 10 ft/sec and presses a button that fires off one of your Timer.delay(0.5) calls, the robot will move 5 feet before the operator will have a chance to control it again (hopefully the motor safety will kick in and stop the robot if this condition occurs). You want to avoid features like gamepad rumble if they will disable the ability to drive the robot.

For an iterative base robot, you will need to keep track of the state of your different events. One way to do this is with small helper classes. For example, the following Rumbler class keeps track of the rumbling state of a gamepad and turns it off after the appropriate delay (provided you use the start() method to start it and periodically call its periodic() method):


package org.usfirst.frc.team868.robot;

import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.GenericHID.RumbleType;

public class Rumbler {

  private final XboxController gamepad;
  private boolean rumbling;
  private double startTime;
  private double duration;
  private RumbleType rtype;

  public Rumbler(XboxController gamepad, double duration, RumbleType rtype) {
    this.gamepad = gamepad;
    this.rumbling = false;
    this.startTime = 0;
    this.duration = 0.5;
    this.rtype = RumbleType.kRightRumble;    
  }
  
  public Rumbler(XboxController gamepad) {
    this(gamepad, 0.5, RumbleType.kRightRumble);
  }
  
  /**
   * Start rumbling the gamepad.
   */
  public void start() {
    rumbling = true;
    startTime = Timer.getFPGATimestamp();
  }
  
  /**
   * Periodic check to see if we need to turn off rumbling (does not block).
   */
  public void periodic() {
    if (rumbling) {
      double now = Timer.getFPGATimestamp();
      if ((now - startTime) > duration) {
        rumbling = false;
        gamepad.setRumble(rtype, 0);
      }
    }
  }

}

You could make use of this in your code by wrapping a gamepad inside a Rumbler object:



  XboxController xbox2 = new XboxController(1); // Manipulator Xbox
                                                // (xboxController)
  // Used to trigger rumble feature on Manipulator gamepad
  Rumbler rumble2 = new Rumbler(xbox2);
  

Then in your teleopPeriodic() you could make use of this and get rid of some of your timer delays:



    // -----------------------Manipulator
    // Controller---------------------------------//
    
    // Always update state of rumble on gamepad
    rumble2.periodic();
    
    if (xbox2.getBButton()) {
      if (solenoid1_trigger != true) {
        solenoid1.set(DoubleSolenoid.Value.kForward);
        solenoid1_trigger = true;
        // Turn on rumbling (periodic() call above will turn it off at the appropriate time)
        rumble2.start();
      } else {
        solenoid1.set(DoubleSolenoid.Value.kReverse);
        solenoid1_trigger = false;
        Timer.delay(.5);
      }
    }

    // ...

You would want to do something similar to keep track of your solenoid states so that you remove those timer delay invocations as well.

These kind of situations are why the command based robot framework was designed.

Good luck.

pblankenbaker brings up many good points. As to why autonomous is doing nothing (actually, what do you mean by beyblades…I’m not familiar with that expression), I’m not sure. I am curious what you have done to figure it out yourself…have you tried putting a println statement in autonomousPeriodic that shows the value of the timeElapsed variable, which is the variable controlling the timed actions? I would also take a look at the value you got in autonomousInit for autoStartTime. I am not in front of a robot right now, but I’m wondering if your construction of a Timer object and the call to the start() method (in autonomousInit) has any effect on the values returned by Timer.getFPGATimestamp() [the documentation is unclear but makes me think that I’d definitely investigate that]. If that resets or modifies the values returned by the getFPGATimestamp method, then placing the call to start() after you get the autoStartTime might be what is ailing you. Perhaps that (calling the start() method) needs to be removed or placed before you get the autoStartTime value. Bottom line, do you know the values of autoStartTime and timeElapsed (which is based on autoStartTime)?

Another thing that looks odd to me (but is ok if it is working…I’m just curious) is your motor inversions on your drive train. You invert one motor only (the rear, right motor). Again, if everything drives ok then it works…but I’d love to understand how that makes sense. I’m guessing that my tired, addled brain is missing something there.

Did you trying looking it up on the internet?

I’m honestly embarrassed to admit that I didn’t (I kind of assumed a weird typo, which is a ridiculous assumption on my part). Now that I have looked it up, I am guessing that the robot just spun in circles…which is what pblankenbaker pointed out should happen given the conditional structure.

I’ve figured out how to get autonomous functioning and selected thanks to everyone in this thread. I can’t thank you all enough. I transferred to programming solo for FRC in Java from programming for our vex bot in RobotC, so it’s been hard, but I’m glad there’s such a friendly/helpful community here.