Skip to content
This repository has been archived by the owner on Dec 13, 2019. It is now read-only.

Seminar Oct 25, 2018

Yu Liu edited this page Jan 15, 2019 · 1 revision
Make sure the project has a proper GradleRIO build structure
  • gradlew, gradlew.bat, and gradle/wrapper in project folder
  • build.gradle file contains correct version of GradleRIO plugin and WPILIB/related libraries
  • Java source files are located in src/main/java/frc/team865/robot package directory
  • ROBOT_CLASS constant in build.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
Try Building the project
  • 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
Example Gradle File
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'
}
The program should now look like this
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
Add the robotInit procedure to our code
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
Our current code
package frc.team865.robot;

import edu.wpi.first.wpilibj.IterativeRobot;

public class RobotExample extends IterativeRobot {
    @Override
    public void robotInit() {
        System.out.println("Hello Robot");
    }
}
Connect to the 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
Robot Safety
  • 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
Deploy Code
  • Command line: gradlew deploy
  • IntelliJ: Under Gradle tab, double click on embeddedTools > deploy
  • Use discoverRoboRIO to troubleshoot the network
Run code
  • 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
Drive train mechanics
  • 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

Speed Controllers
  • 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
Motor Safety
  • 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
Add Speed Controller Library to build script
  • 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
}
Create the speed controllers in code

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);
Create speed controller groups
  • 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);
Controlling the left and right motors
  • 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);
Our current code
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);
    }
}
Move differential drive variable to class level
  • 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;
Make sure to stop the motor when disabled

Add a new procedure into the class:

@Override
public void disabledInit() {
    differentialDrive.stopMotor();
}
Create a controller for the robot
  • 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

Add the teleopPeriodic procedure

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):

  1. Get inputs from the Xbox Controller
  2. 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

Our current code
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);
    }
}
Pneumatics and Gear Shifting
  • 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.

Our current 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));
    }
}
Baseline auto
  • 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.
Adjusting auto values for safety
  • 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
Our final 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 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();
        }
    }
}
Things to do now
  • Add constants file
  • Organize into subsystems
  • Add other mechanisms
  • Sending data to the driver station
  • Drive for a certain distance