Robots Don't Quit Error on Command Based

Hello. I’m Drason from Team 135. We used to be a C++ team but we have recently transitioned to using Java. A huge error we’ve been getting is the “Robots Don’t Quit” error for unhandled exceptions.

We’ve literally followed the example program nearly exactly and it will crash every single time.

We have no idea what’s going on and would really prefer not to use Iterative for the rest of the season. Do any teams here have any idea as to what this might be?

Sorry, I don’t have the error log with me currently. I’ll try and post that tomorrow morning.

Here is a simple subsystem.

package org.usfirst.frc.team1135.robot.subsystems;

import edu.wpi.first.wpilibj.ADXRS450_Gyro;
import edu.wpi.first.wpilibj.command.Subsystem;
import org.usfirst.frc.team1135.robot.commands.DriveJ;
import org.usfirst.frc.team1135.robot.Robot;
import org.usfirst.frc.team1135.robot.RobotMap;
import edu.wpi.first.wpilibj.RobotDrive;

import com.ctre.CANTalon;

/**
*
*/
public class DriveTrain extends Subsystem {

final static int NUMBER_DRIVETRAIN_MOTORS = 4;

static final int FRONT_LEFT = 0;
static final int REAR_LEFT = 1;
static final int FRONT_RIGHT = 2;
static final int REAR_RIGHT = 3;

static CANTalon] drivetrainMotors = new CANTalon[NUMBER_DRIVETRAIN_MOTORS];

    public RobotDrive chassis;

    public ADXRS450_Gyro gyro;


public void InitializeDriveTrainMotors()
{

drivetrainMotors[FRONT_LEFT] = new CANTalon(Robot.robotmap.FRONT_LEFT_TALON_ID);
drivetrainMotors[REAR_LEFT] = new CANTalon(Robot.robotmap.REAR_LEFT_TALON_ID);
drivetrainMotors[FRONT_RIGHT] = new CANTalon(Robot.robotmap.FRONT_RIGHT_TALON_ID);
drivetrainMotors[FRONT_LEFT] = new CANTalon(Robot.robotmap.FRONT_LEFT_TALON_ID);

chassis = new RobotDrive(drivetrainMotors[FRONT_LEFT], drivetrainMotors[REAR_LEFT], drivetrainMotors[FRONT_RIGHT], drivetrainMotors[REAR_RIGHT]);
chassis.setSafetyEnabled(false);

drivetrainMotors[FRONT_LEFT].setInverted(Robot.robotmap.LEFT_SIDE_INVERTED);
drivetrainMotors[FRONT_RIGHT].setInverted(Robot.robotmap.RIGHT_SIDE_INVERTED);
drivetrainMotors[REAR_LEFT].setInverted(Robot.robotmap.LEFT_SIDE_INVERTED);
drivetrainMotors[REAR_RIGHT].setInverted(Robot.robotmap.RIGHT_SIDE_INVERTED);
}

public void TankDrive(double leftpower, double rightpower)
{

chassis.tankDrive(leftpower, rightpower);
}

public void initDefaultCommand() 
{

setDefaultCommand(new DriveJ());
}

Here is a simple command.

package org.usfirst.frc.team1135.robot.commands;

import org.usfirst.frc.team1135.robot.Robot;

import edu.wpi.first.wpilibj.RobotDrive;
import edu.wpi.first.wpilibj.command.Command;

/**
*
*/
public class DriveJ extends Command {

public DriveJ() {
    // Use requires() here to declare subsystem dependencies
    // eg. requires(chassis);
	requires(Robot.drivetrain);
}

// Called just before this Command runs the first time
protected void initialize() {
	Robot.drivetrain.InitializeDriveTrainMotors();
}

// Called repeatedly when this Command is scheduled to run
protected void execute() {
	Robot.drivetrain.TankDrive(
			Robot.oi.GetJoystickY(Robot.oi.LEFT_JOYSTICK), 
			Robot.oi.GetJoystickY(Robot.oi.RIGHT_JOYSTICK));
}

// Make this return true when this Command no longer needs to run execute()
protected boolean isFinished() {
	
    return false;
}

// Called once after isFinished returns true
protected void end() {
	Robot.drivetrain.chassis.tankDrive(0, 0);
}

// Called when another command which requires one or more of the same
// subsystems is scheduled to run
protected void interrupted() {
}

}

It’s hard to tell without the stack trace, but my money’s on the drive train initialization. I have a feeling (and this is just a guess w/o the rest of the code) that something somewhere else is initializing or using the drive train but before the CANTalons are created resulting in a NullPointerException.

Again, just a guess, but do post those logs.

Also, “Robots don’t quit” isn’t a specific error message, it’s tacked on to the end of every error message to indicate that the robot will retry running the code.

My personal opinion:

  • Command based programming is MUCH easier for Auto mode, but makes Teleop code or other periodic code more complicated…
  • This year, each subsystem has a periodic() function (need the new WpiLib), but do not do any control in that function since it will conflict with what the command is doing. periodic() would be good for stuff like sensor sampling or debug information like SmartDashboard.
  • One thing we are trying this year to help clean up our commands is any command that runs for teleop starts with Teleop[subsystem name].

For your specific issue, one thing I notice is the command initializes the drivetrain. This is incorrect because if a command runs in auto mode on the drivetrain, then switches back to this default DriveJ command, you would reinitialize the motors again. Use the subsystem’s constructor to initialize the drivetrain instead:


public DriveTrain() {
    InitializeDriveTrainMotors();
}

Also, make sure there is a call in robotInit to initialize the drivetrain code:


Robot.drivetrain = new DriveTrain();

You also did a weird call order (that would work), execute() uses Robot.drivetrain.TankDrive, but end() uses Robot.drivetrain.**chassis.**tankDrive; chassis should probably go private inside your Drivetrain subsystem.

I would say that this is an over-generalization. If you need to be doing more than one thing at a time, command based programming is your friend.

Steven’s comments look like the likely cause.

static CANTalon] drivetrainMotors 
...
public void InitializeDriveTrainMotors()
{
drivetrainMotors[FRONT_LEFT] = ....
....
}

My only addition is that here, you’re initializing a static member variable inside a non-static method call. I recommend you remove the ‘static’ keyword from the member variable creation. If you’re doing this with the Robot.drivetrain object declaration, you’ll also want to remove its static keyword.

To facilitate this, you’ll need to pass the Robot.drivetrain object into the DriveJ constructor and keep it in a member variable. Then your command would reference it via

this.drivetrain.TankDrive(...)

This approach, while not technically required, introduces a bit of a pattern and structure to your code that makes it much easier to figure out what’s going on as things get more complex.

[/li]
This is most definitely not true for anything but extremely basic robot code. If you want to develop advanced functionality, you’re going to need some sort of framework built on top of iterative. We’ve found command-based to be adequate, with a few modifications.

JesseK’s design pattern recommendation is a solid one - for those who are interested, the pattern he mentions is called constructor injection, and the pattern of “dependency injection” in general is one that we’ve gotten a lot of mileage out of.

Thanks for the help everyone. We figured it out. It was the initialization order of the subsystem. Thanks for the tips!