The Basics of Java Programming and Command-Based Robot Projects TheRobettes.com Agenda • • • • • • • • Packages and Classes Variables and Objects Methods Command-Robot Concepts If/Else Statements For Loops and While Loops Key Command-Robot Classes Glossary, Q&A, Conclusions Packages and Classes • All java code belongs to a specific class. • Packages are sets of related Classes • Every project starts with one ‘main’ class/method • More detailed classes are defined and called on to handle various specific duties. Packages and Classes - examples • Generic classes that sort and organize import java.util.*; import com.first.team2177.common.*; • Standard java classes for ‘streams’ and internet import java.io.*; // read from and write to files (and to & from web sites) import java.net.*; // internet utility library import javax.servlet.*; // http web hosting library import javax.mail.*; // use this to spam your parents all night • US F.I.R.S.T. library packages import edu.wpi.first.wpilibj.*; // Talon, Limit, Joystick… import edu.wpi.first.wpilibj.command.*; • Split your Robot into 3 or more packages import com.first.team2177.robot.*; import com.first.team2177.robot.subsystems.*; import com.first.team2177.robot.commands.*; // the main stuff // 2-4 physical subsets // 5-15 ‘actions’ that the subsystem need to do Packages and Classes – examples 2 • The actual class-list of our Ultimate robot. Robot extends ...wpilib.IterativeRobot RobotMap extends java.lang.Object (implied) OI extends java.lang.Object (implied) // package is ...team2177.robot.* DriveTrain extends ...wpilib.SubSystem ClimberSys extends …wpilib.SubSystem DumperSys extends …wpilib.SubSystem // package is …team2177.robot.Subsystems.* CommandBase extends ...wpilib.Command; AutonomousCmd extends CommandBase TankDrive extends CommandBase ArcadeDrive extends CommandBase ClimbSetup extends CommandBase ClimbPreclimb extends CommandBase ClimbNextStep extends CommandBase ClimbTower extends …wpilib.CommandGroup; // package is ...team2177.robot.Commands.* Variables and Objects • • • • Variables hold a value or setting. Variables give a name & type to what the value is for. Variables can be passed between classes & methods. Variables can keep the same value, or be changed. • There are several basic java variable types. Integers, Long Floats, Doubles Booleans • “Object” variables hold a reference to a java-Class. • String variable are Objects, but work like a basic type. Variables and Objects - examples • In our project, ‘CommandBase’ defines many variables that are used by other ‘Action’ classes. DriveTrain drivetrain; ClimberSys climber; DumperSys dumper; Joystick leftStick; Joystick rightStick; Joystick shootStick; // each subsystem is its own type. // 3 instances of the same type // these value can change during a game, but more likely just once on startup double driveBias = 0.7; // 0.5 would be a perfect left-right balance double practiceSpeedFactor = 1.0; // 1.0 = full-power, down to .6 or .7 for freshmem…. boolean isPitTesting = false; // if true, the Climber system needs to reduce lift-power // "debug" logging variables java.util.Vector messageTank; long starttime = System.currentTimeMillis() / 1000; Methods • Groups of statements performed as a set. Defined by a name, (parameters), and a return type. Class MySampleClass { boolean isButtonPressed() { statements…; return joyStick.getButtonPressed(1);} void startOrStop(boolean) { statements; …; } long setArmHeight(double left, double right) { …; } Talon getActiveMotor() { …; return leftFrontMotor; } void doSomething() { // sample statement … // calling ‘local’ methods -- i.e. from MySampleClass, or else any class it extended… startOrStop( isButtonPressed() ); // bool return used as a bool parameter long h = setArmHeight( fX() , fY() ); // double,double IN, long OUT // calling methods from other classes -- i.e. ‘imported’ variables and types driveTrain.tankDrive( joystick.getY(), joystick2.getY() ); // double-double } // end of doSomething method } // end of MySampleClass Methods – debug() • Being ‘static’ (global), this can be called from any class (if imported). i.e. CommandBase.debug(“my message”); • When called locally, including by a subclasses, only the method name is needed. public static void debug(String msg) { if (messageTank.contains(msg) == false) { messageTank.add(msg); // the parameter text. // print the given message, along with the (first)occurrence-time long time = (System.currentTimeMillis() / 1000) - starttime; String debugTime = "" + (time / 60) + ':' + (time % 60) + " "; System.out.print(debugTime + msg); } } Command-Based Robot Concepts • “It will make simple programs more complicated and the more complicated programs simpler.” • Organized into Subsystem and Command • Easier to make adjustments throughout the season • Code is more spread out throughout the classes OI TankDrive Robot ArcadeDrive Dumper AutonomousCmd RobotMap ClimbSetup ClimbPreClimb ClimbTower ClimbNextStep ClimbNextStep DriveTrain DumperSys ClimberSys ClimbNextStep Motor controllers, sensors, etc Control Classes Subsystems Commands Command Groups Command-Robot Concepts – examples • • • • ‘Robot’ creates our Autonomous & CommandBase CommandBase creates each of our subsystems. CmdBase->OI->init() then creates teleOp commands. ClimbTower and Autonomous are groups, they also create other Cmds. Robot - one instance, created by .wpilib RobotMap & OI – no instances. DriveTrain, ClimberSys , DumperSys - one instance each. AutonomousCmd - one instance, started and stopped by .wpilib/Robot TankDrive & ArcadeDrive – 1 & 2 instances, started by OI-designated buttons ClimbSetup – one instance, created and started by ClimberSys (then optionally by OI/joystick) ClimbPreclimb – one instance, created and started by OI/joystick ClimbTower – one instance, created and started by OI/joystick ClimbNextStep – 9 instances, created and started by ClimbTower CommandBase – 14 (extended) instances. Command-Robot Concepts – code sample • Calling “new” [ClassName](); truns a Class into an Object. Here is the one global method of OI which generates our teleOp commands. static void init(Joystick leftStick, Joystick rightStick, Joystick shootStick) { // this is just condense slightly. // define the climb-actions based on top 3 “shooter” buttons { left, middle, right } Button climbSetupbutton = new JoystickButton(shootStick, 4); // left button climbSetupbutton.whenPressed(new ClimbSetup()); // besides being ‘default ‘, now also allow user to select this action climbSetupbutton = new JoystickButton(shootStick, 3); // middle button climbSetupbutton.whenPressed(new ClimbPreclimb()); // user-action, perform when within the tower perimeter Button climbExecuteButton = new JoystickButton(shootStick, 5); // right button climbExecuteButton.whenPressed(new ClimbTower()); // in position & verified, "GO" /* * Drive-Style Options * use left-driver’s top 3 buttons { left, middle, right } * to select between T-A-BS = Tank - Arcade – BananaSplit (aka Kaj) */ /*left*/ new JoystickButton(leftStick, 4).whenPressed(new TankDriving()); /*middle*/ new JoystickButton(leftStick, 3).whenPressed(new ArcadeDriving(false)); /*right*/ new JoystickButton(leftStick, 5).whenPressed(new ArcadeDriving(true)); // true signals the right-stick for the rotation } If/Else Statements • One, two, or many ‘conditions’ can cause different actions. public void exampleCode(int motorSpeed) { boolean buttonPressed = joystick.getRawButton(4); if ( buttonPressed ) { doThis(1.0); // because of {}, ‘this’ and doMore(); // ‘more’ are always executed back to back. } else if ( booleanA && booleanB ) sideMotor.set( -1.0 ); else if ( limitSwitch.isPressed() || motorSpeed==0 ) sideMotor.set( 1.0); else defaultAction(); } If/Else Statements – Sample Code • Our ‘preclimb’ command-code uses multiple ifs. public class ClimbPreclimb extends CommandBase { boolean isOnTarget = false; // this is used by both different methods below public void execute() { if (climber.shoulderPoint() < 1900) // Early on-- Raise the shoulder(&winch) quicker, and the elbow slower. climber.NudgeArms( .50, .25, .35 ); else if (isOnTarget) // Once raised -- Stall the shoulder-motor with positive-lift, this is to avoid ‘bouncing’ climber.NudgeArms( .1, 0, 0 ); else {// Default-Action, “lower” to the target set-points. isOnTarget = climber.SetTargetPositions("RaiseHooks - elbow-up", .6, 2600, 951, 1300 ); if (isOnTarget) // a nested test, log when we have completed this movement debugPrint(“now at preclimb positions " + climber.shoulderPoint() + ", …. "); } protected void interrupted() { isOnTarget = false; // this reset is needed when we repeatedly go between driving and preclimb } } While Loops • While loops run while a certain condition is true. void raiseRobotArm() { while (armHeight < 30) // condition { // loop actions… outputPower = …statement…; armHeight = setArm( outputPower ); } } While Loops – Sample Code • Our ‘ClimbSetup’ Command performs back-to-back loops. public void execute() { if (climber.winchPoint() > 900) { // only perform these loops once, on startup. // Early on, Loosen the straps and Raise the shoulder, to assure that the elbow can be reversed-to-down. // target is effectively a forward-leaning high-five position while (climber.shoulderPoint() < 2500 || climber.winchPoint() < 2600 ) { climber.setTargetPositions("pre-driving-1", 0.5, 2700, 1417, 2809 ); } // Once raised, Swing the shoulder & elbow 'down', match speeds to minimize slack. while (climber.winchPoint() > 800) { climber.nudgeArms( -.15, -.60, -.55 ); } } // end of if // …always…, get to and maintain the proper ready-for-preclimb position climber.setTargetPositions("driving", 0.3, 839, 2612, 700 )); } // end of method For Loops • For loops run for a certain count of times. (here the count is the condition) void runAutonomous() { // for each ‘N’, 1-5 for (int N=1; N<=5; N++) // something has to eventually change N to end the loop… // here N++ is a statement that does an N+1 each time. { // loop actions… fireCannon( N ); } // end of for loop } // end of runAutonomous() For Loops - example • What if you needed to steer this ? final int ROWS = 4; final int AXELS = 30; …initialize…() { DriveWheel wheels[][] = new DriveWheels[AXELS][ROWS]; for (int row = 0; row < ROWS; row++ ) for (int axel = 0; axel < AXELS; axel++ ) wheels[axel][row] = new DriveWheel( (row*AXELS) + axel ); } Key Command-Robot Classes • Subsystem • Command • CommandGroup Key Classes – SubSystems • The various parts of your robot drivetrain, climber, dumper. • Make a class for each subsystem – File, new file, Command-Based Robot, Subsystem • Add sensor variables, to get readings (read=input). // i.e. long count = myEncoder.get(); • Add motor variables, to set motions (write=output). // i.e. myTalon.set( voltage ); • Add action methods, to be used by Commands. – // i.e. MyCommand() { mySubSystem.doSomething(); } Key Classes – Subsystems – DriveChassis example • Private variables for sensors and motors Talon leftFront, rightFront, etc. Encoder leftSpeed, rightSpeed; • Public action methods, for use by the Commands. void tankDrive(double left, double right) {…} void arcadeDrive(double momentum, double angle) {…} Key Classes – Subsystems – Climber example • Private variables for sensors and motors Talon shoulder = new Talon( RobotMap.SHOULDER_MOTOR); Talon elbow =…; Talon winch1, winch2; “Potentiometer” shoulderPot = new AnalogChannel( RobotMap.SHOULDER_POT); Potentiometer elbowPot = …; Potentiometer winchPot = …; • Private action methods, for local (climber) use. void setShoulderPosition(long) {…} void setElbowPosition(long) {…} void setWinchPosition(long) {…} • Public action methods, for use by the Commands. void nudge(double shoulderPwr, double elbowPwr, double winchPwr) {…} void setPos(long shoulderTgt, long elbowTgt, long winchTgt) {…} long get___Position() { return ___Pot.getAverageValue(); } Key Classes – Commands • Classes that use the functions of the subsystems to perform operations • Make a class for each command – File, new file, Command-Based Robot, command • One(1) Autonomous command may use every Subsystem • Each Subsystem usually creates one defaultcommand for TeleOp. • The OI class can make commands that exchange control of a Subsystem. Key Classes – Commands – ‘execute’ code • Typically each command will ‘require’ a SubSystem in its constructor. public class TankDriving extends CommandBase { public TankDriving() { requires(drivetrain); // requires(Subsystem) is defined by .wpilib.Command } // end of method } // end of class • ‘execute()’ has to be defined by every Command. What execute defines are statements performed by a nearcontinuous .wpilib-defined while loop. // Called repeatedly when this Command is scheduled to run protected void execute() { drivetrain.tankDrive( leftStick.getY(), rightStick.getY() ); } Key Classes – Commands – ‘isFinished’ code • We define ‘isFinished’ in our CommandBase, to just return false. So generally an OI-joystick action is the only thing to interrupt the execute()-loop • We needed to define isFinished() within our ClimbNextStep. This is because they are each needed back-to-back while actually climbing. public void execute() { // simplified isFinished = climber.SetTargetPositions(cmdName, liftPower, shoulderTgt, elbowTgt, winchTgt); } // interrupt one ClimbNextStep command so the next can begin. public boolean isFinished() { return isFinished; } Key Classes – Commands – other code • These methods must be defined somewhere every Command. • We do so as shown within our CommandBase class. // Called just before this Command runs the first time protected void initialize() { } // 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() { } // Called when another command which requires one or more of the same // subsystems is scheduled to run protected void interrupted() { } Key Classes - Command Groups • A class that performs a chain of commands. • Key library methods addSequential(Command) addParallel(Command) Key Classes - Command Groups – ClimbTower code • We start this group in tele-op mode… Effectively when the group needs to be executed, .wpilib code will execute each command at the right time, ‘while needed’. ClimbTower() { // this method gets called by ‘new ClimbTower()’. Within OI-init(). // Lift onto level-1 bar addSequential(new ClimbNextStep("lift-1", 1.0, 1800, 999, 150 )); addSequential(new WaitCommand( 1 )); // pause to stop swinging // Lift arms to level-2 bar (elbow towards 'up') addSequential(new ClimbNextStep("pre-up-1", .35, 3098, 999, 3550 ));// raise shoulder, addSequential(new ClimbNextStep("pre-up-2", .35, 3098, 1536, 3788 ));// straighten elbow addSequential(new ClimbNextStep("pre-up-3", .35, 2872, 1517, 3550 ));// swing shoulder forward addSequential(new ClimbNextStep("lift-2", 1.0, 1800, 999, 150 )); addSequential(new WaitCommand( 7 )); // WaitCommand is defined in .wpilib… // … then prepare for and lift onto level 3, it is similar steps as level-2 above, just different numbers…. Key Classes - Command Groups – ClimbTower code • Examples of Next-Step positions… addSequential(new ClimbNextStep("pre-up-1", lowPower, #, #, #...) addSequential(new ClimbNextStep("pre-up-3", lowPower, #, #, #... addSequential(new ClimbNextStep("lift-2", fullPower, #, #, # )); Glossary, Q&A • More or less complicated java key-words… package import public private protected new static final void (…and concepts) precision type-cast array scope • Time for