TalonFX Remote Sensors#

The TalonFX supports various remote sensors. Remote sensors allow onboard closed-loop functionality at rates faster than a traditional robot processor (~1Khz) by reading the remote sensor directly from the CAN bus. This allows supported motor controllers to execute closed-loop modes with sensor values sourced by supported sensors.

A list of supported remote sensors can be found in the API docs (Java, C++, Python).

Remote sensors can be configured using Tuner X or via code. This document highlights how to configure a remote sensor in a robot program.

RemoteCANcoder#

A supported motor controller will update its position and velocity whenever the CANcoder publishes its information on the CAN bus.

var fx_cfg = new TalonFXConfiguration();
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cancoder.getDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = FeedbackSensorSourceValue.RemoteCANcoder;

m_talonFX.getConfigurator().apply(fx_cfg);
configs::TalonFXConfiguration fx_cfg{};
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cancoder.GetDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = signals::FeedbackSensorSourceValue::RemoteCANcoder;

m_talonFX.GetConfigurator().Apply(fx_cfg);
fx_cfg = configs.TalonFXConfiguration()
fx_cfg.feedback.feedback_remote_sensor_id = self.cancoder.device_id
fx_cfg.feedback.feedback_sensor_source = signals.FeedbackSensorSourceValue.REMOTE_CANCODER

self.talonfx.configurator.apply(fx_cfg)

FusedCANcoder#

Important

This feature requires the device to be Pro licensed. When unlicensed, the TalonFX will fall back to RemoteCANcoder and trip the UsingFusedCANcoderWhileUnlicensed fault.

New in Phoenix 6 is a feedback sensor type called FusedCANcoder. FusedCANcoder will fuse another CANcoder’s information with the motor’s internal rotor, which provides the best possible position and velocity for accuracy and bandwidth. This is useful in applications such as swerve azimuth.

FusedCANcoder requires the configuration of several Feedback config group items, shown below.

Full example: Java, C++

51    /* Configure CANcoder to zero the magnet appropriately */
52    CANcoderConfiguration cc_cfg = new CANcoderConfiguration();
53    cc_cfg.MagnetSensor.AbsoluteSensorRange = AbsoluteSensorRangeValue.Signed_PlusMinusHalf;
54    cc_cfg.MagnetSensor.SensorDirection = SensorDirectionValue.CounterClockwise_Positive;
55    cc_cfg.MagnetSensor.MagnetOffset = 0.4;
56    m_cc.getConfigurator().apply(cc_cfg);
57
58    TalonFXConfiguration fx_cfg = new TalonFXConfiguration();
59    fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.getDeviceID();
60    fx_cfg.Feedback.FeedbackSensorSource = FeedbackSensorSourceValue.FusedCANcoder;
61    fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
62    fx_cfg.Feedback.RotorToSensorRatio = 12.8;
63
64    m_fx.getConfigurator().apply(fx_cfg);
11  /* Configure CANcoder to zero the magnet appropriately */
12  configs::CANcoderConfiguration cc_cfg{};
13  cc_cfg.MagnetSensor.AbsoluteSensorRange = signals::AbsoluteSensorRangeValue::Signed_PlusMinusHalf;
14  cc_cfg.MagnetSensor.SensorDirection = signals::SensorDirectionValue::CounterClockwise_Positive;
15  cc_cfg.MagnetSensor.MagnetOffset = 0.4;
16  m_cc.GetConfigurator().Apply(cc_cfg);
17
18  configs::TalonFXConfiguration fx_cfg{};
19  fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.GetDeviceID();
20  fx_cfg.Feedback.FeedbackSensorSource = signals::FeedbackSensorSourceValue::FusedCANcoder;
21  fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
22  fx_cfg.Feedback.RotorToSensorRatio = 12.8;
23
24  m_fx.GetConfigurator().Apply(fx_cfg);
cc_cfg = configs.CANcoderConfiguration()
cc_cfg.magnet_sensor.absolute_sensor_range = signals.AbsoluteSensorRangeValue.SIGNED_PLUS_MINUS_HALF
cc_cfg.magnet_sensor.sensor_direction = signals.SensorDirectionValue.COUNTER_CLOCKWISE_POSITIVE
cc_cfg.magnet_sensor.magnet_offset = 0.4
self.cc.configurator.apply(cc_cfg)

fx_cfg = configs.TalonFXConfiguration()
fx_cfg.feedback.feedback_remote_sensor_id = self.cc.device_id
fx_cfg.feedback.feedback_sensor_source = signals.FeedbackSensorSourceValue.FUSED_CANCODER
fx_cfg.feedback.sensor_to_mechanism_ratio = 1.0
fx_cfg.feedback.rotor_to_sensor_ratio = 12.8

self.fx.configurator.apply(fx_cfg)

Usage is the same as any status signal:

fx_pos.refresh();
cc_pos.refresh();

System.out.println("FX Position: " + fx_pos.toString());
System.out.println("CANcoder Position: " + cc_pos.toString());
fx_pos.Refresh();
cc_pos.Refresh();

std::cout << "FX Position: " << fx_pos << std::endl;
std::cout << "CANcoder Position: " << cc_pos << std::endl;
fx_pos.refresh()
cc_pos.refresh()

print("FX Position: " + fx_pos.value)
print("CANcoder Position: " + cc_pos.value)

SyncCANcoder#

Important

This feature requires the device to be Pro licensed. When unlicensed, the TalonFX will fall back to RemoteCANcoder and trip the UsingFusedCANcoderWhileUnlicensed fault.

SyncCANcoder allows users to synchronize the TalonFX’s internal rotor sensor against the remote CANcoder, but continue to use the rotor sensor for all closed loop control. TalonFX will continue to monitor the remote CANcoder and report if its internal position differs significantly from the reported position or if the remote CANcoder disappears from the bus.

Users may want SyncCANcoder instead of FusedCANcoder if there is risk that the sensor can fail in a way that the sensor is still reporting “good” data, but the data does not match the mechanism, such as if the entire sensor mount assembly breaks off. Using SyncCANcoder over FusedCANcoder will not benefit from backlash compensation, as the CANcoder position is not continually fused in.

SyncCANcoder requires the configuration of several Feedback config group items, shown below.

/* Configure CANcoder to zero the magnet appropriately */
CANcoderConfiguration cc_cfg = new CANcoderConfiguration();
cc_cfg.MagnetSensor.AbsoluteSensorRange = AbsoluteSensorRangeValue.Signed_PlusMinusHalf;
cc_cfg.MagnetSensor.SensorDirection = SensorDirectionValue.CounterClockwise_Positive;
cc_cfg.MagnetSensor.MagnetOffset = 0.4;
m_cc.getConfigurator().apply(cc_cfg);

TalonFXConfiguration fx_cfg = new TalonFXConfiguration();
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.getDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = FeedbackSensorSourceValue.SyncCANcoder;
fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
fx_cfg.Feedback.RotorToSensorRatio = 12.8;

m_fx.getConfigurator().apply(fx_cfg);
/* Configure CANcoder to zero the magnet appropriately */
configs::CANcoderConfiguration cc_cfg{};
cc_cfg.MagnetSensor.AbsoluteSensorRange = signals::AbsoluteSensorRangeValue::Signed_PlusMinusHalf;
cc_cfg.MagnetSensor.SensorDirection = signals::SensorDirectionValue::CounterClockwise_Positive;
cc_cfg.MagnetSensor.MagnetOffset = 0.4;
m_cc.GetConfigurator().Apply(cc_cfg);

configs::TalonFXConfiguration fx_cfg{};
fx_cfg.Feedback.FeedbackRemoteSensorID = m_cc.GetDeviceID();
fx_cfg.Feedback.FeedbackSensorSource = signals::FeedbackSensorSourceValue::SyncCANcoder;
fx_cfg.Feedback.SensorToMechanismRatio = 1.0;
fx_cfg.Feedback.RotorToSensorRatio = 12.8;

m_fx.GetConfigurator().Apply(fx_cfg);
cc_cfg = configs.CANcoderConfiguration()
cc_cfg.magnet_sensor.absolute_sensor_range = signals.AbsoluteSensorRangeValue.SIGNED_PLUS_MINUS_HALF
cc_cfg.magnet_sensor.sensor_direction = signals.SensorDirectionValue.COUNTER_CLOCKWISE_POSITIVE
cc_cfg.magnet_sensor.magnet_offset = 0.4
self.cc.configurator.apply(cc_cfg)

fx_cfg = configs.TalonFXConfiguration()
fx_cfg.feedback.feedback_remote_sensor_id = self.cc.device_id
fx_cfg.feedback.feedback_sensor_source = signals.FeedbackSensorSourceValue.SYNC_CANCODER
fx_cfg.feedback.sensor_to_mechanism_ratio = 1.0
fx_cfg.feedback.rotor_to_sensor_ratio = 12.8

self.fx.configurator.apply(fx_cfg)