news 2026/9/7 11:24:28

固件人机界面实战:基于STM32与OLED的PID调参菜单设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
固件人机界面实战:基于STM32与OLED的PID调参菜单设计

做电控项目做到一定阶段,你就会发现真正卡住进度的往往不是控制算法本身,而是调试手段。这个系列写到第7期,前几期我们把硬件底板、功率驱动、编码器采集、FOC电流环这些都铺开了,速度环和位置环的PID框架也搭好了,按理说可以开始整定了。可真到了整定那天,我对着板子愣了半天:参数调来调去只能靠改代码重烧,看波形得抱着电脑蹲在台架旁边,设备装到机架上之后更是没法随时连串口。缺的不是控制理论,而是一个能让我在现场直接看数据、直接改参数的固件人机界面。

所以这一期我不讲PID怎么整,专门讲讲“整定之前,先给固件长出人机界面”这件事。这个界面不一定要多好看,但它必须能帮我把“改参数、看响应、调策略”的闭环从电脑搬到设备本体上。文章会从方案选型、菜单结构设计、刷屏与主循环的配合、参数掉电保存这些角度展开,最后把我实机调试时踩过的几个坑完整梳理一遍。如果你也在做电机控制、运动控制或者任何需要PID整定的固件项目,这篇应该能省下你不少弯路。

1. 光有算法还不够,整定最缺的是“现场看得到的手”

先说结论:给固件加人机界面,不是锦上添花,而是整定流程能不能顺畅跑起来的前提。

很多人觉得整定就是拿个串口助手,发几个命令看返回曲线,然后把PID参数填进去就行。这个思路在实验室里确实成立,可一旦设备装上机器、离开工位,问题就来了。我之前做云台稳像项目时,电机和驱动板装在云台内部,整定需要在云台实际负载下进行。每次改参数都要拆开外壳、连上调试器、重新编译烧录,再把云台装回去上电观察。改一个Ki,整个流程走下来十分钟起步,而且改完之后手感对不对全凭感觉。调了半天,大部分时间浪费在“把参数送进固件”这个动作上,而不是真正观察系统响应。

还有一类做法是依赖上位机调参软件,通过串口、CAN或者无线通道实时改参数、看曲线。这套方案功能强,但有一个绕不开的问题:通信链路本身就是个变量。波特率、协议版本、帧格式、固件与上位机的匹配,任何一个环节出问题,整定工作就变成调试通信了。我自己就遇到过固件升级后上位机还认旧协议,结果发过去的参数被解析成乱码,电机当场冲转的情况。

人机界面解决问题的本质,是把调试手段从外部工具收回到设备本身。OLED屏幕加一个旋转编码器,成本不高,但从此参数调整、状态监控、手动输出测试都能在设备跟前完成,不需要任何外部依赖。而且当系统跑起来之后,屏幕上实时刷新的速度环输出、电流、误差这些数据,本身就是最好的整定反馈。整定这个事,讲究的是“改一步看一步”,界面能让你站在设备旁边,手里握着旋钮,眼睛盯着屏幕上的数字变化,这种调试节奏比任何上位机都直接。

这一期我是这么定位的:以STM32F103C8T6主控、I2C接口OLED屏幕、旋转编码器按键为例,说清楚固件里的人机界面从零到一怎么搭。整个过程不需要引入RTOS,裸机主循环加中断就能搞定,所以哪怕你的项目还在用最基础的单片机开发方式,这套思路也完全适用。

2. 三条路线对比:屏显菜单、串口命令行、无线调试页

决定做固件人机界面之后,先别急着买屏幕,第一步是选交互载体。交互载体决定了整个界面的开发模式和使用场景,选错了后面返工成本很高。我把常见的方案分成三条路线,实际对比过之后才做的决定。

2.1 方案A:本地屏显菜单,推荐首选

本地屏显是我最终采用的方案,具体是0.96寸I2C OLED加一个带按键的旋转编码器。交互逻辑参考了老式数控机床的操作面板:主界面显示运行状态,单击进入参数菜单,旋转编码器选择参数和调整数值,长按返回。屏幕分辨率低,不需要图形界面,但胜在实时、直观、零依赖。

这个方案的核心优势是调试效率。改P、I、D参数的时候,编码器转一下就是0.1的步进,按下确认键立刻写入RAM生效,屏幕下一秒就显示新的输出响应。每次拉起整定工艺,手都不用离开设备。设备装在机架上,或者放上老化台连续跑测试,我也能直接看屏幕上的状态,不用一直连着一根串口线。

成本上,OLED屏和编码器加起来十几块钱,对于一个已经在做的控制板来说基本可以忽略。开发量大约三到四个工作日。风险主要在OLED刷新和I2C通信对控制时序的影响,这个我在后面第4节和第6节会专门展开。

2.2 方案B:串口命令行,开发最快但留作备用

串口命令行是很多固件项目默认的选择,因为实现成本最低:初始化一个USART,写一个字符串解析函数,把“P=12.5”这样的命令解析成结构化数据,再回显结果就行。我项目里也留了一个命令行入口,作为调试后门,专门处理那些屏幕上不方便展示的深层配置,比如PID参数限幅值、编码器方向、电流环带宽标定系数。

这个方案的麻烦在于它还是一条“线”。我拿它做主力调试的时候,总觉得被串口线的长度束缚着,而且串口工具动不动就丢帧、乱码,处理起来很烦。后来我把命令行降级为辅助手段,只有在线升级参数版本或者批量写入配置的时候才用。开发量一两天,适合作为其他方案的补充。

2.3 方案C:蓝牙或WiFi无线调试页,远程能力强但复杂度高

如果用ESP8266或蓝牙模块做一个网页调试页,手机扫码就能打开,远程看着波形、拖拽滑杆调参,确实很酷。这个方案的难点在于通信协议、帧校验、断线重连、安全鉴权这些链路问题一拥而上,本来就还没整定完的参数又多了一堆网络变量。

我在另一个项目里试过把ESP8266的AT固件接到STM32上,通过TCP转发串口数据,网页端用Canvas画曲线。花了大概两周把Demo跑通,但稳定性一直不理想:WiFi信道干扰、DHCP租约到期、连接掉线后固件端的状态机恢复,这些都要处理。对本期“整定之前先有界面”的目标来说,这条路线过重了,我暂时没把它列为主方案。

三条路线的对比如下:

路线开发量外部依赖适用场景主要风险
本地屏显菜单3-4天现场调参、设备装机的全场景OLED刷新占用主循环资源
串口命令行1-2天串口线、电脑批量配置、参数导入导出通信异常、现场不便连线
无线调试页两周以上WiFi/蓝牙、手机远程调试、批量设备管理链路不稳定、开发周期长

所以我的最终选择是本地屏显为主、串口CLI为辅。屏幕负责日常整定的高频交互,串口CLI负责那些低频但需要精确输入的批量操作,两条通道互补,哪个都不冲突。

3. 界面长什么样:菜单分三层,按键逻辑一次讲清

方案定了之后,最核心的工作是设计菜单结构和交互逻辑。界面设计的目标用户不是消费者,而是我自己——一个需要在现场快速操作、眼睛不用盯着屏幕太久就能读完信息的调试者。所以设计原则是信息密度优先,交互路径越短越好。

3.1 第一层主界面:状态实时刷新,一眼读完核心数据

主界面显示的内容直接决定你整定时能不能“看得到”。我设计的主界面结构是一个固定的信息面板,从上到下依次是:当前运行模式(速度环/位置环/手动)、目标值、实际反馈值、误差绝对值、PID三项各自的实时贡献值、最终输出占空比。

屏幕分辨率128x64,一屏刚好能放下这些信息。关键数字用大号字体,次要信息用小号字体压缩在角落。比如整定速度环时,我最关心的是“实际转速跟不跟得上目标值”,所以目标转速和实际转速用大字排在一起,误差显示在后面;P、I、D三个贡献值用小字排在底部,用来判断当前到底是哪个环节在起作用。

刷新频率设计在10Hz。为什么不是越快越好?因为人眼对超过10Hz到15Hz的数字刷新已经不太敏感了,而且OLED的I2C带宽有限,刷新太快反而会把宝贵的总线时间占用掉。10Hz对读取数字、观察趋势来说刚好,刷屏功耗也低。这个频率是在实机上试出来的,一开始跑的20Hz刷得眼花,改成10Hz反而更舒服。

3.2 第二层参数菜单:旋转编码器调速,确认即时生效

参数菜单是整个界面最核心的部分。这里需要明确一个设计决策:参数修改是“即时生效”,而不是“改完还要按保存才生效”。整定过程中,我经常需要连续试几个Ki值,观察系统是震荡还是收敛,如果每次改完都要弹一个确认框,调试节奏就被打断了。所以点进参数菜单后,当前参数高亮显示,旋转编码器左右转就调整数值,数值在RAM里的PID结构体里立刻更新,控制环路下一个周期就会使用新值。屏幕上的数字跟着变,系统响应也跟得上,这是整定体验提升最关键的一步。

参数菜单本身做了两级划分:第一级是参数组选择,比如速度环PID组、位置环PID组、电流环限幅组;第二级是组内的具体参数项,比如P、I、D、积分限幅、输出限幅。用旋转编码器按下切换到下一级,长按返回上一级。不再需要第四层了,三层的深度对现场调试来说刚刚好,太深了反而容易按迷失。

3.3 手动输出模式:整定之前的开环探路

主界面和参数菜单解决“调参”的问题,但整定之前还有一个环节需要人机界面支撑,那就是手动开环测试。在PID参数还没有任何参考值时,我习惯先用固定占空比驱动电机,确认电机方向、编码器极性、电流反馈方向这些基础配置是否正确。这个测试以前靠改代码完成,现在直接在手动模式下用旋钮调整输出电压的PWM占空比,或者直接给一个目标转速让系统开环跑,观察编码器反馈是否正确。

这个模式还有一个用途,就是整定过程中遇到异常(比如电机尖叫、电流过大),第一时间切到手动模式,把占空比拉到0,让系统安全停下来。屏幕上有专门的急停按键提示,长按确认键三秒即切断所有输出,比操作电脑快得多。

3.4 菜单状态机的代码骨架

菜单切换用状态机实现。这里给出一个简化版框架,完整代码在项目仓库里:

typedef enum { UI_STATE_MAIN, UI_STATE_PARAM_GROUP, UI_STATE_PARAM_ITEM, UI_STATE_MANUAL_OUT } ui_state_t; static ui_state_t g_ui_state = UI_STATE_MAIN; static int8_t g_menu_cursor = 0; static int32_t g_encoder_acc = 0; void ui_on_encoder_delta(int32_t delta) { g_encoder_acc += delta; } void ui_on_button_event(button_event_t evt) { switch (g_ui_state) { case UI_STATE_MAIN: if (evt == BUTTON_SHORT_PRESS) { g_ui_state = UI_STATE_PARAM_GROUP; ui_draw_menu_group(g_menu_cursor); } else if (evt == BUTTON_LONG_PRESS) { g_ui_state = UI_STATE_MANUAL_OUT; ui_draw_manual_mode(); } break; case UI_STATE_PARAM_GROUP: if (evt == BUTTON_SHORT_PRESS) { pid_group_t *grp = &g_pid_groups[g_menu_cursor]; g_ui_state = UI_STATE_PARAM_ITEM; grp->item_cursor = 0; ui_draw_param_item(grp); } else if (evt == BUTTON_LONG_PRESS) { g_ui_state = UI_STATE_MAIN; ui_draw_main(); } break; default: break; } } void ui_poll(void) { if (g_encoder_acc) { pid_param_adjust_by_cursor(g_encoder_acc); g_encoder_acc = 0; ui_refresh_partial(); } }

这段代码的核心思想是:旋转编码器的增量先累加到一个变量里,主循环轮询的时候一次性消费,避免在中断里直接操作参数结构体造成数据竞态。按键事件类似,中断或扫描函数只记录事件,真正的状态切换放在ui_poll主循环里做。这个模式能保证控制环路的中断优先级始终高于UI逻辑,后面第4节会详细解释为什么这个优先级必须守住。

4. 让屏幕不拖后腿:刷屏与主循环的协作方式

OLED屏幕接在I2C总线上,而I2C通信是典型的阻塞型操作。如果直接在控制ISR里调用OLED刷屏函数,或者让刷屏逻辑占用了主循环的绝大部分时间,控制环路的实时性就会被打穿。整定的时候最怕的就是系统响应数据里掺入“非控制因素”造成的毛刺,你分不清到底是参数调得不对,还是屏幕把时序搞乱了。

4.1 控制中断与UI刷屏的优先级划分

我的做法是把整个系统分成三个优先级层。第一层是定时器触发的控制ISR,负责编码器采样、电流环计算、速度环计算、PWM输出更新,这个中断固定1kHz频率,不允许被任何UI操作阻塞。第二层是主循环,负责处理UI事件、菜单切换、参数更新、Flash读写。第三层是低优先级的刷屏操作,只在主循环的末尾或者空闲的时候执行。

具体到实现上,控制ISR只做一件事:把当前时刻需要展示的“快照”拷贝到一个全局结构体里。主循环读这个快照去刷新UI,而不去直接读控制环路的实时变量。这样即使屏幕刷新得很慢,也不会影响控制ISR取最新数据的时序。快照结构体的访问在主循环端关中断保护一下,避免读到一半数据被更新造成显示错乱。

4.2 OLED刷屏的两种优化手段:局部刷新和双缓冲

标签从底层上诊断了“上传下载”环节,而“下载”模式天然地引导用户在手机/PC网盘保存资源;再者,它回避了“资源上传”的法律风险,使产品模式更稳妥。理论上可迁移至任何以简单“上传/分享”为价值主张的模块化工具。

I2C OLED刷整屏需要发送1024字节的数据,在400kHz的I2C频率下大约要25毫秒。如果每帧都整屏刷新,主循环基本被吃掉了。所以我做了两个优化。

第一是局部刷新。界面里真正变化的内容只有四个数字区域和一个状态图标,我把这些区域标记为脏矩形,只重新发送对应行列的显存数据。OLED驱动IC(比如SSD1306)支持页寻址模式,你可以只更新某一页的某些列,这个功能很好用。实测局部刷新一帧只发送几十字节,耗时不到2毫秒,主循环的负担几乎可以忽略。

第二是双缓冲。显存里维护一个RAM副本,所有绘图操作先在副本上进行,完成后一次性提交到屏幕。这样做的好处是画面不会出现“先画了一半数字、再画另一半”的撕裂现象,而且绘图逻辑和I2C发送逻辑可以彻底解耦。刷屏任务只在主循环里跑,控制ISR永远不需要等待I2C总线。

4.3 动态曲线的绘制方案

主界面的数字刷新解决“看当前值”的问题,但整定还需要“看趋势”——比如给一个阶跃目标后,转速的响应曲线是超调还是爬升。OLED空间有限,我用一个垂直滚动条代替完整波形:屏幕右侧画一条32像素宽的纵向区域,用最近N个周期的误差值映射为不同高度的柱状条,像一个小型示波器。

绘制时先把柱状条画进双缓冲显存,然后整列提交。实际效果是在屏幕一角能看到误差的“尾巴”是长是短,超调的时候柱子明显冲过高亮阈值。这个设计开发量不大,但对整定的帮助非常大,尤其是判断P值增大后系统是否接近临界振荡,一眼就能看出来。

4.4 实测验证:画图不影响控制周期

做完整套机制之后,我用逻辑分析仪抓过控制ISR的实际执行间隔。抓取方式是在ISR入口翻转一个GPIO,然后观察翻转信号的周期抖动。结果显示,无论屏幕是否在刷新,ISR周期始终稳定在1kHz,抖动在微秒级别。这才是“人机界面不影响控制”的实证,也是整定数据可信的前提。如果你发现刷屏后控制波形变脏,先检查是不是把I2C调用直接塞进了ISR,这个坑我见过太多次。

5. 参数怎么存:Flash掉电保存的实用做法

整定过程中调好的PID参数如果只存在RAM里,一掉电全没了,下次开机又得从头调。所以人机界面上必须有一个“保存参数”的入口,把参数固化到MCU的Flash里。这里有不少细节,做不好会出现参数写坏、启动异常这类非常隐蔽的问题。

5.1 参数块结构体:不是简单把数据扔进Flash

直接定义一个结构体然后memcpy到Flash是最朴素的做法,但不推荐直接这么干。固件升级之后,结构体字段可能变了,旧参数被新固件读出来,解释成完全不同的含义,轻则参数异常,重则上电就冲转。我的做法是在参数块前面加一个标志头和版本号,尾部加CRC32校验:

#define PID_PARAM_MAGIC 0x50494431 // "PID1" typedef struct { uint32_t magic; uint32_t version; float kp_speed; float ki_speed; float kd_speed; float kp_pos; float ki_pos; float kd_pos; uint16_t output_limit; uint16_t integral_limit; uint8_t reserved[8]; uint32_t crc32; } __packed pid_param_block_t;

__packed防止编译器在结构体里插入填充字节,保证Flash里存储的字节布局和程序里读出来的完全一致。CRC32的作用是在加载参数时校验数据完整性,只要有任何一位不对,立即放弃这组参数,回到默认值。magic和version的作用是区分“这块Flash里到底存没存过参数”以及“存的参数是哪一版的”。这三个字段配合,基本杜绝了参数错乱的情况。

5.2 写入时机:确认保存才写,不能每次调整都写

Flash的擦写寿命是有限的,STM32F103的内部Flash标称擦除次数在1万次左右,虽然实际上往往能扛住更多,但整定过程可能连续调整几百次参数,如果每次转动编码器都往Flash里写,很快就把Flash磨坏了。所以保存参数必须是一个独立的、显式的动作:在参数菜单里设置好所有参数后,最后按“保存并退出”,固件才执行一次Flash写入。

我的用途上还加了“断电自动保存”的思路:用一个大电容给MCU供电保持短时间,检测到掉电事件后把RAM里的最新参数写入Flash。这个方案并非所有场景都适用,但一些关键项目可以采用,能避免忘记保存导致参数丢失。缺点是硬件成本增加,而且需要在掉电检测ISR里处理Flash擦除,逻辑更复杂。本期还是以“手动确认保存”为主,更可控。

5.3 内部Flash的写入流程与中断控制

STM32内部Flash写入的关键在于:写入之前必须先擦除所在扇区,而且擦除过程中Flash模块会暂停CPU取指令,但这个暂停时间在STM32F103上通常是毫秒级别,对于1kHz控制中断来说还是可能造成“一次性卡顿”。我在实机上碰到过写参数时电机“咯噔”一声的情况,原因就是擦除Flash期间控制中断被延迟太久,导致PWM输出有一个短暂的空档。

我的解决方式是把“保存参数”这个动作放到控制暂停状态下完成。具体流程是:

  1. 按下保存按键后,先置位sys_suspend_flag,控制ISR看到这个标志后暂停PID输出,把PWM占空比强制置为0,等待电机停稳。
  2. 主循环执行Flash解锁、擦除指定扇区、写入参数块、上锁、读回校验。
  3. 校验通过后清除suspend_flag,控制ISR恢复PID计算和PWM输出。

整个过程大约几十毫秒,电机只是轻微停顿一下,但不会出现电流冲击。这个短暂的“断电式保存”体验是值得的,因为换来的是参数可靠性和Flash寿命。

5.4 加载参数:上电时的默认值与异常回退

上电时固件读取Flash参数块,先检查magic、version和CRC32,全部通过才把这些参数加载到RAM。任何一个检查失败,都说明Flash里的参数不可信,这时加载默认PID参数,同时在屏幕上显示一个“PRM ERR”警告,提醒操作人员参数没有加载成功。

另外还有一个细节:升级固件版本后,如果结构体版本号不变,旧参数继续沿用;如果结构体版本号变了,比如新增了一个限幅字段,旧参数块虽然CRC能对上,但字段含义不再匹配,所以版本检查要优先于CRC检查。这个顺序不能反,因为结构体变化后CRC值本身就不一样了,必须先靠版本区分新旧格式。

6. 实测中踩过的四个坑及完整排查链路

界面和存储逻辑写完后,我在真实板子上调了一整天。这一天踩了好几个坑,每一个都很有代表性,单独拿出来讲清楚排查过程,比直接给结论更有价值。

6.1 坑一:OLED刷新时电机声音发闷,速度波形出现毛刺

现象是屏幕每刷新一次,电机运转的声音就跟着变一下,轻微的“嗡嗡—哒—嗡嗡”节奏变化,通过速度反馈波形能看到规律性的毛刺。

我第一反应是I2C总线时序影响了编码器采样,于是用逻辑分析仪同时抓了I2C SCL、控制ISR翻转的GPIO和电机电流反馈。抓到之后发现问题不在I2C总线本身,而是我把一部分用来刷新屏幕的耗时操作放在了主循环里,而主循环里还有其他任务,比如编码器溢出处理、通信报文解析,它们都抢CPU时间。刷新屏幕时主循环卡了几毫秒,这期间如果有编码器更新事件或者通信中断到达,响应就延迟了。速度环的积分项对更新延迟特别敏感,一次几毫秒的延迟就会在输出上留下一个毛刺。

解决方式是把所有UI操作彻底从主循环的时间关键路径上移出去,同时把刷屏频率从20Hz降到了10Hz,速度波形立刻干净了。这里的一个教训是:控制周期能不能保持稳定,不能靠“感觉”,一定要用逻辑分析仪或者示波器看GPIO翻转信号,数字才是唯一的依据。

6.2 坑二:旋转编码器快速转动时参数跳变严重

现象是编码器往一个方向快速旋转,参数数值本该线性增加或减少,但有时候会突然跳好几个单位,甚至出现回退。我一开始以为是编码器型号问题,换了一个带定位槽的还是如此。

用示波器同时抓编码器A相、B相信号,能清楚看到在旋转过程中两路信号上叠加了很多毛刺,尤其是在慢速旋转和刚启动的一瞬间,机械触点抖动导致电平反复跳变。常规的“上升沿触发计数”在这里根本不够用。

解决方式是三管齐下:硬件上加RC低通滤波把毛刺削掉一部分,软件上把编码器A、B相接在定时器的正交编码器模式引脚上,让硬件定时器负责方向和计数,MCU只读取定时器的计数值。正交解码模式天然会把机械抖动当成无效状态过滤掉,配合定时器的硬件滤波功能,计数就非常稳定了。关键是最终把编码器解析从软件中断移到硬件外设,让最底层的精度问题不再消耗CPU精力。

6.3 坑三:保存参数时电机“咯噔”一下

这个现象在第5.3节提到过,完整的排查链路是这样的:第一次出现时我怀疑是PWM模块在Flash操作期间异常复位,于是查了PWM寄存器、看门狗配置,都没发现问题。后来我把一个GPIO在保存参数期间拉高,用示波器观察GPIO电平和PWM输出波形的关系,才确认是Flash擦除期间控制ISR的响应时间被拖长了。

为什么会被拖长?STM32内部Flash擦除时,Flash控制器会阻塞CPU对Flash的访问,而控制ISR的代码本身也在Flash里执行,虽然中断可以继续响应,但取指会被卡住,ISR的实际执行时间被拉长。解决方式不再重复,核心就是“保存时先暂停控制输出”,从机制上避免短时失控。

这里想多说一句:遇到控制异常时,不要急着怀疑芯片坏或者编译器出问题,先用示波器把“什么时候发生的”和“当时其他信号什么样”对应起来,90%的嵌入式问题都能靠这个办法定位。

6.4 坑四:参数保存后重启,读出来的P值和调好的不一致

现象是参数明明保存成功了(屏幕上显示“SAVE OK”),但重新上电后屏幕显示的P值跟保存时差了一点点,比如12.3变成了12.299999。排查到最后发现,这是浮点数在Flash里以二进制存储、读出后显示时精度损失导致的“看起来不对”。12.3这个十进制小数在二进制浮点里本身就是无限循环小数,存进去再读出来,打印的时候做了四舍五入就会看到尾数抖动。

这个其实不算功能故障,但会让操作者怀疑参数没保存对。解决方式有两个层面:显示时用%.1f格式只保留一位小数,这是表面解决;存储时用定点数代替浮点数,比如把12.3存成123,单位0.1,彻底消除格式转换的歧义。我后来把PID参数全部改为定点存储,内部计算时转成float算,保存和显示都用int,这套组合再没出现过尾数问题。

7. 界面长出来后,整定的工作流彻底变了

人机界面上线之后,最直观的感受是:整定从“一次性集中干完”变成了“随时想调就调”。以前调速度环,需要专门腾出半天时间,打上调试器,在电脑前守着串口工具;现在把台架往面前一放,上电,屏幕亮起,旋钮一转,P从0开始往上加,电机的响应手感就在手上。每一次调整的反馈时间从“重新烧录一次”缩短到“下个控制周期”,整定的效率提升不是百分之几十,而是数量级的变化。

整个流程现在是这样跑的:开机后屏幕显示主界面,先切到手动模式,开环给一个固定占空比,确认电机旋转方向和编码器方向一致。然后切回速度环,P值从0慢慢增加,每加一档,给一个阶跃目标,观察屏幕上的转速跟踪曲线和右侧的误差柱。等P值接近临界振荡,再逐步加I,观察静差是否消除,最后加D压超调。调完一组之后,长按保存,参数进Flash,算完成一次整定。

这套流程不再依赖电脑,设备在什么环境就能在什么环境调。后来我把同样的交互套用到位置环和电流环上,菜单结构不需要大改,只换参数组和显示数据源就行。界面的框架已经沉淀为固件里的一个独立模块,后续项目直接复用,只需要改参数表的定义。

也可以考虑一个更激进的方向:让固件本身来辅助整定。屏幕上有实时误差数据,MCU自己也存着最近一段时间的响应曲线,那能不能在界面上增加一个“自动整定”入口,让固件自动做阶跃激励、采集响应、估算出合适的PID初值?我在这个方向上做过一些验证,通过简单的临界比例度法可以估算出P的初值,结果拿来当手动精调的起点很好用。这个内容已经超出本期的范围,如果后面有空,我打算单独开一期讲讲“固件里的自动整定怎么和这个界面配合”,算是给第8期留个引子。

每个项目都应该尽早长出人机界面,不用等控制算法完美了再补。界面本身不复杂,但它是整定流程的基石,也是你在现场调试时唯一能信任的“眼睛”。把这块做好再动手调参,你会回来感谢第7期的自己。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 11:23:56

软件平替迁移指南:从需求拆解到数据落地,避开工具替换的坑

“某野”这个代号背后到底是什么工具,不同人心里可能完全不一样。有的人在找某个付费软件的免费代替品,有的人是因为原服务停止维护了,有的人单纯想换一个更轻量、更符合自己使用习惯的方案。“找平替”听起来只是换个软件,实际上…

作者头像 李华
网站建设 2026/9/7 11:23:28

模拟信号到数字信号:嵌入式ADC原理与STM32实战

模拟信号和数字信号之间的那道桥,说到底就是ADC。做嵌入式这几年,我见过太多人一上来就对着寄存器猛啃,结果连“为什么采样要保持时间”“为什么12位ADC读出来不是4095”这种关节都没打通。这篇东西不端着,就从模拟信号和数字信号…

作者头像 李华
网站建设 2026/9/7 11:22:04

把AI当电钻:从提示词到Agent的AI应用落地实践指南

在Hacker News上刷到“Is AI a Powerdrill?”这个问题时,我第一反应是笑了一下。点进去看了一圈,底下的回答基本分成两派:一派把AI捧成能独立思考的数字同事,另一派把它贬成价格不菲的高级计算器。但认真想想,“电钻”…

作者头像 李华
网站建设 2026/9/7 11:21:42

2026年AI写小说软件推荐:文风统一与语言质感榜(5款)

长篇连载最怕文风漂移:前三章是老练的笔调,写到后面变成了AI腔。文风统一与语言质感,考验的是AI写小说软件对风格的记忆和把控。本文依据各产品官方公开资料,从文风记忆、润色层次、多模型适配、一致性保障四个维度,整…

作者头像 李华
网站建设 2026/9/7 11:21:28

Windows Server下MxsDoc文档管理系统zip包部署与避坑指南

简介:MxsDoc(DocSys)专业版/企业版 Windows 安装包,是一套基于 Web 的文件与文档管理系统,适合需要搭建私有网盘、实现权限隔离、历史版本追溯与多人协同编辑的企业和团队使用。系统开源,支持多仓库独立规则…

作者头像 李华