Skip to content

DC Motors and Encoders

Introduction

Brushed DC motors are the most basic motor type. When paired with encoders, they enable precise speed and position control. They are widely used in educational robots and small wheeled platforms.

Internal Structure of Brushed DC Motors

Core Components

Component Function
Permanent magnets (stator) Provide a constant magnetic field
Armature windings (rotor) Current-carrying conductors that produce torque
Commutator Copper segment ring that mechanically switches current direction
Brushes Carbon or metal brushes that conduct current into the commutator
Bearings Support rotor rotation

Operating Principle

  1. Current flows through the brushes into the commutator and enters the armature windings
  2. Current-carrying conductors experience Ampere's force in the permanent magnetic field: \(F = BIL\)
  3. The resulting torque drives the rotor to rotate
  4. The commutator switches the current direction at the appropriate position, maintaining consistent rotation

Common Models

Model Voltage No-load Speed Stall Torque Typical Application
130 micro motor 3–6V 8000 rpm 0.02 N·m Toys, small experiments
370 motor 6–12V 6000 rpm 0.1 N·m Small robots
550 motor 12–24V 5000 rpm 0.5 N·m Medium platforms
775 motor 12–36V 4000 rpm 1.5 N·m Large chassis

Geared DC Motors

Why Gear Reduction Is Needed

Bare motors run at high speeds (thousands of rpm) with low torque, making them unsuitable for directly driving robot wheels or joints. Adding a gearbox achieves:

\[ \tau_{out} = N \cdot \tau_{motor} \cdot \eta_{gear} \]
\[ \omega_{out} = \frac{\omega_{motor}}{N} \]

Where \(N\) is the gear ratio and \(\eta_{gear}\) is the gear efficiency (typically 0.7–0.9).

Common Geared Motors

Model Voltage Gear Ratio Output Speed Output Torque With Encoder
JGA25-370 6–12V 1:21.3 ~280 rpm 1.5 kg·cm Yes
JGB37-520 6–12V 1:30 ~200 rpm 3 kg·cm Yes
GM25-370 12V 1:34 ~180 rpm 2.5 kg·cm Yes

Gearbox Types

  • Spur gear train: Low cost, higher noise, ~80% efficiency
  • Planetary gear: Compact coaxial design, 85–95% efficiency, common in high-end geared motors
  • Worm gear: Self-locking characteristic, high gear ratio, lower efficiency (40–70%)

Encoders

An encoder is a sensor that measures the angular position and speed of a motor shaft. It is the core component for closed-loop control.

Optical Incremental Encoder

Principle

  • A code disc has evenly spaced slits
  • A light source (LED) and a photosensitive element are placed on opposite sides of the disc
  • Rotation produces pulse signals

Parameters

  • PPR (Pulses Per Revolution): Number of pulses per revolution, e.g., 100/200/400/600
  • CPR (Counts Per Revolution): Counts per revolution after quadrature decoding, \(CPR = 4 \times PPR\)
  • Resolution: \(\Delta\theta = \frac{360°}{CPR}\)

Magnetic Encoder

Operating Principle

  • A diametrically magnetized small magnet is mounted on the shaft end
  • A Hall/magnetoresistive sensor IC detects the magnetic field angle

AS5600 Magnetic Encoder

Parameter Value
Resolution 12-bit (4096 positions/rev)
Interface I2C / Analog output
Accuracy ±1°
Operating voltage 3.3V / 5V
Size Very compact
# AS5600 angle reading via I2C (MicroPython example)
from machine import I2C, Pin

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
AS5600_ADDR = 0x36
RAW_ANGLE_REG = 0x0C

def read_angle():
    data = i2c.readfrom_mem(AS5600_ADDR, RAW_ANGLE_REG, 2)
    raw = (data[0] << 8) | data[1]
    angle = raw * 360.0 / 4096.0
    return angle

Absolute Encoder

  • Each position has a unique code (Gray code or binary)
  • Position is known immediately upon power-up, no homing required
  • Single-turn absolute: unique position within one revolution
  • Multi-turn absolute: can record multiple revolutions

Encoder Type Comparison

Feature Optical Incremental Magnetic Encoder Absolute
Cost Low Medium High
Resolution Medium High High
Noise immunity Medium Strong Strong
Homing on power-up Required Not required Not required
Typical application Geared motor speed measurement Servos / joints Industrial servos

Quadrature Decoding

Dual-Channel Signals

Incremental encoders typically output two signals, A and B, with a 90° phase difference:

        ┌──┐  ┌──┐  ┌──┐
Ch A:   │  │  │  │  │  │
     ───┘  └──┘  └──┘  └──

           ┌──┐  ┌──┐  ┌──┐
Ch B:      │  │  │  │  │  │
        ───┘  └──┘  └──┘  └──

Direction Detection

  • Forward rotation: B is LOW on the rising edge of A
  • Reverse rotation: B is HIGH on the rising edge of A

Quadrature Counting

By using all edges (rising + falling) of both A and B channels, the counting resolution is increased by a factor of 4:

\[ CPR = 4 \times PPR \]

Speed Calculation

Count the number of pulses \(\Delta n\) within a fixed time interval \(\Delta t\):

\[ \omega = \frac{2\pi \cdot \Delta n}{CPR \cdot \Delta t} \quad (\text{rad/s}) \]
\[ RPM = \frac{60 \cdot \Delta n}{CPR \cdot \Delta t} \]

PID Speed Control

PID Controller

PID (Proportional-Integral-Derivative) is the most common algorithm for motor speed control:

\[ u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt} \]

Where:

  • \(e(t) = \omega_{target} - \omega_{actual}\) — Speed error
  • \(K_p\) — Proportional gain: larger error produces larger output
  • \(K_i\) — Integral gain: eliminates steady-state error
  • \(K_d\) — Derivative gain: suppresses overshoot and oscillation

Discrete Implementation

Practical controllers run at a fixed period \(T_s\):

\[ u[k] = K_p \cdot e[k] + K_i \cdot T_s \sum_{i=0}^{k} e[i] + K_d \cdot \frac{e[k] - e[k-1]}{T_s} \]

Arduino/ESP32 Code Example

// Encoder pin definitions
#define ENCODER_A 2   // Interrupt pin
#define ENCODER_B 3
#define MOTOR_PWM 5
#define MOTOR_DIR 4

// Encoder count (volatile for interrupt safety)
volatile long encoderCount = 0;

// PID parameters
float Kp = 2.0, Ki = 0.5, Kd = 0.1;
float targetRPM = 100.0;
float integral = 0, prevError = 0;

// Encoder interrupt service routine
void encoderISR() {
    if (digitalRead(ENCODER_B) == LOW) {
        encoderCount++;
    } else {
        encoderCount--;
    }
}

void setup() {
    pinMode(ENCODER_A, INPUT_PULLUP);
    pinMode(ENCODER_B, INPUT_PULLUP);
    pinMode(MOTOR_PWM, OUTPUT);
    pinMode(MOTOR_DIR, OUTPUT);

    attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderISR, RISING);
    Serial.begin(115200);
}

// PID control loop (runs every 20ms)
unsigned long lastTime = 0;
long lastCount = 0;
const float CPR = 4 * 11 * 21.3;  // 4x quadrature × PPR × gear ratio
const float dt = 0.02;             // 20ms

void loop() {
    unsigned long now = millis();
    if (now - lastTime >= 20) {
        // Calculate current RPM
        noInterrupts();
        long count = encoderCount;
        interrupts();

        float deltaCount = count - lastCount;
        float currentRPM = (deltaCount / CPR) * (60.0 / dt);

        // PID calculation
        float error = targetRPM - currentRPM;
        integral += error * dt;
        integral = constrain(integral, -100, 100);  // Integral windup limit
        float derivative = (error - prevError) / dt;

        float output = Kp * error + Ki * integral + Kd * derivative;
        output = constrain(output, -255, 255);

        // Output to motor
        if (output >= 0) {
            digitalWrite(MOTOR_DIR, HIGH);
            analogWrite(MOTOR_PWM, (int)output);
        } else {
            digitalWrite(MOTOR_DIR, LOW);
            analogWrite(MOTOR_PWM, (int)(-output));
        }

        prevError = error;
        lastCount = count;
        lastTime = now;

        // Debug output
        Serial.print("Target:");  Serial.print(targetRPM);
        Serial.print(" Current:"); Serial.print(currentRPM);
        Serial.print(" Output:");  Serial.println(output);
    }
}

PID Tuning Tips

Step Action Expected Effect
1 Set \(K_i=0, K_d=0\), gradually increase \(K_p\) System begins to respond, may oscillate
2 Slightly reduce \(K_p\), gradually increase \(K_i\) Eliminates steady-state error
3 Increase \(K_d\) Reduces overshoot, faster convergence
4 Fine-tune all three parameters Achieve satisfactory response

Ziegler-Nichols Method

  1. Use P-only control, increase \(K_p\) until sustained oscillation, record as \(K_u\) (ultimate gain)
  2. Measure the oscillation period \(T_u\)
  3. Set parameters: \(K_p = 0.6K_u\), \(K_i = 2K_p/T_u\), \(K_d = K_p T_u/8\)

ESP32 Special Considerations

Hardware Encoder Interface

The ESP32 has a built-in PCNT (Pulse Counter) hardware module for efficient encoder signal processing:

// Using ESP32 PCNT hardware decoding
#include "driver/pcnt.h"

void setupPCNT() {
    pcnt_config_t pcnt_config;
    pcnt_config.pulse_gpio_num = ENCODER_A;
    pcnt_config.ctrl_gpio_num = ENCODER_B;
    pcnt_config.channel = PCNT_CHANNEL_0;
    pcnt_config.unit = PCNT_UNIT_0;
    pcnt_config.pos_mode = PCNT_COUNT_INC;
    pcnt_config.neg_mode = PCNT_COUNT_DEC;
    pcnt_config.lctrl_mode = PCNT_MODE_REVERSE;
    pcnt_config.hctrl_mode = PCNT_MODE_KEEP;
    pcnt_config.counter_h_lim = 32767;
    pcnt_config.counter_l_lim = -32768;

    pcnt_unit_config(&pcnt_config);
    pcnt_counter_pause(PCNT_UNIT_0);
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_resume(PCNT_UNIT_0);
}

PWM Resolution

The ESP32's LEDC PWM supports up to 16-bit resolution:

// ESP32 high-resolution PWM
ledcSetup(0, 20000, 10);  // Channel 0, 20kHz, 10-bit resolution (0-1023)
ledcAttachPin(MOTOR_PWM, 0);
ledcWrite(0, dutyCycle);   // dutyCycle: 0-1023

Summary

  • Brushed DC motors have a simple structure, suitable for introductory and small robot projects
  • Geared motors balance speed and torque through a gearbox
  • Encoders are the key sensor for closed-loop control
  • Quadrature decoding uses A/B channels for direction detection and 4x counting
  • PID control is the foundational algorithm for motor speed/position control
  • The ESP32's PCNT hardware and high-resolution PWM make it an ideal platform for motor control

评论 #