1. Time-Based Programming
Adapt from FIRST Robotics Competition Control System Documentation.
First, here is what a simple code can look like for a Drivetrain with PWM controlled motors (such as SparkMax).
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj.examples.gettingstarted;
import edu.wpi.first.util.sendable.SendableRegistry;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
/**
* The methods in this class are called automatically corresponding to each mode, as described in
* the TimedRobot documentation. If you change the name of this class or the package after creating
* this project, you must also update the manifest file in the resource directory.
*/
public class Robot extends TimedRobot {
private final PWMSparkMax m_leftDrive = new PWMSparkMax(0);
private final PWMSparkMax m_rightDrive = new PWMSparkMax(1);
private final DifferentialDrive m_robotDrive =
new DifferentialDrive(m_leftDrive::set, m_rightDrive::set);
private final XboxController m_controller = new XboxController(0);
private final Timer m_timer = new Timer();
/** Called once at the beginning of the robot program. */
public Robot() {
SendableRegistry.addChild(m_robotDrive, m_leftDrive);
SendableRegistry.addChild(m_robotDrive, m_rightDrive);
// We need to invert one side of the drivetrain so that positive voltages
// result in both sides moving forward. Depending on how your robot's
// gearbox is constructed, you might have to invert the left side instead.
m_rightDrive.setInverted(true);
}
/** This function is run once each time the robot enters autonomous mode. */
@Override
public void autonomousInit() {
m_timer.restart();
}
/** This function is called periodically during autonomous. */
@Override
public void autonomousPeriodic() {
// Drive for 2 seconds
if (m_timer.get() < 2.0) {
// Drive forwards half speed, make sure to turn input squaring off
m_robotDrive.arcadeDrive(0.5, 0.0, false);
} else {
m_robotDrive.stopMotor(); // stop robot
}
}
/** This function is called once each time the robot enters teleoperated mode. */
@Override
public void teleopInit() {}
/** This function is called periodically during teleoperated mode. */
@Override
public void teleopPeriodic() {
m_robotDrive.arcadeDrive(-m_controller.getLeftY(), -m_controller.getRightX());
}
/** This function is called once each time the robot enters test mode. */
@Override
public void testInit() {}
/** This function is called periodically during test mode. */
@Override
public void testPeriodic() {}
}
Now let's look at various parts of the code.
Imports/Includes
import edu.wpi.first.util.sendable.SendableRegistry;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
import edu.wpi.first.wpilibj.motorcontrol.PWMSparkMax;
Our code needs to reference the components of WPILib that are used. In C++ this is accomplished using #include
statements; in Java and Python it is done with import
statements. The program references classes for XBoxController (for driving), PWMSparkMax / TalonFX / CANSparkMax / WPI_TalonSRX (for controlling motors), TimedRobot (the base class used for the example), Timer (used for autonomous), and DifferentialDrive (for connecting the Xbox controller to the motors).
Defining the variables for our sample robot
public class Robot extends TimedRobot {
private final PWMSparkMax m_leftDrive = new PWMSparkMax(0);
private final PWMSparkMax m_rightDrive = new PWMSparkMax(1);
private final DifferentialDrive m_robotDrive =
new DifferentialDrive(m_leftDrive::set, m_rightDrive::set);
private final XboxController m_controller = new XboxController(0);
private final Timer m_timer = new Timer();
The sample robot in our examples will have an Xbox Controller on USB port 0 for arcade drive and two motors on PWM ports 0 and 1 (Vendor examples use CAN with IDs 1 and 2). Here we create objects of type DifferentialDrive (m_robotDrive
), XboxController (m_controller
) and Timer (m_timer
). This section of the code does three things:
- Defines the variables as members of our Robot class.
- Initializes the variables.
Robot Initialization
/** Called once at the beginning of the robot program. */
public Robot() {
// We need to invert one side of the drivetrain so that positive voltages
// result in both sides moving forward. Depending on how your robot's
// gearbox is constructed, you might have to invert the left side instead.
m_rightDrive.setInverted(true);
}
The Robot constructor for our sample program inverts the right side of the drivetrain. Depending on your drive setup, you might need to invert the left side instead.
Simple Autonomous Example
/** This function is run once each time the robot enters autonomous mode. */
@Override
public void autonomousInit() {
m_timer.restart();
}
/** This function is called periodically during autonomous. */
@Override
public void autonomousPeriodic() {
// Drive for 2 seconds
if (m_timer.get() < 2.0) {
// Drive forwards half speed, make sure to turn input squaring off
m_robotDrive.arcadeDrive(0.5, 0.0, false);
} else {
m_robotDrive.stopMotor(); // stop robot
}
}
The AutonomousInit
method is run once each time the robot transitions to autonomous from another mode. In this program, we restart the Timer in this method.
AutonomousPeriodic
is run once every period while the robot is in autonomous mode. In the TimedRobot class the period is a fixed time, which defaults to 20ms. In this example, the periodic code checks if the timer is less than 2 seconds and if so, drives forward at half speed using the ArcadeDrive method of the DifferentialDrive class. If more than 2 seconds has elapsed, the code stops the robot drive.
Joystick Control for Teleoperation
/** This function is called once each time the robot enters teleoperated mode. */
@Override
public void teleopInit() {}
/** This function is called periodically during teleoperated mode. */
@Override
public void teleopPeriodic() {
m_robotDrive.arcadeDrive(-m_controller.getLeftY(), -m_controller.getRightX());
}
Like in Autonomous, the Teleop mode has a TeleopInit
and TeleopPeriodic
function. In this example we don't have anything to do in TeleopInit
, it is provided for illustration purposes only. In TeleopPeriodic
, the code uses the ArcadeDrive method to map the Y-axis of the left thumbstick of the XBoxController to forward/back motion of the drive motors and the X-axis to turning motion.
Test Mode
/** This function is called once each time the robot enters test mode. */
@Override
public void testInit() {}
/** This function is called periodically during test mode. */
@Override
public void testPeriodic() {}
}
Test Mode is used for testing robot functionality. Similar to TeleopInit
, the TestInit
and TestPeriodic
methods are provided here for illustrative purposes only.