Skip to Content
LessonsDay 02 — Joint FOC

Day 02 — Joint-level embedded: FOC, encoders, current as muscle

Nested control loops: FOC inside velocity inside position

Day 01 left you with one rule: one joint, end-to-end, before you chain limbs. Today is what that joint is electrically and digitally. The muscle of a humanoid is not the motor casing. It is regulated stator current, transformed into torque through geometry you must know and measure.

If you skip this layer and jump to “impedance control” or “RL walking,” you are commanding a ghost. The physical plant only hears voltage and current.

Mental model: three nested loops

A modern joint drive is three feedback loops nested by speed. Outer loops are only as honest as the inner ones.

LoopWhat it regulatesTypical rateSensor
Current (FOC)idi_d, iqi_q in the rotor frame10–20 kHzPhase current shunts / hall sensors
Velocityθ˙\dot{\theta} (joint or motor)1–5 kHzEncoder (differentiated or observer)
Position / impedanceθ\theta, or τ\tau vs θ\theta error1–2 kHzEncoder + (optional) torque sensor

Rule: never close an outer loop until the inner one is flat, stable, and logged. A beautiful trajectory planner on a humming, unstable current loop is theater.

Physical build and digital twin share this same nesting. In firmware you tune PI gains on iqi_q. In sim you need an equivalent: either a first-order current lag + torque constant, or a full electrical model. If sim torque appears instantly when you set a command, your digital twin is lying about bandwidth.

Why FOC (and what it actually does)

A brushless PMSM (permanent-magnet synchronous motor) does not have a single “torque wire.” Three phase voltages create a rotating magnetic field. Field-oriented control rotates your control frame with the rotor so that:

  • iqi_q (quadrature) produces torque (roughly τ≈32pλpmiq\tau \approx \frac{3}{2} p \lambda_{pm} i_q for a surface PMSM — pp pole pairs, λpm\lambda_{pm} flux linkage)
  • idi_d (direct) is held near zero for max torque-per-amp (or set negative for field weakening at high speed)

You measure two phase currents (the third is ia+ib+ic=0i_a + i_b + i_c = 0 if the winding is balanced), transform them with a Park/Clarke transform using electrical angle, run PI on idi_d and iqi_q, inverse-transform, and PWM the inverter.

Embedded consequences that bite humanoid builders:

  1. Electrical angle ≠ mechanical encoder angle. Multiply by pole pairs. Get this wrong and FOC becomes a heater that randomly produces torque.
  2. Current sense must be fast and calibrated. Offset error in a shunt amp becomes a DC torque bias. At a knee or ankle, that bias is a constant push into the ground or into the air.
  3. PWM frequency and dead-time matter. Dead-time distortion looks like torque ripple at low speed — exactly where a humanoid stands and shifts weight.
  4. The MCU owns this loop. Do not stream FOC over Wi-Fi. EtherCAT / CAN-FD can carry torque or current setpoints; the 20 kHz loop stays on the drive.

Physical ↔ digital pairing for FOC

PhysicalDigital twin / backing
Motor datasheet: KtK_t, pole pairs, Ld/LqL_d/L_q, resistanceSame constants in plant model; log identification runs
Shunt / hall current sense + ADC timingSample-and-hold delay in sim; noise model optional but offsets matter
Inverter DC bus voltageBus voltage as a state (brownouts change available voltage → current bandwidth)
FOC firmware (same gains you flash)Mirror bandwidth: τcmd\tau_{cmd} filtered by ~1/(2π·f_c) where f_c ≈ current-loop crossover

Exercise: command a step in iq∗i_q^*, log measured iqi_q at ≥10 kHz. Fit rise time. Put that same rise time in sim. If walking later “works in MuJoCo but not on hardware,” check here first.

Encoders: you control what you measure

Joint control quality is dominated by angle sensing, not by how clever the outer controller is.

Common choices on humanoids

TypeWhereStrengthsFailure modes
Magnetic absolute (on-axis / off-axis)Joint outputAbsolute on boot; compactSoft-iron distortion; eccentricity → harmonic error
Optical incremental + indexMotor or jointHigh resolution, low noiseNeeds homing; dust/oil; cable flex
Dual encoder (motor + joint)Both sides of gearboxEstimates torque via twist; catches backlashTwo calibrations; more wiring
ResolverHarsh environmentsRobustAnalog front-end; lower resolution unless careful

Output-side absolute is what whole-body control wants: joint angle in the URDF frame after power cycle. Motor-side incremental is what FOC commutation and high-bandwidth velocity want. Serious limbs often use both — motor encoder for FOC, joint absolute for kinematics and safety.

Encoder zero is a contract

Day 01’s frames already reserved a row for encoder zero. Fill it per joint:

  • Mechanical stop? Mid-range? Calibrated fixture?
  • Sign: does increasing encoder count match the right-hand rule on the named joint axis?
  • Gear ratio and direction between motor encoder and joint angle

Firmware, CAD, and URDF must agree on that sentence. One flipped sign and your “impedance” joint injects energy.

Digital twin side

  • Publish the same joint angle the controller uses (after gearing and zero), not a raw count.
  • Model backlash and compliance if you have dual encoders — or at least a deadband — or sim will be stiffer than reality.
  • Never differentiate raw counts without thinking: velocity estimates need filtering or an observer; naive Δθ/Δt\Delta\theta/\Delta t at 1 kHz amplifies quantization into torque chatter.

Current as muscle

Torque is what moves the body. For a well-modeled PMSM, iqi_q is your muscle activation. Thinking in voltage or in “duty cycle” is thinking in open-loop hope.

Practical chain on a geared joint:

τjoint≈N⋅η⋅Kt⋅iq\tau_{joint} \approx N \cdot \eta \cdot K_t \cdot i_q

where NN is gear ratio, η\eta efficiency (direction-dependent!), KtK_t torque constant.

Why humanoids care more than robot arms:

  • Gravity bias is always present. Standing is continuous torque, not a move-and-hold.
  • Contact: when the foot hits, you want force, not a position bang into the floor. Force comes from current (or a torque sensor outer loop around current).
  • Thermal: continuous iqi_q heats windings. Muscle has a duty cycle. Log motor temperature or estimate I2RI^2R; thermal foldback is a first-class behavior, not an afterthought.

What to log on day-zero joint bring-up

Minimum viable instrumentation for one joint:

  1. iq∗i_q^*, iqi_q (command vs measured)
  2. Bus voltage
  3. Motor and/or joint angle
  4. Estimated τjoint\tau_{joint} from current × KtK_t × NN
  5. MCU loop timing (max / jitter of the FOC tick)

If you cannot plot those five, you do not yet have a joint — you have a motor that spins.

Physical ↔ digital: same “muscle API”

Define one interface both worlds obey:

JointCommand: iq_ref | tau_ref | (later) impedance {K, D, theta_des} JointState: theta, omega, iq_meas, tau_est, fault_flags

Firmware implements it on CAN/EtherCAT. Sim implements it as a motor plugin. Higher layers (WBC, RL policy, teleop) should not know whether the plant is metal or MuJoCo.

Bring-up sequence (do this on the bench)

  1. Spin uncoupled. No link mass. Verify commutation: smooth torque, no cogging from bad angle. Check id≈0i_d \approx 0, iqi_q tracks steps.
  2. Identify KtK_t and direction. Hang a known lever + mass or use a torque sensor. Sign must match frames.md.
  3. Close velocity, then position with conservative gains. Measure tracking bandwidth.
  4. Add the link inertia (or a dummy mass). Retune. Note how gravity feedforward from the digital model helps — this is the first real payoff of the twin.
  5. Only then attach to a multi-joint chain.

Skip steps and you will debug “walking” while a single drive is thermally folding back or losing encoder sync.

SymptomLikely layer
Motor hums, no smooth torqueElectrical angle / pole pairs / encoder alignment
Constant drift force at “zero torque”Current sense offset or id/iqi_d/i_q bias
Fine at no load, oscillates under gravityGains / bandwidth; missing gravity FF from model
Works until warmThermal derating, resistance rise, magnet temp
Sim tracks; hardware lags then overshootsCurrent-loop lag not in twin; or gearbox backlash

What to freeze this week

Update frames for your first joint:

  • Motor type, pole pairs, gear ratio NN, assumed η\eta
  • Encoder type(s), counts/rev, zero definition, sign
  • Current loop rate and where it runs (which MCU)
  • The JointCommand / JointState fields your bus will carry

Physical CAD and digital URDF both reference that row. The lesson is not “buy a better FOC chip.” It is make current, angle, and torque the same story in copper and in code.

Next

Day 03 — Sensing beyond the joint: IMU placement, contact/force, and why estimation rate is a first-class design choice.

Last updated on