⚙️ 固件与电机控制:SimpleFOC / ODrive / 国产方案

从嵌入式选型到 FOC 固件原理(代码级),再到 SimpleFOC、ODrive 两大开源生态的上手路径, 最后落到小米电机生态与国产开源方案。读完你能独立跑通一个无刷电机的 FOC 闭环。
FOC SimpleFOC ODrive STM32 固件开发
🎯 本页学习目标
1. 能说出 STM32 与 ESP32 在电机控制场景下的选型依据(主频、外设、价格、社区生态)
2. 能按"电流采样 → Clark → Park → PID → 逆 Park → SVPWM"完整讲清 FOC 闭环的每一步数学含义,并读懂对应代码
3. 能在 Arduino/ESP32 上用 SimpleFOC 跑通无刷电机的"开环 → 编码器校准 → 闭环"全流程
4. 会用 ODFinder / odrivetool 配置 ODrive,并写出最简单的 Python 控制脚本
5. 能说清国产电机方案(小米 CyberGear 等)与 MIT Mini Cheetah 开源控制的基本思路
建议用时:约 60 分钟(含动手实验约 90 分钟)

1 嵌入式基础速览

人形机器人一个关节 = 电机 + 减速器 + 编码器 + 驱动器。驱动器固件是关节的"神经系统":它负责读编码器、采电流、算 FOC、输出 PWM、走 CAN 总线。无论是直接用 SimpleFOC / ODrive,还是自研驱动,最终都要落到 MCU 固件这一层。先把硬件选型和外设角色搞清楚,后面学库才会"知其然也知其所以然"。

1.1 STM32 / ESP32 选型对比

电机控制对 MCU 的核心要求是:高精度定时器(互补 PWM + 死区)、高速 ADC(同步采样)、足够的算力(FPU 最好有)。下面是两大家族在电机控制场景下的直接对比:

型号内核 / 主频电机控制关键外设参考价格社区生态适合场景
STM32F103C8T6Cortex-M3 @72MHz,无硬件浮点基础定时器 / ADC,无高级电机控制外设约 ¥5~10教程海量裸机入门、学习 MCU 基本功
STM32F401/F411Cortex-M4F @84/100MHz,带 FPU通用定时器 / ADC / DMA约 ¥10~20丰富中小型项目、带 FPU 的过渡选择
STM32G431(关节主力)Cortex-M4F @170MHz + FPU + CORDIC高级定时器(互补 PWM/死区)、内部比较器、运放、FDCAN约 ¥15~25电机控制资料多关节 FOC 主力,众多商用关节的参考架构
ESP32Xtensa 双核 @240MHz外设丰富,但无 CAN 控制器(需外接)、ADC 精度一般约 ¥10~20极活跃,Arduino/ESP-IDF学习 SimpleFOC、无线调试、原型验证

结论:学习 FOC 用 ESP32 + SimpleFOC 上手最快(库支持好、接线简单);做产品原型对标主流关节方案,选 STM32G431 这类带高级定时器 + 内部运放/比较器 + FDCAN 的型号,它几乎是"为 FOC 而生"。

1.2 四大外设在电机控制中的角色

外设在电机控制中的角色关键点
高级定时器(TIM1/8)生成三相六路互补 PWM 并插入死区;编码器正交解码(ABI);触发 ADC 同步采样死区时间、PWM 频率(常见 10~40kHz)、中心对齐模式
ADC采样相电流(低边采样电阻)与母线电压必须由定时器触发、避开开关沿噪声;三相只需采两相
PWM 输出把 SVPWM 计算结果变成三相桥的开关信号六路互补、死区插入、逐波限流(硬件比较器)
CAN / FDCAN关节间总线通信,把目标位置/速度/力矩下发给各关节周期 ≤1ms;人形机器人多关节级联的事实标准;FDCAN 速率更高

1.3 开发环境搭建

💡 学习建议:第一周用 PlatformIO + ESP32 + SimpleFOC 把闭环跑通,建立"FOC 能工作"的信心;第二周再用 STM32CubeIDE + G431 重新实现一遍,重点看定时器触发 ADC、互补 PWM 和死区配置——这两步做完,固件地基就扎实了。

2 FOC 固件实现原理(代码级)

FOC(Field Oriented Control,磁场定向控制)一句话概括:把三相交流电机在"与转子同步旋转的坐标系"里看成一个直流电机,然后像控制直流电机一样用两个 PID 控制电流。这样既能精确控制力矩,又能让电流正弦、效率高、噪声小。

2.1 FOC 控制流水线总览

1电流采样
定时器同步触发 ADC,采两相电流
2Clark 变换
三相 → 两相静止(α,β)
3Park 变换
静止 → 旋转(d,q),得直流量
4双环 PID
d/q 轴电流分别做无静差控制
5逆 Park 变换
旋转 → 静止,得目标电压矢量
6SVPWM 调制
电压矢量 → 三相 PWM 开关序列

其中第 2、3 步合称"电流解算",第 5、6 步合称"电压合成"。整个过程每个 PWM 周期(如 10kHz 时 100μs)执行一次,顺序不能乱。

2.2 第一步:电流采样

力矩与电流成正比(τ = Kt · Iq),所以闭环控制力矩必须先"看见"电流。工程上最常见的是低边采样电阻 + 差分运放 → ADC。三相电流满足基尔霍夫定律 Ia + Ib + Ic = 0,因此只需采两相即可算出第三相。采样时刻必须在 PWM 中心对齐处、避开功率管开关瞬间的电流尖峰(详见第 6 节"常见坑")。

// 伪代码:定时器更新事件触发 ADC 中断,一次采样两相电流
void adc_isr(void) {
  float ia = adc_read(CH_A);          // A 相电流(经运放放大后换算)
  float ib = adc_read(CH_B);          // B 相电流
  float ic = -(ia + ib);              // 基尔霍夫: Ia + Ib + Ic = 0
  // 交给后续 Clark 变换处理...
}

2.3 Clark 变换(三相 → 两相静止坐标系)

三相电流在空间上是 120° 分布的,直接处理麻烦。Clark 变换把它们投影到两个正交轴 α、β 上(等幅值形式):

Iα = Ia
Iβ = (Ia + 2·Ib) / √3
// C 实现(等幅值 Clark 变换)
#define SQRT3 1.7320508f
float ialpha = ia;                                  // Iα = Ia
float ibeta  = (ia + 2.0f * ib) / SQRT3;            // Iβ = (Ia + 2·Ib)/√3

2.4 Park 变换(静止 → 旋转坐标系)

α/β 轴上的电流仍是正弦交流量,PID 对交流量无法做到无静差跟踪。Park 变换把 α/β 投影到与转子同步旋转的 d/q 坐标系上,得到两个近似直流的分量:Id(励磁分量)、Iq(力矩分量)。这里的 θ 是转子电角度——这正是编码器的用武之地。

Id = Iα·cos(θ) + Iβ·sin(θ)
Iq = −Iα·sin(θ) + Iβ·cos(θ)
// C 实现(Park 变换)
float ct = cosf(theta);                             // theta = 转子电角度
float st = sinf(theta);
float id = ialpha * ct + ibeta * st;                // Id = Iα·cosθ + Iβ·sinθ
float iq = -ialpha * st + ibeta * ct;               // Iq = −Iα·sinθ + Iβ·cosθ

2.5 电流环 PID

得到直流量后,两个独立的 PID 分别控制 d 轴与 q 轴电流:q 轴目标来自速度/位置外环(要多少力矩),d 轴目标通常恒为 0(永磁电机最大转矩电流比,即 MTPA 的简化形式)。

// 以 q 轴电流环为例(每个控制周期执行一次)
float err = iq_ref - iq;                    // iq_ref 来自外环
integral += err * dt;
if (integral >  limit) integral =  limit;   // 抗积分饱和
if (integral < -limit) integral = -limit;
float vq = kp * err + ki * integral + kd * (err - err_prev) / dt;
err_prev = err;
// vd 同理,通常 id_ref = 0

电流环响应要快(带宽 1~2kHz),所以它必须跑在最快的中断优先级上,直接由 PWM 周期驱动。

2.6 逆 Park 变换(旋转 → 静止)

PID 输出的 vd、vq 还是旋转坐标系下的量,要变成实际的三相电压,先逆 Park 回 α/β 静止坐标系:

Vα = Vd·cos(θ) − Vq·sin(θ)
Vβ = Vd·sin(θ) + Vq·cos(θ)
// C 实现(逆 Park 变换)
float valpha = vd * ct - vq * st;           // Vα = Vd·cosθ − Vq·sinθ
float vbeta  = vd * st + vq * ct;           // Vβ = Vd·sinθ + Vq·cosθ

2.7 SVPWM 调制(电压 → 开关序列)

SVPWM(空间矢量脉宽调制)把 (Vα, Vβ) 这个电压矢量用三相桥的 8 个基本开关状态(6 个非零矢量 + 2 个零矢量)来合成。每个 PWM 周期内,先判断矢量落在哪个扇区,再计算相邻两个基本矢量的作用时间 T1、T2,剩余时间 T0 用零矢量填充,最后按"七段式"中心对齐排布开关序列:

扇区 s 内,γ 为电压矢量相对扇区起始边的角度:
  T1 = (√3·Ts / Vdc) · |V| · sin(60° − γ)
  T2 = (√3·Ts / Vdc) · |V| · sin(γ)
  T0 = Ts − T1 − T2
// 伪代码:SVPWM 主流程(每 PWM 周期一次)
int sector = sector_of(valpha, vbeta);      // 由 Vα/Vβ 符号与比值判扇区 1..6
float t1 = calc_t1(sector, valpha, vbeta, Ts, Vdc);
float t2 = calc_t2(sector, valpha, vbeta, Ts, Vdc);
float t0 = Ts - t1 - t2;                    // 零矢量填充
update_compare_regs(sector, t1, t2, t0);    // 更新比较寄存器,硬件输出六路互补 PWM
💡 为什么用 SVPWM,而不是简单的 120° 方波六步换相?母线电压利用率更高:SVPWM 线性调制区最大相电压幅值可达 Vdc/√3(约 0.577·Vdc),比六步方波等效的约 0.5·Vdc 提高约 15%;
电流谐波小、转矩脉动小:三相电流更接近正弦,电机噪声和振动明显更低;
与 FOC 天然衔接:逆 Park 输出的 (Vα, Vβ) 直接就是 SVPWM 的输入,算法闭环无缝。

2.8 为什么必须用编码器

回到第 2.4 节:Park 变换和逆 Park 变换都需要转子电角度 θ,它是整个旋转坐标系的"基准"。没有 θ,FOC 就无从谈起:

编码器常见两种:增量式(ABI 正交输出或霍尔,便宜但上电不知道绝对位置,需找零)和绝对值(磁编码器如 AS5600/MT6701,上电即知位置,关节类产品几乎都用绝对值编码器)。

⚠️ 常见事故:编码器接反、极对数设错或未校准电角度偏移,会导致力矩方向判断错误,电机上电瞬间"爆冲"猛转——做实验务必先在电源端限流(可调电源限到 1~2A),再接电机。

2.9 与 SimpleFOC 的代码结构对照

SimpleFOC 把上面整条流水线封装成非常清晰的两步,理解 FOC 原理后读它的源码毫无障碍:

// SimpleFOC 的主循环结构(简化示意)
void loop() {
  motor.loopFOC();   // ① 读编码器 → 电流采样 → Clark → Park → 电流 PID → 逆 Park → SVPWM
  motor.move(target); // ② 根据控制模式(力矩/速度/位置)计算电流目标,交给 loopFOC 执行
}
FOC 数学步骤SimpleFOC 内部对应你改的是什么
电流采样BLDCDriver3PWM / 电流检测类(取决于是否带电流采样)驱动板与引脚
Clark / Park / 逆 ParkBLDCMotor 内部电流解算与电压合成一般不用改
电流 PIDPIDController(电流环)KP/KI 与输出限幅
编码器 → 电角度Encoder / MagneticSensorI2C 等传感器类编码器类型与极对数
SVPWM驱动层的 setPwm / writeDutyCyclePWM 频率、死区(在 MCU 层)

3 SimpleFOC 入门实战

3.1 SimpleFOC 是什么

SimpleFOC 是目前最流行的开源 FOC 库:在 Arduino 生态上把"电机 + 驱动板 + 编码器"抽象成 BLDCMotorBLDCDriver3PWMEncoder 等类,内置力矩/速度/位置三种控制模式,以及开环模式。官方文档(docs.simplefoc.com)有完整的中英文教程,社区活跃度很高,是个人开发者进入 FOC 世界的第一站。

3.2 支持哪些板子

SimpleFOC 支持 Arduino 全系(UNO/Nano/Mega 等)、STM32(BluePill、Nucleo 系列,B-G431B-ESC1 这类电机专用板体验最佳)、ESP32(最推荐入门)、Teensy、Raspberry Pi Pico 等;编码器支持增量 ABI、霍尔、以及 AS5600 等 I2C 磁编码器。详细的板子清单以官方文档为准。

3.3 硬件接线示例(以 ESP32 + 三相驱动板为例)

🔥 上电前必查三项:① 电源正负极与电压档位(24V 就上 24V,别上 48V);② 电机三根相线不能短路、不能碰到金属;③ 可调电源先限流 1~2A——校准或接线错误时,限流能保住驱动板和电机。

3.4 最小可用代码(可直接复制)

下面是一份完整的 SimpleFOC 示例(基于 ESP32):先跑开环验证电机能转,再接编码器切换电压闭环。注释里标了每一步的作用。

// SimpleFOC 最小可用示例:BLDC 无刷电机 · 开环 → 闭环
// 硬件:ESP32 + 三相无刷驱动板(如 L6234 模块/大疆 FOC 板)+ 无刷电机
// 接线:驱动板 VM/GND 接 12~24V 电源;三相输出接电机 A/B/C;
//       编码器(可选)接 GPIO36/39;电机每转一圈的极对数按实际填
#include <SimpleFOC.h>

// 电机对象:极对数(pole pairs),例如 7 —— 必须与电机实际一致
BLDCMotor motor = BLDCMotor(7);

// 三相 PWM 驱动器:引脚依次为 A、B、C(ESP32 示例,按你的接线改)
BLDCDriver3PWM driver = BLDCDriver3PWM(32, 33, 25);

// 增量编码器:ABI 两路引脚 + 每圈计数(CPR),没有编码器可先不启用
Encoder encoder = Encoder(36, 39, 2048);

// 编码器中断回调(ABI 编码器必须)
void doA(){ encoder.handleA(); }
void doB(){ encoder.handleB(); }

void setup(){
  // 1) 驱动器:设置供电电压并初始化
  driver.voltage_power_supply = 12.0f;   // 与你的电源电压一致!
  driver.init();
  motor.linkDriver(&driver);

  // 2) 编码器:初始化并接上电机(注释掉可先跑开环)
  encoder.init();
  encoder.enableInterrupts(doA, doB);
  motor.linkSensor(&encoder);

  // 3) 控制模式:先开环验证,再接编码器后改成 voltage / velocity / angle
  motor.controller = MotionControlType::openloop;   // 开环:不需要编码器
  // motor.controller = MotionControlType::voltage; // 闭环:注释掉上一行,启用这行

  // 4) 安全限幅:限制最大电压,防止误操作烧电机
  motor.voltage_limit = 3.0f;             // 先给 3V,能转再加
  motor.init();
  motor.initFOC();                        // 编码器校准 + FOC 初始化(开环模式会自动跳过)

  Serial.begin(115200);
  delay(1000);
}

void loop(){
  motor.loopFOC();                        // 闭环时必须调用(开环模式内部会跳过)
  motor.move(2.0f);                       // 开环:2V 电压矢量;闭环:2V 目标电压(≈力矩)
}

运行顺序建议:先保持 openloop 模式上电,电机应以 2V 小电压平稳旋转;再接入编码器、改成 voltage 模式重新上电,`initFOC()` 会自动执行编码器校准,成功后电机会"咔"地锁定,此时 `move()` 给的就是带反馈的力矩指令。

3.5 调参顺序(先开环 → 编码器校准 → 闭环)

1开环验证
openloop 模式给 2~3V,确认电机能转、相线/电源正确
2编码器接线
接好编码器,Serial 打印角度,转动电机看读数变化
3编码器校准
initFOC() 自动找电角度偏移;Serial 报 "OK" 即通过
4电压闭环
voltage 模式,给固定电压看是否有力矩反馈
5速度闭环
velocity 模式,调 PID_velocity 的 P、I 与输出斜坡
6位置闭环
angle 模式,调 PID_angle,最后接上位机做关节控制
⚠️ 调参纪律:每次只改一个参数;速度环先给很小的目标值(如 1 rad/s)确认方向;发现电机尖叫抖动,立即断电,把 P 调小一半再试。电压闭环是最稳的起点,电流/速度环都建立在它之上。

4 ODrive 入门

4.1 ODrive 是什么

ODrive 是机器人圈最著名的开源高功率 FOC 驱动板:硬件、固件、上位机工具全部开源(GitHub: odriverobotics/ODrive)。经典板卡 v3.6 基于 STM32F405 + DRV8301 栅极驱动,支持 24~48V 母线电压、双轴驱动,单轴峰值电流可达数十安培量级(持续电流取决于散热,以官方手册为准)。它把"FOC 固件 + 三环控制 + 总线接口"做成即插即用的整板,是轮式机器人、直驱关节样机的标配。

4.2 功能特点

4.3 用 ODFinder / odrivetool 配置的步骤

1安装
PC 上执行 pip install odrive,装好 ODFinder 与 odrivetool
2连接
USB 连接 ODrive,ODFinder 自动识别板卡与固件版本
3基础参数
odrivetool 中设置极对数 pole_pairs、总线电压、编码器类型
4电机校准
执行校准命令,自动辨识编码器偏移、电机电阻与电感
5保存
save_configuration() 写入板载 Flash,断电不丢
# 终端 1:进入交互式工具(需要先安装: pip install odrive)
odrivetool

# 在 odrivetool 交互环境中(伪代码,以实际命令为准):
odrv0.axis0.motor.config.pole_pairs = 7      # 极对数
odrv0.axis0.motor.config.current_lim = 20    # 电流限制(A)
odrv0.axis0.encoder.config.cpr = 4096        # 编码器每圈计数
odrv0.axis0.config.startup_closed_loop = False  # 上电不自动闭环(调试期)
odrv0.axis0.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE  # 执行校准
odrv0.save_configuration()                   # 保存配置

4.4 Python API 示例

校准完成后,日常控制就是几行 Python。下面的脚本让 axis0 进入闭环并依次做速度、位置、力矩控制:

import odrive
from odrive.enums import *

# 找到并连接 ODrive(USB)
odrv0 = odrive.find_any(timeout=10)

axis = odrv0.axis0
# 进入闭环控制
axis.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL

axis.input_vel = 10.0        # 速度模式:目标 10 rad/s
axis.input_pos = 3.14        # 位置模式:目标 π rad
axis.input_torque = 0.5      # 力矩模式:目标 0.5 N·m

# 读取当前状态
print("pos:", axis.pos_estimate, "vel:", axis.vel_estimate)

# 保存配置(参数改动后调用)
odrv0.save_configuration()
💡 提示:位置、速度、力矩三个 input_* 同时存在,生效的是当前控制模式对应的那个。切换模式前先让电机停下,直接改 requested_state 前最好把目标清零。

4.5 适合做什么

5 小米电机生态与国产开源方案

5.1 关于"小米 MotorOS"的核查说明

🔍 事实核查:网络上有"2025 年小米开源了名为 MotorOS 的 STM32 电机控制库"的说法。但经 web_search 多方核实(检索小米官方渠道、GitHub、开发者大会新闻),截至本文写作时,未检索到小米官方开源的 "MotorOS" 电机控制库的可核实资料。GitHub 上确实有名为 motoros 的仓库,但那是 Motoman 工业机械臂的 ROS 驱动,与小米无关。学习时请以小米官方开发者渠道和官方 GitHub 组织为准,警惕 AI 生成内容中的错误引用——本页不传播未经核实的"MotorOS"描述。

5.2 小米开源电机生态(可核实部分)

小米在电机与机器人方向确实有实打实的开源动作,最典型的是 CyberGear 微伺服电机(2023 年发布):这是一颗开源准直驱关节模组,把电机、减速器、编码器、驱动器集成一体,首发价约 499 元,通过 CAN 总线(1Mbps)通信,社区资料齐全,极大拉低了人形/四足关节的入门门槛。围绕它形成了丰富的国产开源生态:

5.3 其他国产与开源方案

5.4 MIT 开源方案:Mini Cheetah 的控制思路

MIT Mini Cheetah 是麻省理工的开源四足机器人:电机放在关节处的准直驱设计 + 高带宽力控,让它能跑、能跳、能后空翻。其开源控制栈包含状态估计、运动规划与关节力矩分配(虚拟模型控制 / 凸优化),社区实现如 CHAMP(MIT Cheetah I 的完整开源实现)把整套控制拆成可读的模块。学习它的意义在于:单关节 FOC 只是"肌肉",Mini Cheetah 展示的是"神经"——如何把整机目标(跑/跳)实时分配成每个关节的力矩指令。

✅ 生态小结:今天的开源世界,个人开发者完全可以复现顶级实验室的方案:SimpleFOC 学 FOC 原理、ODrive 做整机原型、CyberGear 做低门槛关节、MIT 开源控制栈学整机算法。选择取决于你的目标——学原理、做产品、还是做研究。

6 固件开发常见坑

🧯 六大坑位自检清单(踩过的人都会回来点赞):

这些坑基本都可以在硬件设计 + 固件防护两层同时设防:硬件上保证采样电路和死区电路正确,固件上把限流、欠压、过温保护写进主循环,任何一层兜住都不至于炸板。

7 本节自测

1. 关于 Park 变换在 FOC 中的作用,下列说法正确的是?
💡 解析:Clark 变换(三相→α/β)解决的是"三相互差 120° 不好处理"的问题;Park 变换(α/β→d/q)解决的是"交流量 PID 无法无静差跟踪"的问题——投影到与转子同步旋转的坐标系后,Id/Iq 近似直流,PID 才能精确控制。逆 Park 才是"旋转→静止",电压利用率与 SVPWM 有关,与 Park 变换本身无关。
2. 在 SimpleFOC 项目里,AS5600 磁编码器的 SDA/SCL 应该接到开发板的?
💡 解析:AS5600 是 I2C 磁编码器,必须接到硬件 I2C 的 SDA/SCL 引脚并共地;若用 ABI 增量编码器,则接支持编码器计数/中断的引脚(如 ESP32 的 GPIO 中断)。接线错误是最常见的"电机不动/校准失败"原因——先查接线,再查代码。
3. 用 odrivetool 控制 ODrive,校准完成后要让电机进入闭环运行,应该把 axis0.requested_state 设为?
💡 解析:FULL_CALIBRATION_SEQUENCE 只在首次校准(辨识编码器偏移/电阻/电感)时使用;正常运行时把 requested_state 设为 CLOSED_LOOP_CONTROL 即进入闭环,之后通过 input_pos / input_vel / input_torque 给定目标。IDLE 是待机,OPENLOOP_CONTROL 是开环。
4. 关于 FOC 相电流采样的正确做法是?
💡 解析:相电流采样必须与 PWM 同步——在中心对齐 PWM 的固定时刻(如定时器更新事件)触发 ADC,避开功率管开关瞬间的电流尖峰,才能采到真实相电流。这也是电机控制 MCU 强调"定时器触发 ADC 同步采样"的原因;取平均并不能消除开关噪声,反而引入误差。
5. 关于 FOC 闭环与编码器,下列说法正确的是?
💡 解析:Park 变换与逆 Park 变换都依赖转子电角度 θ,没有 θ 就无法确定力矩方向,只能开环「盲转」验证电机能否转(第 2.8 节)。增量式编码器便宜但上电不知道绝对位置、需要找零,所以关节类产品几乎都用上电即知位置的绝对值编码器;电角度 θ = 机械角 × 极对数,并非与机械角无关。
6. 关于 ESP32 与 STM32G431 在电机控制中的选型,下列说法正确的是?
💡 解析:第 1.1 节表格明确指出:ESP32 外设丰富但无 CAN 控制器(需外接)、ADC 精度一般,适合「学习 SimpleFOC、无线调试、原型验证」;而 STM32G431 是 Cortex-M4F @170MHz + FPU + CORDIC,带高级定时器(互补 PWM/死区)、内部比较器、运放、FDCAN,是「关节 FOC 主力」。选型要综合主频、外设、价格与生态,不能只看主频。
7. 关于 SVPWM 相比简单六步方波换相,下列说法正确的是?
💡 解析:第 2.7 节说明:SVPWM 用 8 个基本开关状态(6 个非零矢量 + 2 个零矢量)合成电压矢量,零矢量用于填充剩余时间 T0;其线性调制区最大相电压幅值可达 Vdc/√3 ≈ 0.577·Vdc,比六步方波等效的约 0.5·Vdc 提高约 15%,且电流更接近正弦、谐波小、转矩脉动小。
📌 本节要点速查:①FOC 流水线:电流采样 → Clark → Park → 双环 PID → 逆 Park → SVPWM,每个 PWM 周期执行一次;②选型:学习用 ESP32 + SimpleFOC,量产原型用 STM32G431(高级定时器 + FDCAN);③闭环离不开编码器(提供电角度 θ = 机械角 × 极对数),调参顺序是「先开环 → 再校准 → 后闭环」;④SVPWM 线性调制区最大相电压幅值约 0.577·Vdc,比六步方波高约 15%;⑤ODrive 是开箱即用高功率整板,用 odrivetool 校准后设 CLOSED_LOOP_CONTROL 进闭环;⑥六大坑:采样时序、死区、母线电压、编码器校准、CAN 帧格式、散热限流。
📌 本节小结:FOC 的本质是把三相交流电机放到旋转坐标系里当直流电机控制,完整流水线是"电流采样 → Clark → Park → 双环 PID → 逆 Park → SVPWM",每一步都有清晰的数学与代码对应;SimpleFOC 是学习这条流水线最快的入口(先开环、再校准、后闭环),ODrive 是开箱即用的高功率整板方案;国产生态里小米 CyberGear 把关节模组成本打到数百元,而 MIT Mini Cheetah 展示了从单关节力控到整机运动控制的开源路径。最后记住:采样时序、死区、母线电压、编码器校准、CAN 帧格式、散热限流,是固件开发的六大生死坑。
🤔 思考题: 1. 如果电机不接编码器,FOC 闭环能跑吗?能跑到什么程度?什么场景下"盲转"也够用?
2. 人形机器人 40+ 个关节如果全部用 ODrive 这类整板,会面临哪些系统级问题(成本、总线带宽、散热、体积)?
3. 假如你要自研一个关节驱动固件,会复用 SimpleFOC 的哪些模块(采样、变换、PID),又必须重写哪些(实时性、安全保护、通信协议)?

8 参考来源