Every robot movement begins with an actuator converting electrical energy into torque or force. Pick the wrong one and you get overheating windings, jittery joints, or a drivetrain that stalls under load. This guide covers the physics, the selection math, the driver wiring, and the control code for the four actuator families used in modern robots: DC brushed motors, stepper motors, hobby and smart servos, and BLDC motors. It ends with a ROS2 deployment for a differential-drive base.
Quick Takeaways
- Torque decides the motor, not RPM. Gearboxes trade speed for torque at ratio N, so size the motor from load torque first.
- Steppers give open-loop positioning at ~0.9°-1.8° per full step; BLDC motors with FOC give the best torque density and efficiency; servos bundle motor, gearbox, and feedback into one package.
- Closed-loop control (encoder + PID or FOC) is the dividing line between a hobby drivetrain and a research-grade one.
- Size for a safety margin of 1.5x-2x on continuous torque to avoid thermal derating.
| Actuator | Control | Typical Efficiency | Torque Density | Cost | Best For |
|---|---|---|---|---|---|
| Brushed DC | Open/closed loop | 60-75% | Low | $ | Cheap wheeled bases |
| Stepper | Open loop (step/dir) | 40-70% | Medium | $$ | 3D printers, light arms |
| Hobby Servo | PWM position | 50-65% | Low-Medium | $ | Grippers, pan/tilt |
| Smart Servo (Dynamixel) | Serial bus | 60-75% | Medium | $$$ | Manipulators, humanoids |
| BLDC + FOC | Closed loop | 85-95% | High | $$-$$$ | Drones, legged robots, AMRs |
1. Actuator Fundamentals: Torque, Speed, Power
Mechanical output power is the product of torque and angular velocity:
P_mech = τ · ω
ω = 2π · RPM / 60
Electrical input power adds losses:
P_elec = V · I
η = P_mech / P_elec
Every motor obeys two constants that link electrical and mechanical domains:
τ = Kt · I (torque constant, N·m/A)
V_bemf = Ke · ω (back-EMF constant, V·s/rad)
In SI units, Kt = Ke. A motor with Kt = 0.05 N·m/A drawing 10 A produces 0.5 N·m.
The DC Motor Model
The steady-state voltage equation is:
V = I·R + Ke·ω
Solve for speed and you get the torque-speed line:
ω = (V / Ke) − (R / (Ke·Kt)) · τ
At stall (ω = 0), torque peaks at τ_stall = Kt·V/R and current peaks at I_stall = V/R. Never run a motor near stall for more than a fraction of a second. A 12V motor with R = 0.5Ω pulls 24A at stall.
2. Motor Types in Depth
2.1 Brushed DC Motors
Carbon brushes commutate current mechanically. Control is trivial: apply voltage, get speed. Drive them with an H-bridge (e.g., TB6612FNG, DRV8871) at a PWM frequency of 18-20 kHz to stay above audible range.
Limits: Brush wear (1,000-3,000 hours), electrical noise that couples into IMU and encoder lines, and sparking.
2.2 Stepper Motors
A stepper divides one revolution into discrete steps. A 200-step motor moves 1.8° per full step. Microstepping (1/16, 1/32) with a TMC2209 driver reaches 0.056° nominal resolution, though real accuracy is limited by detent torque and load.
steps_per_rev = 360 / step_angle
microsteps_per_rev = steps_per_rev · microstep_divisor
Failure mode: Lost steps. If load torque exceeds pull-out torque at a given speed, the rotor desynchronizes and the controller never knows. Add an encoder or use a closed-loop stepper (e.g., with a magnetic AS5600 encoder) for any safety-relevant joint.
2.3 Servo Motors
A servo is a motor plus position feedback plus a control loop.
- Hobby servos (SG90, MG996R) take a 50 Hz PWM signal. Pulse width of 1000-2000 µs maps to roughly 0°-180°. Internal potentiometer feedback limits accuracy and lifespan.
- Smart servos (Dynamixel XM430, Feetech STS3215) use half-duplex TTL/RS-485 at up to 4.5 Mbps and report position, load, temperature, and voltage.
2.4 BLDC Motors and Field-Oriented Control
A BLDC motor (brushless DC) commutates electronically using three phases. It has no brushes, runs cooler, and delivers the highest torque density. Two control modes dominate:
| Method | Position Sensing | Torque Ripple | Complexity | Noise |
|---|---|---|---|---|
| Six-step (trapezoidal) | 3 Hall sensors | High | Low | Audible |
| FOC (sinusoidal) | Encoder or Hall + interpolation | Very low | High | Near silent |
FOC transforms three-phase currents into a rotating reference frame using the Clarke and Park transforms:
Clarke: Iα = Ia
Iβ = (Ia + 2·Ib) / √3
Park: Id = Iα·cos(θ) + Iβ·sin(θ)
Iq = −Iα·sin(θ) + Iβ·cos(θ)
Torque is proportional to Iq, the quadrature current. Id is regulated to zero for surface-mount motors. Two PI loops (one per axis) run at 10-40 kHz, which gives smooth torque down to near-zero speed.
Open-source options: ODrive, VESC, SimpleFOC, and the MIT Mini Cheetah actuator architecture (quasi-direct-drive with a 6:1 planetary gear).
3. Sizing an Actuator: Step-by-Step Torque Math
3.1 Wheeled Base
For a robot of mass m (kg) on wheels of radius r (m), climbing slope angle α with rolling resistance coefficient Crr:
F_total = m·g·(sin α + Crr·cos α) + m·a
τ_wheel = F_total · r
τ_per_motor = τ_wheel / n_motors
Example: 20 kg robot, 4 motors… use 2 driven wheels. r = 0.08 m, α = 5°, Crr = 0.02, a = 0.5 m/s².
F = 20·9.81·(0.0872 + 0.02·0.996) + 20·0.5
F = 196.2·(0.1071) + 10
F = 21.0 + 10 = 31.0 N
τ_wheel = 31.0 · 0.08 = 2.48 N·m
τ_per_motor (2 motors) = 1.24 N·m
Apply a 1.5x safety factor: 1.86 N·m at the wheel.
3.2 Gearbox Selection
With gear ratio N and efficiency η_g, motor-side torque and speed are:
τ_motor = τ_wheel / (N · η_g)
ω_motor = ω_wheel · N
Target speed 1.0 m/s gives ω_wheel = v/r = 12.5 rad/s (≈119 RPM). With N = 20 and η_g = 0.85:
τ_motor = 1.86 / (20 · 0.85) = 0.109 N·m
ω_motor = 12.5 · 20 = 250 rad/s (≈2387 RPM)
Check that this operating point sits inside the motor’s continuous torque range, not its peak range.
3.3 Robot Arm Joint
For a link of mass m_l with center of mass at distance L_c, plus payload m_p at length L, the worst-case static torque at a horizontal pose is:
τ_joint = m_l·g·L_c + m_p·g·L
Add dynamic torque using the inertia J and desired angular acceleration α_ddot:
τ_dyn = J · α_ddot
4. Choosing Between Actuators: Direct Comparison
| Criterion | Stepper | Brushed DC + Gearbox | Hobby Servo | Smart Servo | BLDC + FOC |
|---|---|---|---|---|---|
| Position accuracy | ±0.05° (open loop, unloaded) | Encoder-limited | ±1°-2° | ±0.088° | Encoder-limited (<0.01°) |
| Holding torque | High (powered) | None (needs brake) | Medium | Medium-High | Needs active control |
| Peak speed | Low (<1000 RPM) | Medium | Low | Medium | Very high |
| Efficiency | Low (constant current) | Medium | Low | Medium | High |
| Backdrivability | Low | Low (geared) | Low | Low | High (QDD) |
| Driver latency | Step pulse | PWM | 20 ms frame | 1-2 ms | <100 µs loop |
| Typical voltage | 12-48V | 6-24V | 4.8-7.4V | 12-24V | 12-60V |
Rules of thumb:
- Choose steppers for repeatable low-speed positioning without feedback hardware.
- Choose BLDC with FOC when efficiency, speed, or backdrivability matters (legged robots, drones, force control).
- Choose smart servos for fast prototyping of multi-joint arms.
- Choose brushed DC only when cost dominates.
5. Control Implementation
5.1 PID Velocity Controller
The discrete PID law with derivative on measurement (avoids derivative kick):
e[k] = r[k] − y[k]
I[k] = I[k−1] + Ki · e[k] · dt
D[k] = −Kd · (y[k] − y[k−1]) / dt
u[k] = Kp · e[k] + I[k] + D[k]
class PID:
"""Discrete PID with anti-windup and derivative-on-measurement."""
def __init__(self, kp, ki, kd, out_min, out_max):
self.kp, self.ki, self.kd = kp, ki, kd
self.out_min, self.out_max = out_min, out_max
self.integral = 0.0
self.prev_meas = 0.0
def update(self, setpoint, measurement, dt):
error = setpoint - measurement
# Integral term accumulates error over time
self.integral += self.ki * error * dt
# Clamp integral to prevent windup when output saturates
self.integral = max(self.out_min, min(self.out_max, self.integral))
# Derivative on measurement, not error, avoids setpoint kick
derivative = -self.kd * (measurement - self.prev_meas) / dt
self.prev_meas = measurement
output = self.kp * error + self.integral + derivative
return max(self.out_min, min(self.out_max, output))
Tuning workflow (Ziegler-Nichols starting point):
- Set Ki = Kd = 0. Raise Kp until the output oscillates steadily. Record Ku (ultimate gain) and Tu (oscillation period).
- Set
Kp = 0.6·Ku,Ki = 1.2·Ku/Tu,Kd = 0.075·Ku·Tu. - Reduce Kp by 20-30% if overshoot exceeds 15%, since Z-N tends to be aggressive.
Run velocity loops at 500 Hz-1 kHz and position loops at 100-200 Hz in a cascaded structure.
5.2 Hobby Servo with Arduino
#include <Servo.h>
Servo gripper;
void setup() {
// Attach to pin 9, with min/max pulse widths in microseconds.
// Calibrate these per servo to avoid stalling at the end stops.
gripper.attach(9, 600, 2400);
}
void loop() {
gripper.write(30); // Open: 30 degrees
delay(1000);
gripper.write(120); // Close: 120 degrees
delay(1000);
}
Power the servo from a dedicated 5-6V, 3A BEC. Never power an MG996R from the Arduino 5V pin; stall current reaches 2.5A and causes brownouts.
6. Wiring and Power Architecture
A clean power tree prevents most “mystery” resets:
- Battery (e.g., 6S LiPo, 22.2V nominal) feeds a power distribution board.
- Motor drivers connect directly to the battery rail through a TVS diode and a 470-1000 µF bulk capacitor placed within 5 cm of the driver.
- Logic rail (5V/3.3V) comes from a separate buck converter, isolated from motor noise by a ferrite bead.
- Common ground star-point at the battery negative terminal.
- Emergency stop cuts motor power via a contactor, independent of software.
Regenerative braking warning: When a motor decelerates a heavy load, it pumps current back into the supply. Without a brake resistor or a battery that can sink charge, bus voltage spikes and destroys the driver. Set the driver’s overvoltage threshold to ~10% above nominal.
7. Real-World Workflow: Differential-Drive Base on ROS2
This workflow connects two BLDC hub motors to ROS2 using ros2_control and a diff_drive_controller.
7.1 Hardware Interface Configuration (URDF)
<ros2_control name="DiffBotSystem" type="system">
<hardware>
<!-- Custom hardware plugin that talks to the motor driver over CAN/serial -->
<plugin>my_robot_hardware/DiffBotSystemHardware</plugin>
<param name="left_wheel_name">left_wheel_joint</param>
<param name="right_wheel_name">right_wheel_joint</param>
<param name="loop_rate">50</param>
<param name="enc_counts_per_rev">4096</param>
</hardware>
<joint name="left_wheel_joint">
<command_interface name="velocity"/>
<state_interface name="velocity"/>
<state_interface name="position"/>
</joint>
<joint name="right_wheel_joint">
<command_interface name="velocity"/>
<state_interface name="velocity"/>
<state_interface name="position"/>
</joint>
</ros2_control>
7.2 Controller Parameters
controller_manager:
ros__parameters:
update_rate: 100 # Hz, control loop frequency
diff_drive_controller:
ros__parameters:
left_wheel_names: ["left_wheel_joint"]
right_wheel_names: ["right_wheel_joint"]
wheel_separation: 0.42 # meters, center-to-center
wheel_radius: 0.08 # meters
cmd_vel_timeout: 0.5 # seconds, stops robot if /cmd_vel goes stale
publish_rate: 50.0
linear.x.max_velocity: 1.0 # m/s
angular.z.max_velocity: 2.0 # rad/s
linear.x.max_acceleration: 1.5
7.3 Differential Drive Kinematics
The controller converts a body twist (v, ω) into wheel velocities:
ω_left = (v − ω·L/2) / r
ω_right = (v + ω·L/2) / r
where L is the wheel separation and r is the wheel radius. Odometry integrates the reverse mapping:
v = r·(ω_right + ω_left) / 2
ω = r·(ω_right − ω_left) / L
7.4 Launch and Test
# Spawn controllers
ros2 control load_controller --set-state active diff_drive_controller
ros2 control load_controller --set-state active joint_state_broadcaster
# Command 0.3 m/s forward with a 0.2 rad/s yaw rate
ros2 topic pub -r 10 /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \
"{twist: {linear: {x: 0.3}, angular: {z: 0.2}}}"
# Inspect odometry and wheel joint states
ros2 topic echo /diff_drive_controller/odom
ros2 topic echo /joint_states
Validation checklist: Drive a 2 m straight line and measure lateral drift. More than 2% drift usually indicates a wheel radius or separation calibration error, not a PID fault.
8. Thermal Limits and Failure Modes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Motor hot, low torque | Operating above continuous rating | Increase gear ratio or upsize motor |
| Stepper skips steps | Pull-out torque exceeded | Lower acceleration, raise current, add encoder |
| Servo jitters at rest | Noisy supply or deadband too tight | Add bulk capacitance, widen deadband |
| BLDC cogging at low speed | Poor Hall/encoder alignment | Re-run motor calibration |
| Encoder noise spikes | Motor cable routed beside signal lines | Use shielded twisted pair, separate routing |
Winding temperature rise follows the I²R relationship: heating scales with the square of current. Doubling current quadruples heat. Monitor temperature through the driver or a thermistor, and derate above 80°C for most enamel windings.
FAQ
What is the difference between a motor and a servo?
A motor converts electrical energy to rotation with no built-in feedback. A servo combines a motor, a gearbox, a position sensor, and a controller in one unit, so it can hold a commanded angle on its own.
Is a stepper motor or a BLDC motor better for a robot arm?
BLDC motors with FOC and an encoder are better for dynamic arms because they offer higher torque density, speed, and efficiency. Steppers suit low-speed, lightweight arms where open-loop simplicity matters more than performance.
How do I calculate the motor torque a robot needs?
Compute the required wheel or joint torque from load force, radius, slope, and acceleration (τ = F·r). Divide by gear ratio and gearbox efficiency, then apply a 1.5x-2x safety factor and compare against the motor’s continuous torque rating.
Why do robot motors need encoders?
An encoder supplies position and velocity feedback so a PID or FOC loop can correct errors from load changes, friction, or voltage sag. Without feedback, the controller cannot detect stalls or lost steps.




