Encoders are used to determine the rotor position and speed, and are the typical method of feedback to the control system in a motor drive. This document explains how to use the AMDC's encoder interface to extract high quality rotor position and speed data.
For more information:
- on how encoders work and are interfaced with the AMDC, see the encoder hardware subsystem page;
- on the driver functionality included with the AMDC firmware, see the encoder driver architecture page.
The AMDC supports incremental encoders with quadrature ABZ outputs and a fixed number of counts per revolution CPR (for example, CPR = 1024). The user needs to provide code that interfaces to the AMDC's drivers to read the encoder count and convert it into usable angular information that is suitable for use within the control code.
:alt: Motor Cross-Section with Encoder Angles
:width: 350px
:align: right
This document assumes the configuration shown to the right, where the control code expects a measurement of the angle of the rotor's north pole relative to the phase
Upon powerup, the AMDC configures the encoder to a default number of counts per revolution. This is handled in encoder.c as part of the standard firmware package. When using an encoder that has a different number counts per revolution, the user must inform the driver by calling encoder_set_counts_per_rev().
Example code for a 10 bit encoder:
#define USER_ENCODER_COUNTS_PER_REV_BITS (10)
#define USER_ENCODER_COUNTS_PER_REV (1 << USER_ENCODER_COUNTS_PER_REV_BITS)
int task_user_app_init(void)
{
encoder_set_counts_per_rev(USER_ENCODER_COUNTS_PER_REV);
// other user app one-time initialization code
// ...The AMDC provides a convenience function that can be used as an alternate to `encoder_set_counts_per_rev()` when the encoder is specified as a number of bits: `encoder_set_counts_per_rev_bits(USER_ENCODER_COUNTS_PER_REV_BITS).`
The recommended approach to reading the shaft position from the encoder is illustrated in the figure below:
:alt: Encoder Code Block Diagram.svg
:width: 700px
:align: center
First, the AMDC drv/encoder driver module function encoder_get_position() is used to obtain the the encoder's count
The [`drv/encoder`](/firmware/arch/drivers/encoder.md) driver module also has a function called `encoder_get_steps()` which returns the encoder's count since power-on. One rotation direction increments, the other decrements. This value does not wrap around (it ignores `encoder_set_counts_per_rev()` and the z-pulse). Users are advised to use `encoder_get_position()`, which does wrap around and tracks the z-pulse.
Next, the user should calculate
$$ \theta_{\rm m} = \tfrac{2\pi}{\rm COUNTS_PER_REV} \left( \theta_{\rm enc} - \theta_{\rm off} \right) $$ (eq:convCCW)
In this case, a counter-clockwise rotation of the rotor causes the
$$ \theta_{\rm m} &= \tfrac{2\pi}{\rm COUNTS_PER_REV} \left({\scriptstyle \rm COUNTS_PER_REV} - \theta_{\rm enc} + \theta_{\rm off} \right) \ &= 2\pi - \theta_{\rm m, CCW} $$ (eq:convCW)
The user can experimentally determine whether the encoder count increases with counter-clockwise rotation of the shaft by rotating the shaft and using [logging](/getting-started/user-guide/logging/index.md) to observe the trend of $\theta_{\rm enc}$.
Finally, the user must ensure that angle is within the bounds of mod function. This is shown in the final block in the diagram.
Here is example code to convert the encoder to angular position in radians (note that this assumes the encoder offset
double task_get_theta_m(void)
{
// User to set encoder offset
double theta_off = 100;
// User to set encoder count per revolution
double ENCODER_COUNT_PER_REV = 1024;
// User to set 1 if encoder count increases with CCW rotation of shaft, set 0 if encoder count increases with CW rotation of shaft
int CCW_ROTATION_FLAG = 1;
// Angular position to be computed
double theta_m;
// Get raw encoder position
uint32_t theta_enc;
encoder_get_position(&theta_enc);
// Convert to radians
theta_m = (double) PI2 * ( ((double)theta_enc - theta_off) / (double) ENCODER_COUNT_PER_REV);
if (!CCW_ROTATION_FLAG){
theta_m = PI2 - theta_m;
}
// Mod by 2 pi
theta_m = fmod(theta_m,PI2);
return theta_m;
}The example code shown above makes use of an encoder offset value, theta_off. For synchronous machines, this offset is the count value measured by the encoder when the d-axis of the rotor is aligned with the phase U winding axis of the stator. This value typically needs to be found experimentally for each motor/encoder pair because it depends on how the encoder was aligned when it was coupled to the motor's shaft. This section provides a procedure to determine theta_off.
:alt: Torque Variation with Rotor Angle
:width: 250px
:align: right
The approximate encoder offset can be found by taking advantage of the motor having the torque characteristic shown on the right. This depicts shaft torque as the shaft is rotated counter-clockwise and corresponds to the image at the start of the section; positive torque is in the counter-clockwise direction.
The following simple procedure can be used without any feedback control:
- Set the
theta_offto 0 in the control codetask_get_theta_m(). - Eliminate any source of load torque on the shaft.
- Power on the AMDC and rotate the rotor manually by one revolution (so that the encoder z-pulse is detected).
- Align the rotor with the phase U winding axis by applying a large current vector at 0 degrees (
$I_u = I_0$ ,$I_v = I_w = -\frac{1}{2} I_0$ ). This could be accomplished by:- Using a DC power supply, or
- Injecting a current command on the d-axis using the AMDC Signal Injection module with
theta_mfixed to 0.
- Record the current encoder position and use this as the offset value:
theta_off = encoder_get_position();. - Update the variable
theta_offto the appropriate value intask_get_theta_m().
Friction and cogging torque in the motor can decrease the accuracy of the estimate in Finding the offset. A more precise offset can be found by fine-tuning the theta_off value while using closed-loop control to rotate the shaft at different speeds and monitoring the observed d-axis voltage.
:alt: Torque Variation with Rotor Angle
:width: 250px
:align: right
The correct offset is determined by considering how errors in the measured rotor angle impact the current controller's understanding of the
- $\hat{\theta}\mathrm{e}$ is the incorrect eletrical angle (due to error in offset $\theta\mathrm{off}$) that the controller is using
- the
$\gamma$ -$\delta$ vectors indicate where the controller mistakenly understands the$\mathrm{d}$ -$\mathrm{q}$ frame to be located based on$\hat{\theta}_\mathrm{e}$ - the
$\mathrm{d}$ -$\mathrm{q}$ vectors and$\theta_\mathrm{e}$ angle depict the actual$\mathrm{d}$ -$\mathrm{q}$ frame of the motor.
Note that ${\theta}{\mathrm{e}} = p {\theta}{\mathrm{m}}$ is the electrical angle where
The voltage vector of the motor terminals
This voltage vector can be converted into the
When the current commands are set to
If there is no estimation error (i.e., $\theta_\mathrm{e} - \hat{\theta}\mathrm{e} = 0$), the $\gamma$-$\delta$ frame aligns with the $\mathrm{d}$-$\mathrm{q}$ and the $v\mathrm{d}$ value seen by the controller should be zero. Based on this fact, the following procedure describes how to determine the encoder offset by finding the condition where
- Configure the AMDC for closed-loop speed and DQ current control, and configure the operating environment to allow for quick edits to
theta_offand for measuring the d-axis voltage commanded by the current regulator. Consider adding a custom command and using logging to accomplish this. - Command the motor to rotate at a steady speed under no-load conditions. Use the estimated
theta_offobtained in Finding the offset. - Sweep
theta_offover a small range around the initial estimate (e.g., ±5 counts). For each value, monitor the d-axis voltage and find thetheta_offvalue that makes the d-axis voltage closest to 0 V. Identify this by observing when the sign of the d-axis voltage changes. - Repeat step 3 at multiple rotor speeds. At each speed, record the
theta_offvalue that minimizes the d-axis voltage. - Take the average of the collected
theta_offvalues from step 4 to determine the final encoder offset value. - Plot the d-axis voltage with the final offset against the different rotor speeds. The d-axis voltage should be close to zero for all speeds if the offset is tuned correctly.
- In case there is an error in the offset value, a significant speed-dependent voltage will appear on the d-axis voltage. In this case, the user may have to re-measure the encoder offset.
An example of the results is shown in the plot below. After the calibration process, the updated encoder offset results in the d-axis voltage being closer to 0 across different speeds compared to the previous value.
:alt: Torque Variation with Rotor Angle
:width: 300px
:align: center
The user needs to compute a rotor speed signal from the obtained position signal to be used in the control algorithm. There are several ways to do this.
A simple, but naive, way to do this would be to compute the discrete time derivative of the position signal in the controller as shown below. This can be referred to as
Unfortunately, using this approach results in noise in
To solve this, a low pass filter may be applied to this signal. This is shown below to obtain a filtered speed,
Here,
Note that this low pass filter approach will always produce a lagging speed estimate due to phase delay in the filter transfer function. This may be unacceptable higher performance motor control algorithms.
To obtain a no-lag estimate of the rotor speed, users may create an observer [1], which implements a mechanical model of the rotor as shown below.
:alt: Observer Figure
:width: 600px
:align: center
The estimate of rotor speed is denoted by
J: the inertia of the rotorbthe damping coefficient of the rotor.
It is also necessary to provide the electromechanical torque,
The PI portion of the observer closes the loop on the speed, with
This tuning ensures a pole zero cancellation in the closed transfer function, resulting in a unity transfer function for speed tracking under ideal parameter estimates of J and b. An observer bandwidth of 10 Hz is typical of most systems, but similar to the low pass filter approach, users may need to alter this based on the unique aspects of their system.
1. R. D. Lorenz and K. W. Van Patten, "High-resolution velocity estimation for all-digital, AC servo drives," in IEEE Transactions on Industry Applications, vol. 27, no. 4, pp. 701-705, July-Aug. 1991, doi: 10.1109/28.85485. keywords: {Servomechanisms;Optical feedback;Optical signal processing;Transducers;Signal resolution;Velocity measurement;Position control;Feedback loop;Velocity control;Noise reduction}