Skip to content

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 signal path

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_error_timeout_ms float32 Optional Encoder sentry filter timeout.
Default: 2mS.
Set to zero do disable the sentry.

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.


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.

Encoder linearisation

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.


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


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.


  • 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 then encoder_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.


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.


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.

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:

  1. 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.
  2. 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 with test_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.


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


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


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.
  • The encoder works but continually shows warnings

    • From experience:
      • Differential encoder had one connector with DI+ and DI- pins swapped.