Lesson 1.0 - Angelbots1339/ProgramingLessons GitHub Wiki

Overview


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

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.

Part 1.1: Initialization


Info

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, "")

Task

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;
  }

Test

How to run tests Expected result

!

Solution
public TalonFX motor1 = new TalonFX(6);    
@Override
public TalonFX getTalonFXForTest() {
  // TODO Auto-generated method stub
  return motor1;
}

Part 1.2: Configuration


Info

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.

Configs/Configurators

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.

Applying Configs

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);
We will set the following configs:

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

Task

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

Test

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);
}

Part 1.3: Control


Info

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.

Open vs Closed loop

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.

Applying a control request

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));
Simulation

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);
  }

How to run simulation

Task

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

Bonus task

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.2

Tip

  • Remember the units and bounds for the output you get from runMotor(double percentOutput) and the input you need for VoltageOut
  • Make sure to place the code in here:
runMotor(double percentOutput){
  //HERE
}

Test

How to run tests Expected result

!

Solution
@Override
public void runMotor(double percentOutput) {
  motor1.setControl(new VoltageOut(percentOutput * 6));    
}

Part 1.4 Signals


Info

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.

StatusSignal

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

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.

Adding a value
// publishes the value 2 the the key "Test" in the sartdashboard table
 SmartDashboard.putNumber("Test", 2);
Periodic

The perodic() function inside a subsystem runs every loop time, and is perfect for continually publishing the numbers to network tables.

Task

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

Test

How to run tests

!

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]].
⚠️ **GitHub.com Fallback** ⚠️