Lesson 1.0 - Angelbots1339/ProgramingLessons GitHub Wiki
In this lesson you will learn the basics of a single motor system. How to initialize and use a Talon FX motor controller, and then run it in a Simulation to a sim Joystick controller. All the code will be done in the Drive
subsystem class located at src\main\java\frc\robot\subsystems\Drive.java
.
Subsystems allow us to split the robot into smaller sections, which each contain different motors and sensors. Like a drive base, a game object intake, a endgame climb device, etc. What's important is a subsystem should always only have to do one task with its motors. For example, a single subsystem should not contain both the intake and the shooter of a robot, as they will be run independently of each other.
To control motors using code, we utilize premade classes created by vendors to communicate with the motor. These deal with the low level CAN bus communication allowing us to focus on high level control commands (like moving the motor at a certain speed). For the Talon FX we will be using the a wrapper class from CTRE's TalonFX API.
The TalonFXcontroller intakes two parameters in its constructor: the int deviceId
(a unique id set in phoenix tuner to identify each motor on the CAN bus), and string canbus
(the string identifier for the CAN bus that the motor is connected to usually ether the roboRIO or a CANivorewhich is given a name in phoenix tuner). Excluding the seconded parameter uses the default CAN bus aka the roboRIO.
new TalonFX(0, "")
Create and initialize a TalonFX object with the device id 6 on the roboRIO's CAN bus
and return the motor in the getTalonFX1ForTest()
method
@Override
public TalonFX getTalonFXForTest() {
return ADD MOTOR HERE;
}
How to run tests Expected result
!
Solution
public TalonFX motor1 = new TalonFX(6);
@Override
public TalonFX getTalonFXForTest() {
// TODO Auto-generated method stub
return motor1;
}
After initialization comes the configuration. This generally still happens before the robot starts moving, and is done through a bunch of CAN bus commands to configure various CAN devices. With motor controllers, configuration is a very important aspect of using them correctly, and you can set values for things like current limits and which direction is forward on the motor.
CTRE devices objects have a getConfigurator()
method that returns a device-specific Configurator
object. The Configurator
is used to retrieve, apply, and factory default the configs of a device. Configs take the form of a device-specific Configuration
class that group configuration data of devices in a meaningful way. These classes are Passive Data Structures. In our case the TalonFXConfiguration
, has subgroups of configs such as MotorOutputConfigs
, or CurrentLimitsConfigs
.
Configs can be applied to a device by calling apply()
on the Configurator
with a Configuration
object.
TalonFXConfigurator talonFXConfigurator = motor1.getConfigurator();
TalonFXConfiguration talonFxConfigs = new TalonFXConfiguration();
talonFxConfigs.SoftwareLimitSwitch.ForwardSoftLimitEnable = true; // Changing Config
talonFXConfigurator.apply(motorConfigs);
TalonFXConfiguration.MotorOutut.Inverted
which sets the desired direction of rotation when given on a positive control value, or basically which way is forward (we will go in to the form of this value more in Part 1.3: Control).
TalonFXConfiguration.MotorOutput.NeutralMode
defines the behavior of a motor when it is not being controlled by the program, and its velocity is set to 0. There are two neutral modes: NeutralModeValue.Brake
which will make the motor hard to turn, or NeutralModeValue.Coast
which allows free movement.
TalonFXConfiguration.CurrentLimits
contains several things to change. Particularly we will edit the SupplyCurrentLimitEnable
, SupplyCurrentLimit
, SupplyCurrentThreshold
, and SupplyTimeThreshold
parameters. These allow us to restrict the amount of current used by the motors to protect them from burning out. When the SupplyCurrentLimitEnable
is set to true and the motor current is above the SupplyCurrentThreshold
, it starts a timer if it stays above the limit for the length of SupplyTimeThreshold
, then it limits the current to the SupplyCurrentLimit
. For those interested in the other current limit Supply vs Stator
Config the motor to turn clockwise when given a positive input, to restrict movement when its not given an input, and to have a supply current limit of 25 amps, a threshold of 40, and a time threshold of 0.1.
Tip
- Change config params before applying them
- Place your code in the constructor of the subsystem
How to run tests Expected result
!
Solution
public Drive() {
TalonFXConfiguration talonFXConfiguration = new TalonFXConfiguration()
TalonFXConfigurator talonFXConfigurator = motor1.getConfigurator();
talonFXConfiguration.MotorOutput.Inverted = InvertedValue.Clockwise_Positive;
talonFXConfiguration.MotorOutput.NeutralMode = NeutralModeValue.Brake;
talonFXConfiguration.CurrentLimits.SupplyCurrentLimitEnable = true
talonFXConfiguration.CurrentLimits.SupplyCurrentLimit = 25;
talonFXConfiguration.CurrentLimits.SupplyCurrentThreshold = 40;
talonFXConfiguration.CurrentLimits.SupplyTimeThreshold = 0.1;
talonFXConfigurator.apply(talonFXConfiguration);
}
After the motor has been initialized and configured, it is ready to go. You can control a motor in a few different ways. Duty Cycle control (In phoenix 5 was percentage-based control) runs the motor at a percentage of its full power (-1 to 1), so setting it to 1 will send it at full speed forward. Voltage-Based control allows you to give the motor a specific voltage (generally -12 to 12 volts). Both of these methods are limited by the voltage of the battery. A value of 1 with Duty Cycle control will output the maximum amount of voltage the battery is supplying, and Voltage-Based control will try to get as close to the target voltage as possible. There is also FOC based control, but is only for Phoenix pro and so you'll probably never use it.
Apart from the base level control stated above control methods are separated in to two groups. Open loop where it is a blind method of control, where you don't know the output, and are just spinning the motor at a given speed. Closed loop control uses goals for the motor (like a speed or a position), and then the motor adjusts its output accordingly based on the input. This is often calculated with PID. For now we will just focus on open loop control.
Controlling a motor requires a ControlRequest
class. The open loop ControlRequest
classes include DutyCycleOut
and VoltageOut
. To send a ControlRequest
to the motor, you use the setControl
method.
// Command motor1 to 100% of duty cycle
motor1.setControl(new DutyCycleOut(1.0));
In a case like this where the lessons are preformed without physical motors, simulations can be used instead. In this case, the following code can be added to utilize simulations. Copy it after the previous lesson's code in the constructor of the drive system. Replace motor1 with the name of your motor.
public Drive() {
//code from previus lesson
TalonFXSimState motor1SimState = motor1.getSimState(); // Initialize sim motor
motor1SimState.setSupplyVoltage(12); // Configure sim motor
}
//Simulates the physics of a motor in the real world with a moumnet of inertia of 0.002 kg/m^2 on the shaft which is basicly free spin
public DCMotorSim dcMotor = new DCMotorSim(DCMotor.getFalcon500(1), 1, 0.002);
@Override
public void periodic(){
dcMotor.setInputVoltage(motor1SimState.getMotorVoltage())
dcMotor.update(0.02)
motor1SimState.setRawRotorPosition(dcMotor.getAngularPositionRotations());
motor1SimState.setRotorVelocity(dcMotor.getAngularVelocityRPM() / 60);
}
The method runMotor(double percentOutput)
is supposed to move the motor based on the input percentage. It will be fed a value from axis 0 on the joystick connected to port 0. The data will be in a range from -1 to 0. This should be the a and d key if you followed the How to run simulation guide.
Complete the runMotor method. Make sure the possible output range of the motor is -6 to 6 volts, and check to make sure the motor responds correctly by graphing the output voltage of the motor in simulation.
How to Graph In Simulation Should look like this: !
A: Pressing d key down B: Pressing no key C: pressing a key down
increase the moment of inertia to 0.2 to simulate a higher load on the motor. Try to think: why does the motor's voltage spike then settle out?
Answer
It is the current limiting at work set in part 1.2Tip
- Remember the units and bounds for the output you get from
runMotor(double percentOutput)
and the input you need forVoltageOut
- Make sure to place the code in here:
runMotor(double percentOutput){
//HERE
}
How to run tests Expected result
!
Solution
@Override
public void runMotor(double percentOutput) {
motor1.setControl(new VoltageOut(percentOutput * 6));
}
The final way to interact with motors is to receive data from its internal sensors and feedback systems. This information is sent in status signals from the motor through the CAN bus on regular intervals (this interval can be individually set for specific signals). There are a few signals we will take a look at.
getPosition
: This is the total number of rotations the motor has gone since it was initialized or reset. It is recorded from a internal encoder in the falcon.
getVelocity
: This is calculated from change in position over time and is given in Rotations per second. (Specifically they use a rolling average and the parameters for calculating this can be set in the config).
getFault_SupplyCurrLimit
: Signals also include faults. A normal fault is a fault that is currently happening, not to be confused with sticky faults that stay after they occur. This includes issues such as over-voltage, over heating, and unstable voltage. In this case getFault_SupplyCurrLimit
tells you if the motor is currently limiting current.
The StatusSignal
Java object provides APIs to read the signals explained above.
The device object provides getters for all available signals. Each getter returns a StatusSignal
that is typed appropriately for the signal. (aka for TalonFX getPosition
, getFault_SupplyCurrLimit
)
StatusSignal<Double> supplyVoltageSignal = m_device.getSupplyVoltage();
The value of the signal can be retrieved from the StatusSignal
by calling getValue()
.
double supplyVoltage = supplyVoltageSignal.getValue();
Network tables allows for customizable communication between the driver station (your computer) and the robot. They can be used for anything from reading sensor values mid-match to choosing your auto at the start. They also are very useful in debugging as you can use it as a better version of a print statement. In this case, we will be using them to display the signal we get from the motor. While WPI has a direct api for network tables, it is quite verbose, so for quick jobs and testing we use Smartdashboard which is simpler and shorter.
// publishes the value 2 the the key "Test" in the sartdashboard table
SmartDashboard.putNumber("Test", 2);
The perodic()
function inside a subsystem runs every loop time, and is perfect for continually publishing the numbers to network tables.
Record and continually publish the following values to the smart dashboard network table under the given keys:
- Velocity of the simulated motor in RPS. Key: "Velocity"
- Total rotations of the simulated motor. Key: "Position"
- If the robot has tripped the supply current limit in its lifetime. Key: "CurrentLimit"
Tip
- Publish the values continually in the
perodic()
function - Be careful with types
!
Solution
```java StatusSignal velocity = motor1.getVelocity(); StatusSignal position = motor1.getPosition(); StatusSignal currentLimit = motor1.getFault_SupplyCurrLimit(); @Override public void periodic() { velocity.refresh(); position.refresh(); currentLimit.refresh(); SmartDashboard.putNumber("Velocity", velocity.getValue());
SmartDashboard.putNumber("Position", position.getValue());SmartDashboard.putBoolean("Current Limit", currentLimit.getValue());
//code from previous lessons
}
</details>
Go on to [[Lesson-2.0]].