Arcade Drive

Now that we know how to create commands, let’s do something a little more useful. In this chapter we are going to create a command which will allow us to control the drive motors using a joystick.

The first step is to create the new class called ArcadeDriveCommand under the commands folder. Like we did with the DriveSubsystem, copy the ExampleCommand and paste it into the commands folder. Then change the name of the copy to and in that file search and replace all instances of ExampleCommand with ArcadeDriveCommand. Also, since this command will be using the DriveSubsystem, replace all instances of ExampleSubsystem with DriveSubsystem. After you have done that you file should look like:

Now since we are going to control the robot with the joystick, we are going to need access to the Joystick class. We have already created an instance in the RobotContainer and we do not want to create a second instance hear so we will pass in the Joystick instance via the constructor:

Note that as we are doing with the DriveSubsystem parameter we have created a member variable m_joystick an initialized it in the constructor with the joystick argument so that we will have access to the Joystick class for the life of this object.

Now in the execute() function we need to read the joystick and set the power on the motors. Let’s start by just reading the value from the joystick, and make the robot drive forward and backwards using this value. The function to get the position of the joystick is getY() and we note that it returns a number from -1.0 to +1.0. Since the motors take their power setting in the same range, we can simply pass this on to the setPower(…) function of our DriveSubsystem class:

Your file should now look like this:

Now we don’t want to connect this command to a button like we did with the DriveForTimeCommand. What we want is for this command to always run whenever no other command that requires the DriveSubsystem is running. To do this we add a setDefaultCommand(…) call to the constructor of our RobotContainer class:

Here we are creating an instance of our ArcadeDriveCommand class and setting it as the default command for the DriveSubsystem. With this set, the ArcadeDriveCommand will run whenever no other command that requires the DriveSubsystem is running. For example, if we were to now press the B1 button the ArcadeDriveCommand would be interrupted and the DriveForTimeCommand would execute. Once the DriveForTimeCommand ends, the ArcadeDriveCommand would be restarted and the robot would once again be under joystick control.

Your file should now look like:

Now run your program and you should be able to use the joystick to move the robot forward and backwards.

Of course, we are going to want to be able to turn our robot. How are we going to do that? To make the robot turn right, we want to run the left motor forward and the right motor backwards. To turn left we need to do the opposite. If we are going to use the X value from the joystick, we might accomplish this by changing the execute() function of our ArcadeDriveCommand class as follows:

Now this will enable the robot to turn, but it will no longer drive forward and backward. See if you can figure out how to change the execute() function to accomplish both driving forward and backward and turning. When you have your solution, compare it to the one below:

Don’t scroll down until you have tried to find your own solution!




You might find that controlling the robot, especially at low speeds is a bit tricky. There is a simple way that we can improve it. Right now if we were to graph the power we apply to the motors vs the X or Y of the joystick, we would just see a straight line. What we would like to do is curve the relationship so that we have more control over the lower speeds. We can do this by cubing the and Y values. Compare the linear (red) and cubic (blue) curves:

Notice that in both cases, we can still get full power out of the robot by pushing the joystick full over in one direction or another (this is because 1.0 * 1.0 * 1.0 is still equal to 1.0). However for joystick positions less than 1.0, we see that the power output increases much more slowly than the linear case, allowing for better control at the lower speeds. We can implement this change in our execute() function as follows:

Try it out and see if you don’t have better control over the robot.

Note that we could also just square the inputs which would result in a less drastic flattening of the curve. However if we wanted to use the square, we would need to take care to make sure that the sign was correct when X or Y becomes negative (remember that -1.0 * -1.0 is equal to +1.0). As an exercise, why don’t you see if you can come up with a solution that squares the inputs but still preserves the sign. You can then compare your solution to the one below:





As a final note, there is another way this may be accomplished by using the Math.abs function which returns the absolute value of a number.

By taking the absolute value and multiplying it by itself, we square the value but retain the sign. While this involves less typing it may or may be less intuitive.

Finally you can also do this using the Math.signum function which returns +1 if the number is positive and -1 if the number is negative.

Next: Wheel Encoders