Encoders and Rotor Position
dev_motor_controller_7250 supports 1 encoder channel with the following parameters:
Encoder / Estimator sampling frequency | 30kHz |
Available channels | 0 |
op_state warning bit |
Bit 4 - Encoder warning flag |
Encoder and Position Signal Path
JCS positional information is typically derived via acquiring a position from some position source, then passing this through an estimator to derive velocity and an estimated position. The position and velocity are then output as JCS signals, or depending on the application, used internally by the device system.
Encoder Common Configuration
The following parameters are common to all encoder types:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_type |
enum | Required | Selects the active encoder. |
encoder_X_position_zero |
command | Optional | Records the current encoder position as the zero offset. |
encoder_X_position_offset |
float32 | Optional | Sets or returns the rotor position offset. |
encoder_X_direction |
boolean | Required | Sets the encoder direction. |
encoder_X_theta_bypass |
boolean | Optional | Bypasses the encoder such that JCS input signal th_m is presented to the estimator. |
encoder_X_theta_bypass_direction |
boolean | Optional | Sets the direction of the bypassed input signal. |
encoder_X_linearisation_coeffs |
float32 array | Optional | An array of 64 coefficients for encoder linearisation. If any elements are non-zero, encoder linearisation is activated. |
Encoder selection
Select the active encoder by writing an enum to encoder_X_type. See device specific "Supported Encoders" for available options.
Encoder position zeroing and offset
An encoder may be zeroed by either writing a zero offset or commanding the system to sample the current encoder position as the zero offset.
To store the encoder current position as the zero offset, issue JCS command encoder_X_position_zero
. The encoder position will be sampled and stored in encoder_X_position_offset
. The sampled zero position may be read as a float32 from encoder_X_position_offset
.
To write an encoder offset, write a float32 to encoder_X_position_offset
. Any existing entry will be overwritten.
NOTE:
Devices (such as motor controllers) may have specific encoder zeroing requirements.
Encoder commissioning
Incremental encoders always start with their position reset to zero.
When commissioning an absolute encoder, it is useful to first zero the encoder at some mechanical position that defines the start of the travel. This position may be stored in the device configuration file as a zero offset, so that the zero position can be recovered at each startup. A typical process might be:
- Move the joint to the required mechanical zero position.
- Issue JCS command
encoder_X_position_zero
. - Retrieve the zero position with a float32 read to
encoder_X_position_offset
. - Store this zero position offset in the device YAML config, eg:
encoder_0_position_offset: 0.7565
Encoder direction
Set the encoder direction with a boolean write to encoder_X_direction
.
Encoder theta bypass
Encoder theta bypass allow the system to take a positional input from JCS signal th_m_X
rather than from an encoder.
An example use of this feature is driving a motor in open loop mode while recording e.g. th_m_encoder_X
for the encoder linearisation process.
Enable theta bypass with a boolean write to encoder_X_theta_bypass
.
The incoming direction of th_m_X
may be changed with a boolean write to encoder_X_theta_bypass_direction
.
Encoder theta bypass is not available on all devices.
Encoder linearisation
The encoder system supports linearisation of the encoder position by means of a lookup table for correction. The linearisation process takes the scaled encoder value and adds a small corrective bias term that is derived from the linear interpolation of the correction table.
Encoders may be linearised by writing an array of 64 linearisation coefficients to encoder_X_linearisation_coeffs
.
Encoder error handling
The encoder system is monitored for errors and registers errors as they occur. If an encoder error occurs, the previous timestep encoder value is used and the encoder manager will attempt to continue.
If a flood of errors occur such that the error rate goes above the limit set in encoder_X_error_rate_limit
, then an E-stop will be generated and the system will attempt to shut down.
To tune the encoder error rate monitor, write a float32 to encoder_X_error_rate_limit
:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_error_rate_limit |
float32 | Optional | Configures the encoder error rate limit. Valid values between [0.0f, 1.0f] Default: 0.05f Set to 1.0f to disable. |
Notes:
- Disabling the error rate monitor is recommended for encoder testing only.
- If the error rate monitor is disabled encoders will still attempt to return a valid position, regardless if the data is erroneous. This may be useful for debugging
- The encoder error rate is available as JCS signal
encoder_X_error_rate
even if the error rate monitor is disabled.
Supported Encoders
dev_motor_controller_7250 supports the following encoders:
Encoder type | Description |
---|---|
spi |
Differential or single ended generic absolute or incremental encoder |
jcs_as5047p |
JCS differential absolute encoder |
incremental |
Differential or single ended incremental encoder. Configurable counts per revolution |
jcs_network |
Rotor position over JCS network. (Limited performance due to network rates) |
Generic SPI Encoder Interface
JCS supports a generic SPI interface for communicating with encoders. The electrical interface supports either differential or single ended connections. See the device specific hardware documentation for connectors and pinouts.
Select the generic SPI encoder by writing enum value spi
to encoder_X_type
.
SPI configuration
The SPI peripheral must be configured for the appropriate encoder. Configure the device SPI peripheral with the following parameters:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_spi_frequency |
enum | Required | Sets the SPI peripheral frequency divisor. |
encoder_X_spi_phase |
enum | Required | Sets the SPI peripheral sampling phase. |
encoder_X_spi_polarity |
enum | Required | Sets the SPI peripheral sampling polarity. |
encoder_X_spi_transfer_size |
enum | Optional | Defines the number of bits per transfer. Default: 8 bits. |
encoder_X_spi_using_chip_select |
bool | Required | Sets the SPI peripheral to use the chip select line. Only effective if hardware supports it. |
SPI frequency
Select the SPI frequency by dividing down fpclk
and writing the value as an enum to encoder_X_spi_frequency
.
See device Hardware Specifications for fpclk
value.
encoder_X_spi_frequency value |
Description |
---|---|
frequency_fpclk_on_2 |
SPI frequency is fpclk / 2 |
frequency_fpclk_on_4 |
SPI frequency is fpclk / 4 |
frequency_fpclk_on_8 |
SPI frequency is fpclk / 8 |
frequency_fpclk_on_16 |
SPI frequency is fpclk / 16 |
frequency_fpclk_on_32 |
SPI frequency is fpclk / 32 |
frequency_fpclk_on_64 |
SPI frequency is fpclk / 64 |
frequency_fpclk_on_128 |
SPI frequency is fpclk / 128 |
frequency_fpclk_on_256 |
SPI frequency is fpclk / 256 |
SPI phase and polarity
Configure the SPI peripheral phase by writing an enum to encoder_X_spi_phase
:
encoder_X_spi_phase value |
Description |
---|---|
first_edge |
SPI data sampled on the first clock edge. |
second_edge |
SPI data sampled on the second clock edge. |
Configure the SPI peripheral polarity by writing an enum to encoder_X_spi_polarity
:
encoder_X_spi_polarity value |
Description |
---|---|
idle_low |
SPI SCLK is idle low. |
idle_high |
SPI SCLK is idle high. |
SPI transfer size
Configure the SPI peripheral sample size in bits by writing an enum to encoder_X_spi_transfer_size
.
encoder_X_spi_transfer_size value |
Description |
---|---|
num_bits_8 |
8 bits per SPI transfer. |
num_bits_7 |
7 bits per SPI transfer. |
num_bits_6 |
6 bits per SPI transfer. |
num_bits_5 |
5 bits per SPI transfer. |
num_bits_4 |
4 bits per SPI transfer. |
NOTE:
Regardless of the number of bits sent, one byte per transfer must be allocated in the spi command and masks in the sections below.
SPI pin support
The SPI interface supports chip select, SCK and MISO.
MOSI is not supported electrically on all devices, however the software interface remains the same.
On devices where the MOSI pin is not supported, the encoder selected must support returning data with with either the encoder's MOSI pin held in a known state, or MOSI not required. Examples are:
- AKSiM-2 which will return valid position data if it's MOSI pin is held low, equivalent to SPI command 0x00.
- AS5047p which will return valid position data if it's MOSI pin is held high, equivalent to SPI command 0xff.
Position recovery
Position recovery is performed by masking and shifting the position bits out of a received byte array. Operation is based on SPI transfers of 4 - 8 bits per transfer using efficient DMA.
The transfers are represented by an array of bytes defined by the user in encoder_X_spi_command
, with each byte containing one transfer (of 4 - 8 bits).
The number of bytes defined in the array defines the number of transfers the will occur per transaction.
For example, a encoder_X_spi_transfer_size
of 6 with two bytes defined in encoder_X_spi_command
will result in a total of 12 bits being exchanged.
Once the data is exchanged, any positional and status information needs to be extracted and manipulated for use. The encoder system requires that the returning position information be right aligned and a maximum of 32 bits wide.
The returned combined position and status information is masked via the byte array encoder_X_spi_position_mask
to separate out the position.
The resulting masked position is then shifted with encoder_X_spi_position_mask
(if required) to ensure it is right aligned.
Finally, the configured number of position bits encoder_X_spi_position_bits
is used to scale the resulting position to the range \([0, 2\pi]\).
The following parameters are used for position recovery:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_spi_command |
uint8 array | Required | Defines an array of transfers to complete the SPI data exchange. For devices that support MOSI, the command to send is defined in this array. |
encoder_X_spi_position_mask |
uint8 array | Required | An array of values to mask the position bits out of the returned data. |
encoder_X_spi_masked_position_shift |
uint8 | Optional | The number of bits to shift the masked position data by. The final position must be a right aligned 32 bit unsigned int. |
encoder_X_spi_position_bits |
uint8 | Required | The number bits that make up the position. |
Fault handling
An SPI encoder may return status information within the returned data. For example, AS5047p returns error status in bit 14.
The JCS generic SPI interface can mask out these bits of interest and generate an error if the assertion criteria is met.
The error flags are configured similarly to extracting the position information.
A mask encoder_X_spi_flag_mask
first masks out the bits representing errors, then a shift encoder_X_spi_flag_bits_shift
can be applied to move the resultant error flags into a 32 bits wide right aligned integer.
An error condition will be raised to the encoder error monitor if any bits are set within the resulting 32 bits wide integer.
Notes:
- Defining
encoder_spi_flag_mask
enables error checking. If it is not defined no error checking of the encoder will occur. - If
encoder_spi_flag_mask
is not defined thenencoder_spi_flag_bits_shift
has no effect.
The following parameters are used for fault handling:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_spi_flag_mask |
uint8 array | Optional | An array of values to mask the fault bits out of the returned data. |
encoder_X_masked_flag_shift |
uint8 | Optional | The number of bits to shift the masked fault bits by. The flags must fit into a 32 bit unsigned int. |
Example
AS5047p encoder has 14 bits of position and 2 status bits for a total of 16 bits. We will configure the SPI encoder for two 8 bit transfers. Our device does not support MOSI (the encoder has MOSI pin pulled high).
########################################################################
# SPI encoder
encoder_0_type: spi
# AS5047p: 14 bits of position, 2x status bits, Total of 16 bits = 2x bytes.
# Command frame:
# 15: Parity = 1
# 14: Read = 1
# 13:0: Address = 0x3fff - Measured angle with dynamic angle compensation
encoder_0_spi_position_bits: 14
encoder_0_spi_command: [0xff, 0xff]
# Read data frame:
# 15: Parity bit
# 14: Frame error bit - 1 = error occurred
# 13:0: Data - Measured angle with dynamic angle compensation
encoder_0_spi_position_mask: [0x3f, 0xff]
# We don't need to shift - our position is already right aligned
encoder_0_spi_position_bits_shift: 0
# Bit 14 of the returned data represents an error. We will watch this bit
# and flag an error if it is set
encoder_0_spi_flag_mask: [0x40, 0x00]
# The resulting flag will be within a uint32, so no need to shift right
# SPI config
# Capture on second clock transition, falling edge
encoder_0_spi_phase: second_edge
encoder_0_spi_polarity: idle_low
# about 3mhz
encoder_0_spi_frequency: frequency_fpclk_on_16
Note in this example:
- Since we are not using MOSI, the values contained in the
encoder_0_spi_command
have no effect. However, the spi command byte array must still be defined as this determines the number of SPI transfers that occur. - The values in
encoder_0_spi_position_mask
mask out the lower 14 bits of position information. - We do not need to shift the masked position - it is already right aligned.
- We do not need to shift the masked flags - they already fall within a uint32.
IMPORTANT:
JCS Generic SPI encoder interface does not currently support computing parity bits or CRC for error checking.
JCS AS5047p Encoder
Select the JCS AS5047p based encoder by writing enum value jcs_as5047p
to encoder_X_type
.
JCS AS5047p encoder has no specific configuration requirements.
The following debug parameters are available:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_as5047p_last_rx |
uint16 | Optional | Raw SPI frame returned by the AS5047p encoder. (Read only) |
Incremental Encoder
The incremental encoder interface supports either differential or single ended connections. See the device electrical specifications for pinouts.
Select an incremental encoder by writing enum value incremental
to encoder_X_type
.
Configure the incremental encoder with the following parameters:
Command | Type | Required | Description |
---|---|---|---|
encoder_X_incremental_cpr |
uint32 | Required | The number of encoder counts per mechanical revolution. |
Supported Position and Velocity Estimators
dev_motor_controller_7250 supports the following estimators:
Estimator type | Description |
---|---|
tracking_loop |
Tracking loop based estimator |
kf |
Two state Kalman filter based estimator |
Tracking Loop Estimator
A rotational tracking loop estimator that produces estimates of rotational position and velocity.
Select the tracking loop estimator by writing enum value tracking_loop
to estimator_X_type
.
The tracking loop estimator has good performance and is recommended as a starting point for many applications.
Configure the estimator with the following parameters:
Command | Type | Required | Description |
---|---|---|---|
estimator_X_tracking_loop_kp |
float32 | Optional | Tracking loop proportional gain term. |
estimator_X_tracking_loop_ki |
float32 | Optional | Tracking loop integral gain term. |
estimator_X_tracking_loop_bandwidth_hz |
float32 | Optional | Sets the tracking loop bandwidth (Hz). Kp and Ki will be computed and populated by setting this parameters. |
Compute gains from bandwidth
The tracking loop gains \(K_p\) and \(K_i\) may be computed from a desired bandwidth by setting parameter estimator_X_tracking_loop_bandwidth_hz
.
The gains are computed as:
\(BW_{rad/S} = 2\pi BW_{Hz}\)
\(K_p = 2 BW_{rad/S}\)
\(K_i = {1\over4} {K_p}^2\)
Rotor Position Alignment
Rotor and Encoder Relationship
Before dev_motor_controller can startup and run, the relationship between the encoder's zero position and the motor's D axis must be established.
Two methods of encoder/rotor alignment are supported by dev_motor_controller:
- Ramping the D axis current and zeroing the position via a call to
encoder_X_position_zero
once the rotor has locked to the D axis. Typically used with incremental encoders, or first time commissioning of absolute encoders. - Writing the encoder position to the D-Axis zero position offset to
encoder_X_position_offset
. Typically used with absolute encoders.
The rotor is considered aligned when either a write to encoder_X_position_zero
is performed to zero the encoder or an encoder offset is written to encoder_X_position_offset
.
Controllers that rely on rotor position to operate will not enter a run state until the rotor is aligned.
(1) D-Axis alignment and encoder zero procedure
This procedure ramps the D axis current such that the rotor 'locks' to the D axis. When the rotor has settled the encoder is zeroed and and the offset between the encoder zero position (arbitrary at startup for incremental encoders) and the motor D axis is stored.
The typical process is:
- Configure the current controller gains and limits.
- Start the motor controller with command
start
. - Snooze for 100ms to let dev_motor_controller run calibration routines.
- Configure
controller_mode
withtest_align
to select the alignment controller. - Enter into alignment mode by starting the controller with
controller_start
. - Poll
align_in_dwell
to wait until the controller is in the dwell period. - Snooze for a bit to let the rotor settle.
- Zero the encoder with command
encoder_X_position_zero
. - Exit controller alignment mode with command
controller_stop
.
NOTE:
-
This procedure requires that the rotor is able to turn. If a load is connected to the motor output, the alignment current must be set such that it can cause the motor to turn.
-
The alignment current must be large enough to over come any motor friction, cogging torque etc. The result of a motor that is not aligned correctly may be an uncontrollable motor.
INFO:
For motors with high cogging torque, increasing the ramp time and alignment current can help with alignment. For challenging motors such as this it is recommended to use an absolute encoder and perform the alignment process once so that correct motor operation can be validated in a controlled manner, rather than aligning an incremental encoder every startup.
The following commands are available to configure the alignment controller and monitor the progress of the rotor D axis to an encoder:
Command | Type | Required | Description |
---|---|---|---|
is_aligned |
boolean | Optional | Returns true if the rotor is aligned. |
align_in_dwell |
boolean | Optional | Returns true if the alignment process is in the dwell section. |
i_d_alignment |
float32 | Optional | Sets the alignment current. This is motor dependent. |
i_d_alignment_ramp_time_ms |
float32 | Optional | Sets the alignment ramp up time. Default value: 2 seconds. |
encoder_X_position_zero |
command | Optional | Records the encoder position as the zero offset. |
encoder_X_position_offset |
float32 | Optional | Sets or returns the rotor position offset. |
controller_mode |
enum | Required | Set the controller mode to test_align to choose the alignment controller. |
controller_start |
command/ boolean |
Required | Write as a command to start the controller and start the alignment process. Read as a boolean to poll the controller status. Returns: true if the controller is running. false if the controller is in any other state. |
controller_stop |
command | Optional | Stops the controller and aborts the alignment process. |
(2) Writing an encoder zero offset
An absolute encoder can recover it's own position at power up and in turn the position of the rotor, assuming the rotor / encoder position relationship has not changed.
If this relationship is known then it can be written to dev_motor_controller via command encoder_X_position_offset
, which is typically written as a configuration value in each dev_motor_controller YAML configuration file.
Once it is written to the device then dev_motor_controller is considered aligned.
Initial absolute encoder offset recovery
When first commissioning an encoder and motor it is required to find the offset that will be written to each dev_motor_controller at startup.
This process is simple and uses the same D axis alignment and encoder zero procedure outlined above, except the newly written offset is fetched at the end of the procedure by reading command encoder_X_position_offset
.
The offset is then stored in the dev_motor_controller.yaml configuration.
WARNING:
Any time the positional relationship between the motor and the encoder changes the offset becomes invalid and the offset recovery procedure must be ran again.
Failure to do so can result in erratic motor behaviour.
Checking for a working encoder
Before starting motor control it is recommended to ensure the encoder is working first.
Configure a basic JCS system.
Configure the motor controller and encoder for the expected operation.
Configure dev_HOST.yaml such that it does not start any motor controllers (e.g. remove/comment references to controller_start
for the motor controllers).
Start the JCS system and examine the signal th_m
.
Rotate the motor by hand. th_m
should complete one cycle for one mechanical revolution.
Troubleshooting encoders
-
SPI encoders
- At high data rates the propagation delay of the drivers and receivers can effect the transmission.
- 3MHz - 6MHz is a practical upper limit to SPI data transfer rates.
- At high data rates the propagation delay of the drivers and receivers can effect the transmission.
-
The encoder works but continually shows warnings
- From experience:
- Differential encoder had one connector with DI+ and DI- pins swapped.
- From experience: