Robot Drive Stuttering

My robot drive seems to be having a severe issue. When we push the tank drive forward, it stutters a bit and is not smooth at all. I look at all other robots and they don’t do this. I am also getting some watchdog errors as well as robotdrive not updated enough.

Here is a link to our codes github: https://github.com/Creeoer/2019-Deep-Space-Repo/tree/master/Deep-Space%20Robot/src/main/java/frc/robot

Watchdog errors are caused by iterative robot code not looping often enough. While loops within your code or timer.delay() calls will cause this. It’s best to almost never use either of these functions; any time you make part of your robot code loop or wait, none of the rest of the code runs! Without the watchdog, this would cause your mechanisms to move completely uncontrollably; the watchdog detects this state and shuts down your robot.

A very simple replacement for timer delays would be something like the bad psuedo code below (i’m 1000% sure there are better ways):

if (event that you want to trigger a delay)
set a variable (eg “eventTime”) to the current match time

if (eventTime != 0 & the current match time > eventTime)
do the things you wanted to do only after the delay here
eventTime = 0

Fix these issues and then see if the drive is still jerking around. This could be code or mechanical.

Agreed with Chris.

You’re calling Timer.Delay() in a bunch of places that you shouldn’t.

WPI’s TimedRobot works off of a 50hz loop (one tick every 20ms). In other words, the {Mode}Periodic() function is called at a rate of 50hz, or once every 20ms. When you use Timer.Delay() for close to or greater than this period, you’ll start getting watchdog errors, since the method call took >20ms to execute. Your robot may also stutter, as you’ve been seeing.

Instead of using Timer.Delay(), I would have a separate Timer object that keeps track of how long something has been running for (one Timer instance per event you want to track, don’t try and share them). You can use {TimerInstance}.start() and {TimerInstance}.get() (returns the amount of time, in seconds, as a double, the timer has been running) to do this, and {TimerInstance}.reset() to reset the timer. This way, you can let the loop complete without blocking it.

1 Like

Ok, Ill try and comment out that for now and see if its still stuttering.

Well, the Timer.delay() was there for a reason. Its to set the motors to a specific location and they have worked in the past years ago so i didnt think it was a problem. I will get back to you once i get to the robot. If you are a programmer you know the struggle.

The issue is that, none of the other code runs. So if your drivetrain was set to full speed, for example, even if your driver lets go of the sticks, the part of the code that controls the drivetrain won’t run again until the timer is done. It’s possible that in the past, programmers shut down the watchdog (which is possible, but a super bad idea) and only used short delays. This is pretty unsafe.

You can still use time to schedule events, you just have to think of your code as something that runs repeatedly. Store the information you need in variables or objects in order for your robot to know when to start and stop doing things, as that’s how you pass information between iterations. Then make the timed actions conditional on being within the desired time frame.

Once you start thinking of your robot control from this paradigm, it’ll make sense, and future behaviors will be easier to implement.

I’m without looking at your code it is possible you are browning out? Check to see if your voltage drops too far for the Roborio it will cut power to the motors and can cause a jittering effect

How would I be able to fix that? Is that generally on the programmers side or electrical?

A common way to replace delays like that in Embedded programming is to use the system clock to check how much time has elapsed since you started an action. This is one of the functions offered by the Timer class in WPILIB. Allowing you to check if the amount of time has passed instead of halting all robot code until the time has passed. You would probably need to re-write your logic a bit to make this work.

Its also worth noting that such functionality is offered in the Command-Based Template for Commands already. You can set a timeout for a command, so you set a motor and the command will keep it going until the timeout is reached.

Depending on how your robot is set up and what you’re doing, I would recommend using sensors instead of “dead reckoning” to a specific location. If you’re running into a hardstop that’s okay, but sensors make it much, much easier to know where your manipulator/subsystem is at any given time, without having to “guess” in the code. I see you have a potentiometer set up, so that’s good. Hopefully you’ll eventually be able to integrate it into your code, since I don’t see you using it anywhere at the moment.

I noticed you pushed some new code, and in particular are using a Timer object. Good! A couple notes:

  • Nitpick: You’re assigning your Timer object twice in robotInit()
  • MotorController objects (VictorSPX, Talon, etc) only accept a double input from -1 to 1 in their set() method, with 1 being 100% forward, 0.5 being 50% forward, etc. Anything over or under this is truncated. Thus, your calls to arm.set(10) is actually arm.set(1).
  • You’re setting timePassed to timer.get() in robotInit(), which means it will only be updated once when your code starts (and will actually be set to 0.0 at the moment), then never set again. I would recommend not using a “timePassed” class variable, since this is already stored in your Timer object and can be easily obtained by calling timer.get(). Instead, you can use a local variable in your method to get a consistent comparison of time.
  • You’re starting your timer and then immediately stopping it in some of your if statements, such as here. I recommend calling stop() and reset() when your if statement (for example, if(timePassed > 0.06)) is executed instead. You’ll probably also want to move your other “ending state” logic in as well.

Overall example:

public void arms(){
        if(stickL.getRawButton(3) && !areArmsOn && currentArmPos == 2){
            areArmsOn = true;
            arm.set(1);
            timer.start(); // I think this can be called multiple times without issue?
            if(timePassed > 0.06) {
                arm.set(0);
                areArmsOn = false;
                currentArmPos = 3;
                timer.stop();
                timer.reset();
            }
       }
       // More code...
}

Remember: This code is being executed once every 20ms. You want to allow it to run through your code really fast without blocking it. The teleopPeriodic() method will be called every 20ms, which is in turn is calling your arms() method. You don’t want it to block when these methods are called, which is why swapping to a Timer is nice. This does, however, mean that you need a slightly different mindset of how this code is being executed. Instead of having one block of code execute everything it needs to in one pass – eg with Timer.delay()– you need to remember that this code can be executed 1, 2, 5, 10, or even more times before every part of the method you want to execute is actually executed. Namely, the part in your timePassed if statements; these will only run once your timer has been running for a certain amount of time.

Depending on how complex you want your functions to be, it may be a good idea to consider swapping over to CommandBasedRobot, which gives you a nice, structured system that pulls logic into separate classes. This allows much better separation of code, which in turn makes very complex robots much more manageable. It comes at the cost of abstracting away some code execution logic – namely executing Commands with WPILib Scheduler constructs, such as when you’re mapping joystick button actions to Commands – and makes it a little more difficult to map out when things will execute just by looking at code, however I think it’s a much nicer system than shoving everything into one file.

Let me know if I overexplained this…

It would be Mechanical in the gearboxes. Just look at the Battery voltage in the Driverstation and if that looks good than it’s not the issue.