首页 / 资讯中心 / 文章详情

STM32F427+C620/M3508:CAN与PWM控制、PID调参与避坑

STM32F427+C620/M3508:CAN与PWM控制、PID调参与避坑 ★ FEATURED ARTICLE
简介面向RoboMaster竞赛调试与STM32嵌入式控制学习的C620电调Demo程序说明文档围绕M3508电机在PWM与CAN两种模式下的驱动展开适合已具备Keil5和HAL库基础、希望理清电调接线、工程配置与控制逻辑的读者。压缩包仅含1个PDF文件约905KB内容集中便于随查随用。文档从RoboMaster主控板与STM32F427平台切入说明STM32CubeMX及F4固件包准备梳理三相线、7-Pin数据线、CAN/PWM信号线与24V电源的连接要点并强调两种信号线不可同时接入。程序部分给出FreeRTOS工程分层、BSP组中CAN滤波与收发、PID初始化与计算、StartDefaultTask控制代码以及CAN_CONTROL/PWM_CONTROL宏切换方式并对比CAN电流闭环与PWM转速闭环差异附按键调速流程和供电注意事项。已有3366人学习可作为电调联调与代码框架对照的参考资料。1. 从一份 C620 电调 Demo 说起两种控制模式差在哪手上有一块 STM32F427 的主控板、一只 C620 电调、一台 M3508 电机再配一份别人写好的 Demo 工程这是很多人第一次碰电机控制时的全部家当。翻车的场景也很统一编译通过、程序下载成功、按键按下去电机纹丝不动电调指示灯有规律地闪手边除了电调说明书什么都没有。这份 Demo 的价值不在代码多漂亮而在于它把 PWM 与 CAN 两条控制链路都摆出来了CAN 侧电调只做电流闭环转速环得自己在 STM32 里补一个 PIDPWM 侧电调内部已经做了转速闭环主控只要给出符合标准的脉宽就行。前者适合要精细控速、后面还要挂多台电机的场景后者适合先把电机转起来、验证硬件通路。搞清这个差异接线顺序和调参思路才不会互相打架。2. 环境搭起来CubeMX、HAL 固件包与 Keil5 工程生成整套工程跑在 Keil5 μVision 上主控 MCU 是 ST 的 STM32F427外设初始化代码由 STM32CubeMX 配合 F4 系列 HAL 固件库生成。这一层看起来只是装软件实际上后面九成的“生成出来编译不过”“找不到外设函数”都埋在这里。先说清楚为什么走这条路再把装包、生成、分组三个动作拆开。2.1 工具链选型CubeMX 生成 HAL 调用还是回去用标准外设库STM32F427 这颗片子上这套 Demo 要用到的外设不少CAN1 控制器、TIM5 的 PWM 输出通道、若干按键 GPIO、一个调试串口再加上 FreeRTOS 中间件。手写标准外设库初始化等于把时钟使能、复用功能映射、中断优先级分组、NVIC 使能逐条抄一遍漏一条就表现为“程序在跑但外设没反应”排查成本极高。HAL 库把这些封装成HAL_XXX_InitCubeMX 再把参数配置可视化代价是生成的代码体积大、调用层次深但对调试阶段来说这个代价换来的确定性很值。第二个理由是代码保护。CubeMX 生成的main.c、freertos.c、各外设的Msp.c里都有USER CODE BEGIN/USER CODE END注释块只要用户代码写在块内改完.ioc重新生成时不会被覆盖。这条规矩必须一开始就守住——见过太多次把业务逻辑写进USER CODE块外面改一个 CAN 波特率重新生成几百行控制代码直接归零。提示CubeMX 本体版本和 F4 固件包版本要保持配套。升级 CubeMX 后打开老.ioc常提示“固件包版本不匹配”此时别急着点升级先在旧工程里确认USER CODE区的位置和内容再决定要不要迁移。至于标准外设库F4 系列官方早已不再更新新拿到的板子和资料基本都是 HAL 生态跟着 Demo 走 HAL 是最省事的。常见的 STM32 软件、插件、固件包社区和各类中文入门站点上都能找到但版本混杂下面这一节专门说怎么装干净。2.2 固件包安装从本地 zip 导入别只装 CubeMX 本体装环境最容易踩的坑是以为装完 CubeMX 就万事大吉。CubeMX 本身只是个配置器真正的外设驱动代码在“固件包”Firmware Package里F4 系列对应STM32CubeF4那一包。包里才有Drivers/STM32F4xx_HAL_Driver和各类例程模板缺了它生成工程时外设初始化代码是空的。需要齐的组件如下表缺一个都会卡在不同环节组件作用缺失后的典型表现STM32CubeMX图形化配置、生成初始化代码无.ioc工程只能手搭JREJava 运行环境CubeMX 运行依赖双击无反应或启动即退出STM32CubeF4 固件包HAL 驱动与外设模板生成工程缺Drivers编译报未定义Keil MDK 器件支持包编译下载目标芯片Keil 里选不到 STM32F427装固件包的路径是固定的打开 CubeMX菜单Help→Install New Libraries→From Local选中下载好的 zip 包Open即可。装成功后在Help→Manage embedded software packages的列表里对应器件系列的版本号前面复选框会变成绿色。这一步是判断装没装上的唯一可靠依据比看目录文件更直观。装完顺手在终端里核一下仓库路径后面换机器、给同事复现环境都用得上# CubeMX 默认把固件包解压到用户目录下的 STM32Cube/Repository # Linux / macOS 用 $HOMEWindows 在 Git Bash 下同样可用 ls $HOME/STM32Cube/Repository # 期望看到形如 STM32Cube_FW_F4_Vx.x.x 的目录 # 进去确认关键子目录存在缺 Drivers 说明包不完整 ls $HOME/STM32Cube/Repository/STM32Cube_FW_F4_*/Drivers第一行的作用是定位仓库。CubeMX 安装本地 zip 时不是“注册一下”而是把压缩包解压到这个仓库目录所以目录存在且命名规范就代表包被识别了。第二行是完整性校验正常的 F4 包下面应当有STM32F4xx_HAL_Driver、CMSIS、BSP等子目录如果只有Projects而没有Drivers多半是下载到了例程包而非完整固件包生成代码时照样报错。注意只在 CubeMX 里勾选“在线安装”而不落本地包换一台没有外网的机器就彻底没法生成工程。手上常备一份 zip比每次联网下载稳得多。另外提醒一句 JRE。CubeMX 是 Java 写的机器上没装 JRE 或者装了但没进环境变量表现是双击图标闪一下就没了连报错窗口都不给。装完在命令行敲java -version能出版本号才算过关。2.3 .ioc 配置与工程分组FreeRTOS 和 BSP 组怎么落地.ioc里要配的东西不多但每一项都直接影响后面代码怎么写。按这份 Demo 的用法核心配置集中在四处系统时钟树、CAN1、TIM5 的 PWM 通道、按键 GPIO最后在中间件里勾上 FreeRTOS。几个关键参数按“够用且留余量”的原则给系统时钟HSE 外接晶振PLL 倍频到芯片允许的主频保证定时器时基算得准。CAN1波特率 1 Mbps打开自动总线关闭ABOM打开 RX FIFO0 消息挂起中断。1 Mbps 是这类电调的常用速率帧短、周期紧速率低了反馈会滞后。TIM5作为 PWM 输出时基通道 1 输出。时基要卡在电调能接受的频率区间内。按键 GPIO输入模式上拉或下拉按板子电路定别配成浮空。FreeRTOS启用保留默认任务控制逻辑就放在默认任务里。TIM5 的时基值得单独算一遍因为它决定了脉宽分辨率。若定时器时钟为 90 MHz预分频写 89则计数频率 1 MHz一个计数就是 1 μs自动重装值写 19999则周期 20000 μs即 50 Hz。此时比较寄存器 CCR 的值直接等于高电平微秒数CCR 1500就是 1.5 ms 高电平。这种“CCR 即微秒”的配置是刻意选的调参时不用再换算改一个数就是改多少微秒。# 生成工程后目录结构大致如下只列关键层级 Core/ Inc/ # main.h、FreeRTOSConfig.h、各外设 Msp 头文件 Src/ # main.c、freertos.c、stm32f4xx_it.c、各外设初始化 Drivers/ STM32F4xx_HAL_Driver/ # HAL 驱动CubeMX 自动拷入 CMSIS/ # 内核与器件头文件 Middlewares/ Third_Party/FreeRTOS/ # 实时内核源码与移植层 BSP/ # 自己建的组bsp_can.c/h、pid.c/h MDK-ARM/ # Keil 工程文件这份结构里BSP组是手动加的Demo 把bsp_can.c/h、pid.c/h放在这里与生成代码物理隔离。这个做法我一直在用CubeMX 管的目录一律不手改自己写的一律进BSP重新生成时只需在 Keil 里确认文件还在组里不会出现“代码被覆盖”这种不可逆事故。在 Keil 里加组的方法是右键工程目标 →Manage Project Items新建BSP组把两个.c文件添进去头文件路径在Options for Target→C/C→Include Paths里加上../BSP。加完编译一次确认没有报重复定义再往下写逻辑。3. 硬件接线CAN 与 PWM 两条链路的选择与互斥接线这一章看着简单实际上电调不响应、电机抖、主控板烧保护二极管大半出在这里。所有接线的总原则只有一条CAN 和 PWM 二选一另一路的信号线必须拔掉。这条不是建议是硬约束——两路同时给电调收到的指令来源不确定行为会完全失控。3.1 功率侧先接对三相线与 7-Pin 数据线C620 电调到 M3508 电机之间有两组线一组是三根三相输出线线上有颜色区分一组是 7-Pin 数据线黑色。三相线接错顺序电机不会立刻烧但转向会反表现为给定正转速指令电机反转此时不用改代码把三相线中任意两根对调即可。7-Pin 数据线是电调读电机霍尔和温度的通道这根没插好现象是电调能上电、但反馈全是零CAN 侧永远收不到有效转速。接线顺序上我习惯先把功率侧和电机侧全部接完、用扎带固定再动信号线。因为三相线是大电流路径接完再反复插拔容易碰到旁边的弱电针脚。3.2 CAN 链路2-Pin 接 CAN1终端电阻别忘CAN 控制方式的连线只有两根电调的 CAN 信号线2-Pin接到主控板的 CAN1 端口C620 电调上的 PWM 信号端口保持空置。这里有两个容易被忽略的点。第一是 CAN_H 和 CAN_L 不能反。反了之后总线静默用示波器看两条线都没有差分波形程序里HAL_CAN_AddTxMessage返回HAL_OK因为发送邮箱确实进去了但总线上没有任何应答电调毫无反应。判断方法是量两条线对地的静态电平正常情况下两者都在 2.5 V 附近反接时数值会明显偏离。第二是终端电阻。CAN 总线两端各需要一个 120 Ω 终端电阻短距离、两个节点的桌面调试常常不接也能通但一旦线拉长到半米以上或者节点变多就会出现偶发丢帧——表现是电机转速偶尔一顿。桌面验证阶段可以先用板载或外接的 120 Ω 顶上等上台架再按实际拓扑配。3.3 PWM 链路TIM5 通道与板上 C1~C4 端口PWM 方式下电调的 3-Pin PWM 信号线接到主控板上标注的 PWM 端口C1、C2、C3 或 C4CAN 信号端口空置。Demo 里用 TIM5 作为 PWM 信号源具体引脚在主控板上有明确标注接线前对着板子上的丝印确认一遍通道号别凭记忆插。PWM 侧的参数逻辑和 CAN 完全不同电调内部已经做了转速闭环主控给的是“目标转速”的占位信号高电平宽度决定转速。常见做法是 1 ms 到 2 ms 的高电平对应转速区间的一端到另一端1.5 ms 附近对应停止或中位具体以电调说明书为准。频率方面50 Hz 到 500 Hz 是这类电调普遍接受的范围Demo 的 50 Hz 属于下限优点是兼容性好缺点是每 20 ms 才更新一次指令动态响应偏慢如果只做按键调速这个刷新率完全够不必纠结。用前面算好的时基改转速就是改一个数__HAL_TIM_SET_COMPARE(htim5, TIM_CHANNEL_1, 1500)给 1.5 ms1600就是 1.6 ms。写代码时把这个值做成变量按键加减即可不用每改一次都重新算分频。3.4 供电与上电顺序动力电池还是电源箱电源线是 C620 电调上那根黑色线接主控板的 24 V 电源端口。检查完所有接线再上电顺序不能反先确认信号线只接了一路再给 24 V。这里有一条写进说明书的警告值得单独拎出来必须用动力电池供电。用实验室电源箱供电时电机在减速或堵转瞬间会产生反电动势能量回灌到 24 V 母线电压瞬间抬高超过主控板上电源保护二极管的耐压就会被击穿。击穿之后不一定会立刻冒烟常见表现是主控板还能跑程序但某一路供电异常或者干脆反复重启查半天查不到原因。注意上电前用手指把 CAN 和 PWM 两组信号线各摸一遍确认另一路确实是空的。这一秒钟的动作能省掉后面两小时的排查。4. 代码落地bsp_can、pid 与 StartDefaultTask 的骨架工程骨架搭好之后真正要写的代码只有三块CAN 的初始化与收发、PID 的参数与计算、控制任务里的模式切换与节拍。三块加起来不到两百行但每一块的参数含义都得说清楚否则调参时只能瞎试。4.1 bsp_can.c滤波器配置与收发时序CAN 部分负责两件事把电调反馈的转速帧收进来把电流指令帧发出去。滤波器的作用是只放行关心的 ID其余帧直接丢弃减少中断负载。这份 Demo 把滤波器放在BSP_CAN_Init里初始化接收动作放中断。/* bsp_can.c —— CAN1 初始化与收发骨架 */ #include bsp_can.h CAN_TxHeaderTypeDef tx_header; uint8_t tx_data[8]; uint32_t tx_mailbox; void BSP_CAN_Init(void) { CAN_FilterTypeDef filter {0}; /* 滤波器组 0只放行 0x201~0x204 这四个反馈帧 */ filter.FilterBank 0; filter.FilterMode CAN_FILTERMODE_IDMASK; /* 掩码模式 */ filter.FilterScale CAN_FILTERSCALE_32BIT; filter.FilterIdHigh 0x201 5; /* 标准帧 ID 左移 5 位对齐 */ filter.FilterIdLow 0x0000; filter.FilterMaskIdHigh 0x7FC 5; /* 低 2 位不比较覆盖 0x201~0x204 */ filter.FilterMaskIdLow 0x0000; filter.FilterFIFOAssignment CAN_RX_FIFO0; filter.FilterActivation ENABLE; HAL_CAN_ConfigFilter(hcan1, filter); /* 打开 FIFO0 挂起中断接收在中断服务里取走 */ HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); HAL_CAN_Start(hcan1); } /* 发一帧电流指令0x200 为广播帧4 台电调共用一帧 8 字节 */ void BSP_CAN_SendCurrent(int16_t i1, int16_t i2, int16_t i3, int16_t i4) { tx_header.StdId 0x200; tx_header.IDE CAN_ID_STD; tx_header.RTR CAN_RTR_DATA; tx_header.DLC 8; tx_data[0] (uint8_t)(i1 8); tx_data[1] (uint8_t)(i1 0xFF); tx_data[2] (uint8_t)(i2 8); tx_data[3] (uint8_t)(i2 0xFF); tx_data[4] (uint8_t)(i3 8); tx_data[5] (uint8_t)(i3 0xFF); tx_data[6] (uint8_t)(i4 8); tx_data[7] (uint8_t)(i4 0xFF); HAL_CAN_AddTxMessage(hcan1, tx_header, tx_data, tx_mailbox); }滤波器的两个参数最容易写错FilterIdHigh存的是标准帧 11 位 ID 左移 5 位后的高 16 位直接写0x201是匹配不上的掩码0x7FC表示 11 位 ID 中只有最低 2 位不参与比较因此 0x201 到 0x204 四个 ID 全部放行——这正好对应四台电调的反馈帧。改成一机一档时把掩码改成0x7FF就只收一个 ID。发送侧的0x200是广播帧8 字节拆成 4 个 16 位有符号数对应四路电流指令没接电机的通道填 0。电流值的量程由电调定义常见做法是 ±16384 对应满量程电流写超了会被截断所以 PID 输出必须先限幅再下发。注意HAL 的 CAN 接口在不同版本之间换过一套。老工程里常见HAL_CAN_Transmit()新版本是HAL_CAN_AddTxMessage()同一个工程里别混用混用会编译报重复符号或者运行期发送失败。接收中断里只做一件事把报文里的转速字段取出来存到一个全局变量供控制任务读取。别在中断里做 PID 计算也别在中断里打印前者会把节拍打乱后者会直接卡死。4.2 pid.c结构体、参数初值与限幅CAN 模式下转速环是必须自己补的因为电调只做电流闭环。PID 部分拆成结构体和计算函数两块结构体里除了三个参数还要存积分项、上次误差和限幅值这几个字段决定了积分饱和和输出越界能不能被压住。/* pid.c —— 位置式 PID带积分限幅与输出限幅 */ typedef struct { float kp, ki, kd; float err, last_err; float integral; float out_max; /* 输出限幅对应电流指令量程 */ float i_max; /* 积分限幅抗饱和 */ } PID_t; void PID_Init(PID_t *p, float kp, float ki, float kd, float out_max, float i_max) { p-kp kp; p-ki ki; p-kd kd; p-err p-last_err 0.0f; p-integral 0.0f; p-out_max out_max; p-i_max i_max; } float PID_Calc(PID_t *p, float set, float fdb, float dt) { p-err set - fdb; p-integral p-err * dt; /* 积分限幅防止长时间偏差把积分项推到饱和退饱和要很久 */ if (p-integral p-i_max) p-integral p-i_max; if (p-integral -p-i_max) p-integral -p-i_max; float d (p-err - p-last_err) / dt; p-last_err p-err; float out p-kp * p-err p-ki * p-integral p-kd * d; if (out p-out_max) out p-out_max; if (out -p-out_max) out -p-out_max; return out; }参数上dt必须和任务节拍严格一致任务 10 ms 跑一次就传 0.01传错了相当于把 Kp、Ki 整体缩放调半天调不出感觉。out_max按电流指令量程给先设成满量程的一半等响应稳定再放开。i_max一般取out_max的 0.3 到 0.5 倍太小会导致稳态误差压不下去太大则退饱和慢表现为超调后长时间回不来。积分项单独限幅这件事很多人图省事直接在输出端限幅了事。输出限幅只能拦住下发值拦不住结构体里的integral越滚越大等目标值一变积分项要花好几秒才退回来——这段时间电机是持续全速的很危险。4.3 StartDefaultTask宏切换、10 ms 节拍与按键主控制逻辑放在 FreeRTOS 默认任务的StartDefaultTask里两种模式靠宏切换。写的时候把宏定义放在文件顶部注释掉另一路别用#if嵌套判断容易两路同时生效。/* freertos.c —— 控制任务骨架 */ #define CAN_CONTROL /* #define PWM_CONTROL */ void StartDefaultTask(void *argument) { BSP_CAN_Init(); PID_Init(speed_pid, 8.0f, 0.5f, 0.0f, 8000.0f, 3000.0f); uint32_t tick osKernelGetTickCount(); for (;;) { #ifdef CAN_CONTROL /* CAN 模式自己跑转速环输出电流指令给电调 */ int16_t cur (int16_t)PID_Calc(speed_pid, target_rpm, feedback_rpm, 0.01f); BSP_CAN_SendCurrent(cur, 0, 0, 0); #elif defined(PWM_CONTROL) /* PWM 模式电调内部闭环这里只给脉宽 */ __HAL_TIM_SET_COMPARE(htim5, TIM_CHANNEL_1, pwm_pulse); #endif Key_Scan(); /* 按键改 target_rpm 或 pwm_pulse */ osDelayUntil(tick 10); /* 严格 10 ms 节拍抗累积漂移 */ } }osDelayUntil和osDelay的区别值得说一句后者是“从现在起等 10 ms”每次循环的执行时间会累积进去跑久了节拍就变成 12 ms、13 ms前者是“等到下一个绝对时刻”只要单次执行时间小于周期节拍就是严格的 10 ms。PID 的dt依赖这个稳定性所以这里用绝对延时。按键扫描放在任务里而不是中断里是因为要消抖、要处理长按放中断反而麻烦。按键只改target_rpm这个变量不直接改输出改动幅度建议做成小步长一次加几十转到几百转别一下从零跳到满速。5. 避坑与排查五条踩过的坑调试阶段遇到的现象多数能在下面五条里对上号。每一条都按“看到什么 → 为什么 → 怎么办”写照着比对就行。5.1 上电后电机不转电调指示灯规律闪烁现象程序下载成功按键能触发但电机纹丝不动电调上的指示灯按固定节奏闪。原因这类闪烁是电调的故障码最常见的是没收到有效控制信号——CAN 线没插好、CAN_H/CAN_L 接反、或者波特率配错。也可能是指令值一直是 0比如 PID 输出被限幅成 0或者目标转速变量压根没被按键改到。解决先用万用表量 CAN 两条线对地静态电平正常应在 2.5 V 附近再用调试器看target_rpm和 PID 输出是否真在变化最后核对.ioc里 CAN1 的波特率是否和电调一致。三步走完能定位到是硬件通路还是软件数值的问题。5.2 CAN 能发出去但收不到反馈现象HAL_CAN_AddTxMessage一直返回HAL_OK但接收中断从来不进feedback_rpm恒为 0。原因九成是滤波器写错。FilterIdHigh忘了左移 5 位、掩码给成0x7FF却写了广播 ID、或者FilterFIFOAssignment和中断里读的 FIFO 不是同一个都会导致帧被硬件直接丢弃。还有一种情况是HAL_CAN_Start在配置滤波器之前就调用了。解决把滤波器配置改回最简单的全放行掩码全 0先验证链路通不通通了再收紧掩码确认HAL_CAN_Start在HAL_CAN_ConfigFilter之后在中断入口加一个计数器看是不是压根没进中断。全放行能收、收紧后收不到问题一定在滤波器参数上。5.3 PWM 模式下电机啸叫、转速跳变现象PWM 方式驱动时电机能转但有明显啸叫转速时快时慢。原因三种可能。一是 PWM 频率太低50 Hz 下每 20 ms 才更新一次负载变化时响应跟不上二是脉宽值被别的代码意外改写比如同一路定时器通道被两个地方SET_COMPARE三是电源供不上母线电压跌落导致电调保护。解决先把频率提到 200 Hz 到 500 Hz 区间试啸叫通常会改善再全局搜SET_COMPARE确认只有一个地方写这个通道最后用示波器看母线电压波形有明显跌落就换供电方式。5.4 两路信号线同时插上行为完全失控现象电机突然全速转或者来回抽动按键完全不起作用。原因CAN 和 PWM 同时接入。电调对指令来源的判断不确定收到的指令相互冲突。解决立刻断电拔掉其中一路。这是硬件层面的互斥代码里加任何判断都救不了。养成习惯每次上电前检查一次信号线。5.5 用电源箱供电主控板上的保护二极管击穿现象调试一段时间后主控板异常程序还在跑但某路供电不对或者反复重启。原因电源箱不具备吸收反电动势的能力电机减速或堵转时能量回灌母线电压瞬间抬高超过保护二极管耐压。解决换成动力电池供电或者在电源端加足够的储能电容和泄放通路。已经击穿的二极管只能换板子这条属于不可逆损伤只能靠事前规避。6. 从“能转”到“转得稳”阶跃验证与调参顺序电机转起来只是开始能稳住才算调完。我一般不看“转得顺不顺”这种主观感受而是做一次阶跃验证给定一个转速目标用串口把target_rpm和feedback_rpm一起按 10 ms 周期打出来画成两条曲线看响应。这套方法不需要额外硬件串口助手把数据存成 CSV导入表格软件就能看。验证时先给 0再跳到 1000 rpm观察三个指标上升时间、超调量、稳态误差。上升时间看从跳变到第一次接近目标花了多久超调量看冲过头多少稳态误差看稳定后两条线还差多少。三个指标里超调是最危险的——转速冲得太高机械结构可能撞限位所以调参顺序永远是先压超调再谈速度。对着曲线调规律大致如下表曲线表现主要矛盾调整方向上升极慢始终追不上Kp 偏小逐步加大 Kp每次加 20%冲过头后缓慢回落积分饱和减小 i_max或先清零积分项稳态时目标附近小幅振荡Kp 偏大或 Kd 缺失减小 Kp加少量 Kd存在固定稳态误差Ki 偏小小幅增加 Ki注意配合 i_max指令一变就长时间全速积分项没限幅补上 integral 的上下限调 Kp 的时候有个手感每次加 20% 左右加到手感上“快到位了但还没冲”就停下然后加 Ki 消除稳态误差Ki 从很小的值开始比如 0.1观察稳态误差收敛速度最后加 Kd 压超调Kd 对噪声敏感反馈转速本身有量化误差时Kd 给大了会让输出高频抖动宁可少给。还有一个容易被忽略的细节反馈转速的单位。电调反馈的转速是 rpm 整数PID 里当浮点用没问题但如果做差分求导量化误差会被放大这也是我一般不急着上 Kd 的原因。验证做完把当次的参数、母线电压、负载情况记在工程目录的一个文本文件里。这份记录比代码本身值钱——换一台电机、换一块板子照着上一次的基线调能省掉大半时间。我早期调这类电调时吃过一次亏连续调了两小时感觉“差不多了”就收工第二天重新上电发现转速抖得厉害原因是前一晚最后一次改参数后没有重新做阶跃验证改的是哪一项自己都记不清了。从那以后每次动 PID 参数不管改多小都强制走一遍阶跃验证并记录结果改完不验证等于没改。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站