-
Notifications
You must be signed in to change notification settings - Fork 1
Seminar Oct 25, 2018
-
gradlew
,gradlew.bat
, andgradle/wrapper
in project folder -
build.gradle
file contains correct version ofGradleRIO
plugin andWPILIB
/related libraries - Java source files are located in
src/main/java/frc/team865/robot
package directory -
ROBOT_CLASS
constant inbuild.gradle
points to the main robot class - The main robot class is a java file stored at the Java sources root and extends
IterativeRobot
, but should be empty for now - The
team
property also need to be set correctly for it to find the network IP
- Either
gradlew build
in command line or find build->build in the Gradle tab in IntelliJ - If libraries not already downloaded, it might not be possible at school
plugins {
id "java"
id "edu.wpi.first.GradleRIO" version "2018.06.21"
}
def ROBOT_CLASS = "frc.team865.robot.RobotExample"
// Define my targets (RoboRIO) and artifacts (deployable files)
// This is added by GradleRIO's backing project EmbeddedTools.
deploy {
targets {
target("roborio", edu.wpi.first.gradlerio.frc.RoboRIO) {
// Team can be overridden by command line, for use with VSCode
team = getTeamOrDefault(865)
}
}
artifacts {
artifact('frcJava', edu.wpi.first.gradlerio.frc.FRCJavaArtifact) {
targets << "roborio"
// Debug can be overridden by command line, for use with VSCode
debug = getDebugOrDefault(false)
}
}
}
wpi {
wpilibVersion = "2018.4.1"
wpiutilVersion = "3.2.0"
ctreVersion = '5.3.1.0'
ntcoreVersion = "4.1.0"
cscoreVersion = "1.3.0"
shuffleboardVersion = '1.3.1'
}
// Defining my dependencies. In this case, WPILib (+ friends)
dependencies {
compile wpilib()
compile ctre()
}
// Setting up my Jar File. In this case, adding all libraries into the main jar ('fat jar')
// in order to make them all available at runtime. Also adding the manifest so WPILib
// knows where to look for our Robot Class.
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
manifest edu.wpi.first.gradlerio.GradleRIOPlugin.javaManifest(ROBOT_CLASS)
}
task wrapper(type: Wrapper) {
gradleVersion = '4.7'
}
package frc.team865.robot;
import edu.wpi.first.wpilibj.IterativeRobot;
public class RobotExample extends IterativeRobot {
// Add code here
}
- Inside the brackets we add various procedures that define the behaviour of the robot. These include:
robotInit
robotPeriodic
disabledInit
disabledPeriodic
teleopInit
teleopPeriodic
autonomousInit
autonomousPeriodic
- and some other ones
package frc.team865.robot;
import edu.wpi.first.wpilibj.IterativeRobot;
public class RobotExample extends IterativeRobot {
@Override
public void robotInit() {
// add code on robot init
}
}
- Replace the comment with a print
package frc.team865.robot;
import edu.wpi.first.wpilibj.IterativeRobot;
public class RobotExample extends IterativeRobot {
@Override
public void robotInit() {
System.out.println("Hello Robot");
}
}
- Start robot with the main circuit breaker
- Wait until wireless connection shows up to connect (or connect using a cable)
- Note: the radio needs to be imaged and named the first time we use it
- Start driver station and read the status lights for comms, robot code, and joysticks
- Check that it is in
TeleOperated
mode - Click on the settings button to show prints
- Make sure the robot space is clear and call out before enabling it
- Disabling the robot (Anyone running the robot MUST know)
- Enter key on any screen to normal disable
- SPACE key on any screen to EMERGENCY disable
- Command line:
gradlew deploy
-
IntelliJ: Under Gradle tab, double click on
embeddedTools > deploy
- Use
discoverRoboRIO
to troubleshoot the network
- The robot code starts automatically after deploy, calling
robotInit
- Other modes can be selected manually and clicking Enable. In an actual match this is handled by the Field Management
- If there is an error, it will keep atempting to restart the code
- Code can be restarted from the driver station
-
The drive train is the most stable mechanism to test things
-
We use differential drive/tank drive/West coast drive
-
Controls left and right side independently
-
Each side has two motors with a gearbox and chains to move multiple wheels
-
The two sides of the drive trains are reversed
- Lets us send digital signals to the motors through code
- There are many types of speed controllers, each with its own code interface
- We use one called VictorSPX on our robot
- In code, we need to specify the pin number connected to the RoboRIO
- We can then send it a decimal value between -1 and 1 indicating voltage percent
- The two motors on each side must be in sync and especially not run against each other!
- Motors must stop when the robot is disabled
- Motors should have a dead band to prevent wearing out
- Our type of speed controller requires an external library
In the dependencies section in build.gradle
, add this line:
dependencies {
compile wpilib()
compile ctre() // Add the Cross The Road Electronics library for speed controller
}
Add the following lines to the robotInit
method:
WPI_VictorSPX rightMotor1 = new WPI_VictorSPX(1); // pin = 1
WPI_VictorSPX rightMotor2 = new WPI_VictorSPX(3); // pin = 3
WPI_VictorSPX leftMotor1 = new WPI_VictorSPX(2);
WPI_VictorSPX leftMotor2 = new WPI_VictorSPX(8);
- Speed controller groups helps to sync multiple motors
Add the following lines to make groups
SpeedControllerGroup rightMotors = new SpeedControllerGroup(rightMotor1,rightMotor2);
SpeedControllerGroup leftMotors = new SpeedControllerGroup(leftMotor1,leftMotor2);
- Use the built-in
DifferentialDrive
class to help control both sides - It automatically inverts the first motor group, so we don't need to do it in our code
- We will use it after to respond to controllers
DifferentialDrive differentialDrive = new DifferentialDrive(rightMotors, leftMotors);
package frc.team865.robot;
import com.ctre.phoenix.motorcontrol.can.WPI_VictorSPX;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.SpeedControllerGroup;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
public class RobotExample extends IterativeRobot {
@Override
public void robotInit() {
WPI_VictorSPX rightMotor1 = new WPI_VictorSPX(1);
WPI_VictorSPX rightMotor2 = new WPI_VictorSPX(3);
WPI_VictorSPX leftMotor1 = new WPI_VictorSPX(2);
WPI_VictorSPX leftMotor2 = new WPI_VictorSPX(8);
SpeedControllerGroup rightMotors;
SpeedControllerGroup leftMotors;
rightMotors = new SpeedControllerGroup(rightMotor1,rightMotor2);
leftMotors = new SpeedControllerGroup(leftMotor1,leftMotor2);
DifferentialDrive differentialDrive;
differentialDrive = new DifferentialDrive(rightMotors, leftMotors);
}
}
- Right now, it doesn't do anything with the differential drive
- We need to move it to the class level to make it usable from other methods
Move the line DifferentialDrive differentialDrive
; to just after the class declaration, which becomes
private DifferentialDrive differentialDrive;
Add a new procedure into the class:
@Override
public void disabledInit() {
differentialDrive.stopMotor();
}
- We need a controller for the driver to send signals to the robot
- There are various controllers we could use
- We will use an Xbox controller
Add a new class member variable under differentialDrive
:
private XboxController xboxController;
And initialize it in the robotInit
method with its controller port number:
xboxController = new XboxController(0);
Now we are ready to write robot driving code
This section of code will be ran repetitively in the TeleOp
mode and will drive the robot
@Override
public void teleopPeriodic() {
double xSpeed = xboxController.getY(GenericHID.Hand.kLeft);
double zRotation = xboxController.getX(GenericHID.Hand.kRight);
boolean isQuickTurn = xboxController.getBumper(GenericHID.Hand.kLeft);
differentialDrive.curvatureDrive(xSpeed,zRotation, isQuickTurn);
}
It does the following two things for every iteration (approximately 20ms):
- Get inputs from the Xbox Controller
- Tell the
differentialDrive
to perform a curvature drive (also known as Cheesy Drive) with those input values. This built-in function handles the limit, dead band, and motor speed calculations
Specifically, the left Y axis tells the robot how fast to drive, the right X axis tells the robot what the curvature is, and when the left bumper is held down, the robot should turn in-place
package frc.team865.robot;
import com.ctre.phoenix.motorcontrol.can.WPI_VictorSPX;
import edu.wpi.first.wpilibj.GenericHID;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.SpeedControllerGroup;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
public class RobotExample extends IterativeRobot {
private DifferentialDrive differentialDrive;
private XboxController xboxController;
@Override
public void robotInit() {
WPI_VictorSPX rightMotor1 = new WPI_VictorSPX(1);
WPI_VictorSPX rightMotor2 = new WPI_VictorSPX(3);
WPI_VictorSPX leftMotor1 = new WPI_VictorSPX(2);
WPI_VictorSPX leftMotor2 = new WPI_VictorSPX(8);
SpeedControllerGroup rightMotors;
SpeedControllerGroup leftMotors;
rightMotors = new SpeedControllerGroup(rightMotor1,rightMotor2);
leftMotors = new SpeedControllerGroup(leftMotor1,leftMotor2);
differentialDrive = new DifferentialDrive(rightMotors, leftMotors);
xboxController = new XboxController(0);
}
@Override
public void disabledInit() {
differentialDrive.stopMotor();
}
@Override
public void teleopPeriodic() {
double xSpeed = xboxController.getY(GenericHID.Hand.kLeft);
double zRotation = xboxController.getX(GenericHID.Hand.kRight);
boolean isQuickTurn = xboxController.getBumper(GenericHID.Hand.kLeft);
differentialDrive.curvatureDrive(xSpeed,zRotation, isQuickTurn);
}
}
- We need pneumatics to shift between low and high gear, or when sometimes when it's on neither
- Use a compressor to compress the air into the air tank and use a solenoid to control the shifting
- Turn on the compressor at the beginning so it can be charged
- We want to program it so it can be toggled with the controller
Add two instance variables to the class:
private Compressor compressor;
private Solenoid shifter;
Initialize it in robotInit
:
compressor = new Compressor(0); // pin = 0
compressor.setClosedLoopControl(true); //ensures the compressor is turned on at start
shifter = new Solenoid(1); // pin = 1
shifter.set(false); // make sure it's not shifting right now
Add code that controls these in teleopPeriodic
compressor.setClosedLoopControl(xboxController.getBackButton());
shifter.set(xboxController.getBumper(GenericHID.Hand.kRight));
Now we have fully functional TeleOp
code.
package frc.team865.robot;
import com.ctre.phoenix.motorcontrol.can.WPI_VictorSPX;
import edu.wpi.first.wpilibj.*;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
public class RobotExample extends IterativeRobot {
private DifferentialDrive differentialDrive;
private XboxController xboxController;
private Compressor compressor;
private Solenoid shifter;
@Override
public void robotInit() {
WPI_VictorSPX rightMotor1 = new WPI_VictorSPX(1);
WPI_VictorSPX rightMotor2 = new WPI_VictorSPX(3);
WPI_VictorSPX leftMotor1 = new WPI_VictorSPX(2);
WPI_VictorSPX leftMotor2 = new WPI_VictorSPX(8);
SpeedControllerGroup rightMotors;
SpeedControllerGroup leftMotors;
rightMotors = new SpeedControllerGroup(rightMotor1, rightMotor2);
leftMotors = new SpeedControllerGroup(leftMotor1, leftMotor2);
differentialDrive = new DifferentialDrive(rightMotors, leftMotors);
xboxController = new XboxController(0);
compressor = new Compressor(0);
compressor.setClosedLoopControl(true);
shifter = new Solenoid(1);
shifter.set(false);
}
@Override
public void disabledInit() {
differentialDrive.stopMotor();
}
@Override
public void teleopPeriodic() {
double xSpeed = xboxController.getY(GenericHID.Hand.kLeft);
double zRotation = xboxController.getX(GenericHID.Hand.kRight);
boolean isQuickTurn = xboxController.getBumper(GenericHID.Hand.kLeft);
differentialDrive.curvatureDrive(xSpeed, zRotation, isQuickTurn);
compressor.setClosedLoopControl(xboxController.getBackButton());
shifter.set(xboxController.getBumper(GenericHID.Hand.kRight));
}
}
- The most basic autonomous task in 2018
- Need to drive forward past the auto line
- We need to tell the robot how fast to drive and for how long
- We need to periodically send signals to the motor to indicate we still want it moving for motor safety
- Use a
Timer
object to keep track of time
Add the timer in the class declaration (make sure the proper timer in edu.wpi.first.wpilibj
package is imported. There are other timers):
private Timer baselineTimer;
Create a new method, autonomousInit
. Create and start the timer in there:
@Override
public void autonomousInit() {
baselineTimer = new Timer();
baselineTimer.start();
}
Now create another new method to run periodically:
@Override
public void autonomousPeriodic() {
if (baselineTimer.get() < 3.0){
differentialDrive.tankDrive(-0.2, -0.2, false);
} else {
differentialDrive.stopMotor();
}
}
- This periodic method checks if the timer has reached 3 seconds. If it hasn't, then keep driving. Otherwise stop the program.
- Currently we have set the Tank Drive power to 20 percent - a very low power
- Try autonomous programs with smaller values first because we don't know that it works
- When we see it working, increase the value or the time
package frc.team865.robot;
import com.ctre.phoenix.motorcontrol.can.WPI_VictorSPX;
import edu.wpi.first.wpilibj.*;
import edu.wpi.first.wpilibj.drive.DifferentialDrive;
public class RobotDone extends IterativeRobot {
private DifferentialDrive differentialDrive;
private XboxController xboxController;
private Timer baselineTimer;
private Compressor compressor;
private Solenoid shifter;
@Override
public void robotInit() {
WPI_VictorSPX rightMotor1 = new WPI_VictorSPX(1);
WPI_VictorSPX rightMotor2 = new WPI_VictorSPX(3);
WPI_VictorSPX leftMotor1 = new WPI_VictorSPX(2);
WPI_VictorSPX leftMotor2 = new WPI_VictorSPX(8);
SpeedControllerGroup rightMotors;
rightMotors = new SpeedControllerGroup(rightMotor1, rightMotor2);
SpeedControllerGroup leftMotors;
leftMotors = new SpeedControllerGroup(leftMotor1, leftMotor2);
differentialDrive = new DifferentialDrive(rightMotors, leftMotors);
xboxController = new XboxController(0);
compressor = new Compressor(0);
compressor.setClosedLoopControl(true);
shifter = new Solenoid(1);
shifter.set(false);
}
@Override
public void disabledInit() {
differentialDrive.stopMotor();
}
@Override
public void teleopPeriodic() {
double xSpeed = xboxController.getY(GenericHID.Hand.kLeft);
double zRotation = xboxController.getX(GenericHID.Hand.kRight);
boolean isQuickTurn = xboxController.getBumper(GenericHID.Hand.kLeft);
differentialDrive.curvatureDrive(xSpeed,zRotation, isQuickTurn);
compressor.setClosedLoopControl(xboxController.getBackButton());
shifter.set(xboxController.getBumper(GenericHID.Hand.kRight));
}
@Override
public void autonomousInit() {
baselineTimer = new Timer();
baselineTimer.start();
}
@Override
public void autonomousPeriodic() {
if (baselineTimer.get() < 3.0){
differentialDrive.tankDrive(-0.2, -0.2, false);
} else {
differentialDrive.stopMotor();
}
}
}
- Add constants file
- Organize into subsystems
- Add other mechanisms
- Sending data to the driver station
- Drive for a certain distance