1. ODrive固件架构概览
ODrive作为一款高性能开源电机驱动器,其固件设计采用了模块化架构。v0.5.6版本固件主要包含以下核心模块:
- 电机控制环路(电流环/速度环/位置环)
- CAN通信协议栈
- USB接口处理
- PWM信号生成
- 编码器接口处理
- 系统状态机管理
整个工程采用C++编写,基于STM32 HAL库开发,通过面向对象的方式组织各个功能模块。代码仓库采用标准的ARM GCC编译工具链,开发者可以通过PlatformIO或Makefile进行构建。
提示:阅读源码前建议先熟悉STM32的定时器、ADC和DMA工作原理,这些是电机控制的基础硬件资源。
2. 主控制流程解析
2.1 系统初始化序列
系统启动后执行的主要初始化步骤:
- 硬件抽象层初始化(HAL_Init)
- 时钟树配置(SystemClock_Config)
- GPIO和外围设备初始化(MX_GPIO_Init)
- 电机控制外设初始化(PWM定时器、ADC、编码器接口)
- 通信接口初始化(USB、CAN、UART)
- 各功能模块对象实例化
- 安全监测系统启动
关键代码位于main.cpp的setup()函数:
cpp复制void setup() {
// 硬件层初始化
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM1_Init(); // PWM定时器
// 功能模块初始化
odrv.axis0_.encoder_.setup();
odrv.axis0_.motor_.setup();
odrv.axis0_.controller_.setup();
// 通信接口初始化
init_canbus();
usb_serial_init();
}
2.2 实时控制循环
主控制循环在main.cpp的loop()函数中实现,采用定时中断驱动:
- 1kHz中断触发电流环控制
- 10kHz中断触发PWM更新
- 后台任务处理通信和状态监测
电流环执行的典型时序:
cpp复制void current_loop_callback() {
// 1. 读取相电流(通过ADC)
float Ialpha, Ibeta = get_phase_currents();
// 2. Clarke/Park变换
transform_to_dq(Ialpha, Ibeta, &Id, &Iq);
// 3. PI控制器计算
Vd = current_controller_d.update(Id_setpoint - Id);
Vq = current_controller_q.update(Iq_setpoint - Iq);
// 4. 逆Park变换
transform_to_alpha_beta(Vd, Vq, &Valpha, &Vbeta);
// 5. 空间矢量调制(SVPWM)
update_pwm_duty(Valpha, Vbeta);
}
3. 电机控制核心模块
3.1 FOC控制实现
磁场定向控制(FOC)在motor_control.cpp中实现,关键组件包括:
- 电流采样与校准
- 坐标变换(Clark/Park变换)
- 空间矢量PWM调制
- 抗饱和PI控制器
电流采样采用同步采样技术,在PWM中点触发ADC转换:
cpp复制void ADC_IRQHandler() {
if (ADC_IT == ADC_IT_JEOC) {
phaseA_current = (ADC1->JDR1 * 3.3f / 4096 - offsetA) / gainA;
phaseB_current = (ADC2->JDR1 * 3.3f / 4096 - offsetB) / gainB;
}
}
3.2 位置/速度控制环
位置控制器在controller.cpp中实现,采用级联控制结构:
- 外环位置PID
- 内环速度PID
- 最内环电流PID
速度估算采用编码器差分法:
cpp复制float estimate_speed() {
float delta_ticks = current_ticks - last_ticks;
float delta_time = current_time - last_time;
return (delta_ticks / encoder_cpr) * (2 * M_PI) / delta_time;
}
注意:速度估算对编码器分辨率敏感,高CPR编码器需要优化差分算法以避免量化噪声。
4. 通信协议栈解析
4.1 CAN通信实现
CAN协议处理位于canbus.cpp,采用自定义协议格式:
code复制| 字节0 | 字节1-2 | 字节3-4 | 字节5-7 |
|-------|---------|---------|---------|
| 命令字 | 参数ID | 数据 | 保留 |
关键处理函数:
cpp复制void process_can_message(CAN_RxHeaderTypeDef* header, uint8_t* data) {
uint16_t cmd = (data[0] << 8) | data[1];
switch (cmd) {
case CMD_SET_POSITION:
controller.set_pos_setpoint(*(float*)&data[2]);
break;
case CMD_GET_STATE:
send_state_update();
break;
}
}
4.2 USB虚拟串口
USB通信采用CDC ACM协议,在usb_serial.cpp中实现数据分包处理:
cpp复制void usb_rx_callback(uint8_t* buf, uint32_t len) {
for (int i = 0; i < len; i++) {
if (buf[i] == '\n') {
process_command(rx_buffer);
rx_buffer.clear();
} else {
rx_buffer.push_back(buf[i]);
}
}
}
5. 关键算法实现细节
5.1 抗饱和PI控制器
在control.cpp中实现的改进PI控制器具有:
- 积分抗饱和
- 输出限幅
- 动态调节功能
核心算法:
cpp复制float PI_Controller::update(float error) {
// 比例项
float output = error * kp;
// 积分项(带抗饱和)
if (!(output >= max_output && error > 0) &&
!(output <= min_output && error < 0)) {
integrator += error * ki * dt;
}
// 输出限幅
output = constrain(output + integrator, min_output, max_output);
return output;
}
5.2 编码器校准流程
编码器校准在encoder.cpp中实现,包含以下步骤:
- 电机预定位到已知角度
- 采样多圈原始计数值
- 计算偏移量和方向
- 验证校准结果
校准函数示例:
cpp复制bool Encoder::calibrate() {
// 移动到零位
motor.set_position(0);
// 采样编码器值
int32_t readings[128];
for (int i = 0; i < 128; i++) {
readings[i] = get_raw_count();
delay(10);
}
// 计算平均值和方差
calc_offset_and_variance(readings);
return variance < MAX_ALLOWED_VARIANCE;
}
6. 安全与异常处理
6.1 故障监测系统
故障检测机制包括:
- 过流保护(硬件比较器+软件校验)
- 温度监测(NTC电阻)
- 通信超时检测
- 编码器异常检测
故障处理状态机:
cpp复制void check_protections() {
if (current > MAX_CURRENT) {
enter_error_state(ERROR_OVERCURRENT);
} else if (motor_temp > MAX_TEMP) {
enter_error_state(ERROR_OVERTEMP);
} else if (encoder_error_count > MAX_ERRORS) {
enter_error_state(ERROR_ENCODER);
}
}
6.2 看门狗系统
独立看门狗(IWDG)和窗口看门狗(WWDG)双重保护:
cpp复制void init_watchdogs() {
// 独立看门狗(基础保活)
IWDG->KR = 0xCCCC;
IWDG->KR = 0x5555;
IWDG->PR = 4; // 约1s超时
// 窗口看门狗(时序敏感任务)
WWDG->CFR = WWDG_CFR_WDGTB1 | WWDG_CFR_W_6_0;
}
7. 开发与调试技巧
7.1 实时数据记录
通过USB高速记录调试数据:
cpp复制void log_debug_data() {
static float log_buffer[LOG_CHANNELS];
log_buffer[0] = motor.current.q;
log_buffer[1] = motor.current.d;
log_buffer[2] = encoder.pos_estimate;
usb_serial_send((uint8_t*)log_buffer, sizeof(log_buffer));
}
7.2 参数调优方法
PID调参建议流程:
- 先调电流环(最高带宽)
- 再调速度环(带宽为电流环1/5-1/10)
- 最后调位置环(带宽为速度环1/3-1/5)
典型参数范围:
| 控制环 | 比例系数 | 积分系数 | 微分系数 |
|---|---|---|---|
| 电流环 | 0.1-1.0 | 100-1000 | 0 |
| 速度环 | 0.01-0.1 | 1-10 | 0.001 |
| 位置环 | 1-10 | 0 | 0.1-1 |
8. 关键代码文件说明
8.1 主要源文件结构
code复制/firmware
├── main.cpp # 主控制循环
├── motor_control.cpp # FOC实现
├── controller.cpp # 位置/速度控制
├── encoder.cpp # 编码器接口
├── canbus.cpp # CAN协议栈
├── usb_serial.cpp # USB通信
└── utils
├── filters.cpp # 滤波器实现
└── pid.cpp # PID控制器
8.2 重要头文件定义
odrive_constants.h包含关键参数定义:
cpp复制// 电机参数
#define POLE_PAIRS 7
#define PHASE_RESISTANCE 0.1f
#define PHASE_INDUCTANCE 0.0001f
// 保护阈值
#define MAX_CURRENT 30.0f
#define MAX_VOLTAGE 24.0f
9. 编译与烧录指南
9.1 开发环境搭建
推荐工具链配置:
- ARM GCC 9-2020-q2-update
- OpenOCD 0.10.0
- STM32CubeProgrammer
PlatformIO配置示例:
ini复制[env:odrive]
platform = ststm32
board = genericSTM32F405RG
framework = stm32cube
upload_protocol = stlink
9.2 固件烧录步骤
- 连接ST-Link调试器
- 进入DFU模式(BOOT0拉高)
- 使用STM32CubeProgrammer擦除芯片
- 烧录生成的
odrive.elf文件 - 验证校验和
提示:首次烧录后需执行参数初始化命令
odrv0.erase_configuration()
10. 性能优化技巧
10.1 中断优先级配置
关键中断优先级设置(数值越小优先级越高):
| 中断源 | 优先级 | 说明 |
|---|---|---|
| PWM定时器中断 | 0 | 电流环控制 |
| ADC中断 | 1 | 电流采样 |
| USB中断 | 3 | 通信 |
| CAN中断 | 2 | 实时控制指令 |
10.2 内存优化策略
- 将频繁访问的变量放入CCM RAM
cpp复制__attribute__((section(".ccmram"))) float current_loop_buffer[128];
- 使用DMA减轻CPU负担
cpp复制void init_adc_dma() {
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
HAL_DMA_Init(&hdma_adc1);
}