news 2026/10/21 12:42:30

STM32自动喂食器实战:硬件选型、状态机设计与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32自动喂食器实战:硬件选型、状态机设计与避坑指南

简介:基于STM32的自动喂食器课程设计资源包,面向嵌入式相关专业的学生与开发者,适用于学习STM32外设驱动、定时器中断、PWM控制及Proteus仿真等典型嵌入式开发环节。资源共201个文件,压缩包约11.75MB,包含完整的Keil工程文件、ST官方HAL库源码(如stm32f4xx_hal_tim.c、stm32f4xx_hal_uart.c)、编译生成的hex/axf文件,以及Proteus仿真工程和说明文档,便于直接查看与二次修改。已有7576人学习下载。从中可掌握STM32F401定时器配置来设定喂食周期、利用LCD显示状态、按键输入和电机PWM调速等关键设计思路;仿真工程可在无硬件条件下先行验证逻辑,降低调试门槛。整体方案涵盖硬件选型、软件编程、系统集成与功能实现,对完成课程设计或入门智能家居类项目有实际参考价值。

1. 为什么用STM32做自动喂食器:一个嵌入式课程设计能落地的关键点

很多嵌入式课程设计选自动喂食器,因为它的功能链路足够完整:传感器采集、电机控制、RTC定时、人机交互、掉电存储,几乎把STM32的核心外设都过了一遍。但同样是这个课题,有人交出的是“能转的舵机+延时函数”,有人交的是“能跑一个月不出错的完整系统”,差别不在芯片型号,而在方案设计。这篇笔记从一个能直接复现的STM32自动喂食器方案入手,把硬件选型、状态机设计、驱动代码和实际踩坑串一遍,目标是让新手照着搭能点亮,让熟手直接换参数就能改造成自己的版本。

2. 硬件选型与模块接线:从舵机到称重传感器的整套搭配

2.1 主控选型:为什么默认选STM32F103C8T6而不是F407

自动喂食器的控制逻辑并不复杂,不需要浮点运算,不需要大量缓存,F103C8T6的72MHz主频、64KB Flash、20KB SRAM完全够用。更重要的是它的资源配套成熟,HAL库和标准库的例程多,课程设计阶段遇到问题搜起来快。F407的优势在更高速的ADC和更多定时器,但这个项目里用不上,成本还高一截。

选型时还要看引脚数量,C8T6是LQFP48封装,GPIO刚好够用。我一般会预留两个空闲引脚,一个接调试用的LED,一个留作后续扩展低功耗唤醒功能。如果手上的板子是C6T6(32KB Flash)也能跑,但代码编译完接近极限,不建议省这十几块钱。

2.2 执行机构对比:舵机、步进电机与直流减速电机怎么选

出粮机构是整个系统里最容易翻车的部分,选型要看食料形态和出粮精度。

  • 舵机(SG90/MG995):适合旋转拨片式出粮,SG90扭矩不够,拨片插进粮仓经常卡死,我用的是MG995,扭矩大一些,但需要外部5V供电,不能直接从板子3.3V取电。
  • 步进电机(28BYJ-48 + ULN2003驱动板):适合转盘式定量出粮,控制精准,每次转固定角度,缺点是速度和噪音,夜间喂食会吵醒人。
  • 直流减速电机(带霍尔编码器):适合螺旋推进式出粮,但闭环控制代码复杂,课程设计周期内不建议碰。

从“能交差”和“能改进”两个角度,我建议用步进电机。它的逻辑清晰,出粮量可以通过脉冲数反复校准,写报告时也有数据可以展开。MG995舵机做备选,但要注意堵转电流能拉到1A以上,电源纹波会干扰ADC采样。

2.3 传感器与RTC模块:称重、时钟、显示一个都不能少

喂食器不能只“定时转一下电机”,至少要有三个反馈维度:

  • 称重模块:HX711 + 压力传感器,放在粮仓底部或食盆下方,检测余粮量和是否已进食。HX711用24位ADC,两路差分输入,接传感器四根线(E+、E-、A+、A-),SCK和DOUT接GPIO。
  • RTC模块:DS3231比DS1302贵,但温度补偿晶振的漂移量小很多,一个月误差几秒。DS1302便宜但走时漂移明显,如果喂食器连续运行两周,喂食时间会偏出二十分钟。课程设计如果周期短,DS1302能用;如果要做长期测试,还是DS3231靠谱。
  • 显示与交互:0.96寸OLED(I2C)显示当前时间、下次喂食倒计时、余粮百分比。三个按键,分别是模式切换、手动出粮、参数设置。这块不做复杂菜单,三个按键足够覆盖所有操作路径。

2.4 接线表与电源分配:烧板子的坑都在电源这里

模块引脚连接供电备注
STM32F103C8T6 最小系统—3.3V用AMS1117-3.3稳压
OLED SSD1306SCL→PB6, SDA→PB73.3VI2C1
DS3231SCL→PB6, SDA→PB73.3V与OLED共用I2C总线,地址不同
HX711SCK→PA1, DOUT→PA03.3V传感器激励接5V也可,看模块说明
步进电机驱动ULN2003IN1→PA4, IN2→PA5, IN3→PA6, IN4→PA75V电机供电必须独立走线
三按键分别接PB0/PB1/PB10,另一端接GND3.3V内部上拉
蜂鸣器PA83.3V喂食完成短鸣两次
继电器/电子开关PA95V控制食盆加热或照明(可选)

电源是这里最大的坑。步进电机启动瞬间电流能到300mA以上,如果和STM32共用一条细杜邦线,电压会被拉低,MCU直接复位。我的做法是:5V适配器进来先分两路,一路给ULN2003驱动板和电机,另一路经过AMS1117给MCU供电,两路地在电源入口处单点汇合。

接完线先别上电,用万用表量一遍VCC和GND之间有没有短路,再量AMS1117输出是不是3.3V。这个习惯能救回一半以上的“上电冒烟”事故。

3. 状态机驱动的喂食逻辑:定时、手动与补食三种模式怎么切

3.1 为什么用状态机而不是一堆if-else

自动喂食器的行为看起来简单:到点转电机、按按钮转电机、余粮少了提示。但如果把这些动作写成线性if-else,会出现一个典型问题——用户按“手动出粮”时正好碰到定时喂食触发,两个流程同时操作电机,步进电机相位冲突,轻则卡转,重则烧驱动。

用状态机可以把系统行为收敛成三个稳定状态:待机(IDLE)、喂食中(FEEDING)、设置中(SETTING)。所有按键事件和定时器事件都先进入事件队列,状态机根据当前状态决定要不要响应,从根上避免了事件竞争。对课程设计来说,状态机还有一个额外的好处:答辩时画状态转移图非常直观,评审老师一眼就能看出系统结构,比看一堆散落的函数好讲得多。

3.2 核心状态定义与事件映射

这段是系统的骨架,状态枚举和事件枚举先定义清楚:

typedef enum { STATE_IDLE = 0, // 待机:显示时间,等待事件 STATE_FEEDING, // 喂食中:电机转动,屏蔽重复触发 STATE_SETTING // 设置中:循环修改喂食时间和手动出粮量 } SysState; typedef enum { EVENT_NONE = 0, EVENT_BTN_MODE, // 模式键,短按切换状态,长按进入设置 EVENT_BTN_FEED, // 手动出粮键 EVENT_BTN_SETUP, // 参数调整键 EVENT_TIMER_PLAN, // 定时喂食触发 EVENT_WEIGHT_LOW // 余粮不足 } SysEvent;

事件产生后统一丢进一个环形缓冲,状态机主循环只从缓冲取事件,不在中断里直接处理业务逻辑。这样做的目的是让状态机永远只面对“一个事件”,不会出现中断嵌套导致的全局变量错乱。

参数说明:EVENT_BTN_MODE和EVENT_BTN_SETUP分开,是因为三个按键里有一个需要复用——短按切换状态,长按进入参数修改。如果按键逻辑直接写在状态机里,后面加“长按”功能时代码会很别扭,我这里把按键扫描独立成模块,只向上报事件,状态机不关心按键怎么检测的。

3.3 状态转移与喂食动作实现

状态转移逻辑放在主循环里,每10ms跑一次:

void SM_Process(SysEvent evt) { switch (state) { case STATE_IDLE: if (evt == EVENT_BTN_MODE) { state = STATE_SETTING; Display_EnterSetting(); } else if (evt == EVENT_BTN_FEED) { Feed_Once(MANUAL_FEED_AMOUNT); } else if (evt == EVENT_TIMER_PLAN) { Feed_Once(TIMER_FEED_AMOUNT); } else if (evt == EVENT_WEIGHT_LOW) { Buzzer_Alert(3); } break; case STATE_FEEDING: // 喂食中不响应任何新事件,防止重复出粮 if (evt == EVENT_TIMER_PLAN) { Log_AddBacklog(); // 记录一次未执行的定时喂食 } break; case STATE_SETTING: if (evt == EVENT_BTN_MODE) { state = STATE_IDLE; RTC_SaveConfig(); Display_BackToMain(); } else if (evt == EVENT_BTN_SETUP) { Setting_AdjustValue(1); // 每次+1分钟 } break; } }

Feed_Once内部实现是:先检查当前是不是STATE_IDLE,是的话把状态切到STATE_FEEDING,然后以旋转步进电机N圈的方式出粮,完成后回到STATE_IDLE。喂食期间如果定时器又触发了一次,说明用户设置的间隔太短,系统只记录不执行,避免电机连续转。

这个“记录未执行事件”的思路是从实际操作里长出来的——测试时我把定时间隔设成1分钟,结果喂食还没结束下一次触发就来了,电机嗡嗡响个不停。加上Log_AddBacklog后,至少知道错过了一次喂食,后面做统计也有数据。

3.4 步进电机驱动与出粮量计算

28BYJ-48步进电机是减速电机,减速比1/64,步距角5.625°/64,转一圈需要4096个脉冲。出粮量不能直接按脉冲数算,得先实测:让电机每次转200个脉冲,称一次出粮重量,连续测5次取平均,得到“每脉冲出粮克数”,再反推目标喂食量对应的脉冲数。

#define PULSE_PER_REV 4096 // 电机转一圈的脉冲数 #define GRAMS_PER_CIRCLE 12.5f // 实测:转一圈出粮12.5g,需自己校准 #define TIMER_FEED_GRAMS 25.0f // 定时喂食目标量,可配置 void Feed_Once(float target_grams) { float need_revs = target_grams / GRAMS_PER_CIRCLE; uint32_t need_pulses = (uint32_t)(need_revs * PULSE_PER_REV); state = STATE_FEEDING; for (uint32_t i = 0; i < need_pulses; i++) { Stepper_Step(1); // 单步旋转 delay_us(800); // 速度控制,太快会失步 if ((i % 100) == 0) { Check_Weight(); // 每100步看一次称重,防止过量 } } state = STATE_IDLE; }

参数说明:GRAMS_PER_CIRCLE这个值千万别照抄,我的粮仓、叶轮形状、粮食颗粒大小跟你不一样,出粮量差20%是正常的。校准方法:程序里写一个“测试模式”,按一次手动出粮转一圈,拿厨房秤称重,改宏定义,反复三次取平均值。

delay_us(800)对应步进频率约1.25kHz,28BYJ-48在这个速度下扭矩比较稳,再快容易失步。实际运行中如果听到电机“哒哒”声变脆但不转动,就是失步了,把延时拉长到1000us。

4. 驱动层与上层逻辑分离:HAL库代码组织与中断优先级处理

4.1 代码目录结构与模块划分

课程设计代码最容易写成“一个main.c走天下”,查bug时从头翻到尾。这个项目规模不大,但功能模块不少,我建议按这个结构组织:

User/ ├── main.c // 主循环,调度器 ├── sys_state.c // 状态机模块 ├── sensor_weight.c // HX711驱动与滤波 ├── motor_stepper.c // 步进电机驱动 ├── rtc_ds3231.c // 时钟模块 ├── oled_ssd1306.c // 显示模块 ├── key_scan.c // 按键扫描 ├── buzzer.c // 蜂鸣器 └── config_store.c // 参数存储(喂食时间、喂食量)

每个模块提供两个接口:xxx_Init()和xxx_Process(),初始化在main最开始调用,Process在状态机需要时调用。这样做的边界价值在于——如果后面想换成别家的OLED屏,只需要改oled_ssd1306.c,上层调用接口完全不变。答辩时也能明确说出“驱动层与业务层解耦”,这是加分项。

4.2 主循环调度:10ms时基与事件轮询

不要用HAL_Delay做所有事情。HAL_Delay在中断里会出问题,而且多个模块各自延时会导致时间片混乱。我的做法是开一个SysTick中断,每1ms产生一次时基,用一个全局计数变量tick_ms累加,主循环里按时间片轮询各任务:

volatile uint32_t tick_ms = 0; void SysTick_Handler(void) { tick_ms++; } void main_loop(void) { uint32_t last_key = 0, last_disp = 0, last_weight = 0; while (1) { if (tick_ms - last_key >= 20) { // 每20ms扫一次按键 Key_Scan(); last_key = tick_ms; } if (tick_ms - last_disp >= 200) { // 每200ms刷新一次OLED Display_Update(); last_disp = tick_ms; } if (tick_ms - last_weight >= 500) { // 每500ms采样一次称重 Weight_Sample(); last_weight = tick_ms; } SM_Process(GetNextEvent()); // 事件队列取事件 } }

时基参数说明:按键扫描20ms是防抖的经典窗口,小于10ms会漏掉快速按键,大于50ms手感肉。OLED刷新200ms是因为SSD1306是I2C接口,全屏刷新一次要传1KB数据,在72MHz主频下大概占用几毫秒,刷新太快主循环就卡了。称重500ms采样是因为HX711每次转换要100ms左右,而且压力传感器的噪声需要多次平均,太快反而波动大。

这是整个系统的主干,后面加功能都往这个循环里挂任务就行。

4.3 HX711称重读取:24位ADC的滤波为什么必须做

HX711读取本身不复杂,SCK拉低、DOUT变低表示数据准备好,然后连续读24个脉冲拿数据。但原始数据直接用的话,数字能跳几百甚至上千,因为电机的震动、桌面的晃动都会影响传感器。

#define HX711_SCK GPIO_PIN_1 #define HX711_DOUT GPIO_PIN_0 uint32_t HX711_Read(void) { uint32_t value = 0; HAL_GPIO_WritePin(GPIOA, HX711_SCK, GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(GPIOA, HX711_DOUT) != GPIO_PIN_RESET); // 等待数据就绪 for (uint8_t i = 0; i < 24; i++) { HAL_GPIO_WritePin(GPIOA, HX711_SCK, GPIO_PIN_SET); value = value << 1; if (HAL_GPIO_ReadPin(GPIOA, HX711_DOUT) == GPIO_PIN_SET) { value++; } HAL_GPIO_WritePin(GPIOA, HX711_SCK, GPIO_PIN_RESET); } // 第25个脉冲表示通道A增益128,本项目不用切换 HAL_GPIO_WritePin(GPIOA, HX711_SCK, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(GPIOA, HX711_SCK, GPIO_PIN_RESET); return value; } float Weight_GetGrams(void) { // 简单滑动平均滤波:取最近5次读取结果,去掉最大最小值再平均 uint32_t buf[5]; for (uint8_t i = 0; i < 5; i++) { buf[i] = HX711_Read(); HAL_Delay(100); } // 冒泡排序取中间三个值 for (uint8_t i = 0; i < 4; i++) { for (uint8_t j = i + 1; j < 5; j++) { if (buf[i] > buf[j]) { uint32_t tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp; } } } uint32_t sum = buf[1] + buf[2] + buf[3]; float avg = sum / 3.0f; // 标定系数,用已知砝码校准:系数 = 砝码克数 / (读取值 - 零点值) return (avg - zero_offset) / scale_factor; }

滤波逻辑说明:HX711原始读数受噪声影响很大,滑动平均+去掉极值能有效抑制偶发尖峰。注意zero_offset和scale_factor两个参数需要标定,零点值就是空仓时读到的数据,系数用100g砝码压上去算。

常见误用是直接用HX711库返回的原始值当重量,这个值受传感器个体差异和安装位置影响极大,不标定的话余粮判断完全不靠谱。

4.4 RTC与掉电存储:断了一周电,喂食计划不能丢

DS3231自带电池备份,断电后时间不丢,但喂食计划和出粮量参数存在MCU的Flash里,断电就没了。课程设计阶段很多人忽略这个问题,实际使用中一次意外断电,所有配置清零,用户就得重新设一遍。

我把配置存在STM32内部Flash的最后一个扇区:

typedef struct { uint16_t feed_hour; // 定时喂食小时 uint16_t feed_min; // 定时喂食分钟 uint16_t feed_amount; // 每次出粮克数(乘以10存储) uint16_t crc16; // 校验值 } FeedConfig; void Config_Save(FeedConfig *cfg) { // 先擦除扇区,再写入 FLASH_EraseInitTypeDef erase = {0}; uint32_t sector_error = 0; erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = CONFIG_FLASH_ADDR; erase.NbPages = 1; HAL_FLASHEx_Erase(&erase, &sector_error); HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, CONFIG_FLASH_ADDR, cfg->feed_hour); HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, CONFIG_FLASH_ADDR + 2, cfg->feed_min); HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, CONFIG_FLASH_ADDR + 4, cfg->feed_amount); HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, CONFIG_FLASH_ADDR + 6, cfg->crc16); }

Flash写入次数有限(F103的数据闪存一般标称10k次擦写),所以只有用户修改参数时才调用Config_Save,喂食完成不写Flash。校验用sail16或简单累加和都行,目的是上电时检测配置有效,无效就恢复默认值。这里最容易翻车的点是Flash写入地址越界,我用的最后一个扇区是0x0800F800(64KB型号),不同容量芯片地址不一样,务必查手册再写。

5. 避坑记录:上电复位、粮食卡轴与RTC跑飞的典型排查

5.1 现象:一上电步进电机疯转,停不下来

第一次把代码烧进去,电机在初始化没完成时就开始转,而且完全不受控制,按停止键也停不下来。

原因排查:步进电机的四个控制引脚(IN1到IN4)在MCU复位期间是高阻态,ULN2003输入悬空时输出会乱跳,导致电机自由转动。另外我的初始化代码里先开了电机驱动器的使能,再初始化GPIO,顺序反了。

解决:把GPIO初始化放到系统时钟配置之后、任何外设使能之前,同时把四个控制引脚初始化为推挽输出低电平,确保电机上电处于锁止状态。代码顺序调整为:HAL_Init → 系统时钟 → GPIO初始化(电机引脚先拉低)→ 其他外设。从那以后我每次写新工程,都把执行机构引脚的电平状态列出来逐项检查,不再相信“默认低电平”这种话。

5.2 现象:粮食出口堵塞,电机持续转动但不出粮

测试时发现定时喂食触发后,电机在转,也能听到“咔咔”声,但出粮口几乎没有粮食落下。

原因排查:粮仓底部是平的,粮食堆在角落形成拱桥效应,叶轮转过去时没有粮食可带。这是机械结构问题,不是代码问题。

解决:把粮仓底部改成漏斗斜面(倾斜角度至少45度),在出粮口上方加一个竖直的搅拌棒,跟随电机一起旋转,把搭桥的粮食打散。这个改动之后,出粮重量稳定性从“每次差10g”改善到“每次差2g以内”。如果你用3D打印做外壳,设计时直接加这根搅拌结构,省得后面改造。

5.3 现象:DS3231时间偶尔跳变,喂食时间偏差超过半小时

系统运行两天后,RTC读取到的时间比实际快了半小时以上,而且不是稳定快,是突然跳变到某个错误值。

原因排查:我用的是I2C总线,OLED和DS3231挂在同一组引脚上。OLED刷新(每200ms一次)占用I2C总线时间,如果恰好被中断打断,DS3231的读操作可能读到半个字节,导致时间寄存器数据错乱。

解决:把I2C通信改用硬件I2C(HAL库的I2C1),并提高优先级。更彻底的方案是把OLED和DS3231分开,DS3231用软件模拟I2C挂在另外两个引脚上,OLED用硬件I2C挂原引脚。两者物理隔离后,干扰再没出现过。注意:软件模拟I2C要开编译优化,否则时序可能慢到DS3231不响应。

5.4 现象:手动按键偶尔触发两次喂食

按键按一下,有时会出粮两次。

原因排查:按键扫描周期是20ms,我用的机械按键没有做完整的“按下释放”状态检测,按下一次的抖动能被两个20ms周期各采样到一次,产生两个按键事件。

解决:按键扫描里增加状态机,识别“按下沿”而不是电平。按下时登记事件,释放前不再重复登记,同时加一个300ms的全局锁定时间,按键后300ms内的任何按键事件直接丢弃。代码上改一行判断,但效果明显。之前在实机上统计过,不带沿检测每秒能多触发0.5到2次误事件,带上之后基本为0。

5.5 现象:称重数值漂移,余粮误报

空粮仓时称重显示30g,跑了一会儿变成40g,又过了几分钟回到25g。

原因排查:HX711的参考电压受供电波动影响,电机启动时电流大,电源电压跌落了100mV左右,导致ADC读数偏移。另一个因素是温度漂移和机械结构的热胀冷缩。

解决:从两个方向处理。电源上,电机供电和MCU供电完全分开,两个5V入口接一个大电容(1000uF电解电容 + 0.1uF陶瓷电容)做储能。代码上,不要直接用单次称重结果,而是在电机停止运行后300ms再采样,避开电源波动窗口。同时做软件校准:每次喂食完成后,等待3秒,连续读5次,取中间值更新余粮显示。

6. 进阶玩法:加一个“无RTC离线喂食日志”功能

课程设计答辩时,评审经常会问“你怎么证明系统按时喂食了”。光靠“我看到的”不够有说服力,我给这个项目加了一个离线喂食日志模块,不依赖外部存储芯片,用STM32内部Flash剩余空间,记录最近50次喂食记录。每条记录的结构就四个字段:

typedef struct { uint32_t timestamp; // 从RTC读出的Unix时间 uint16_t feed_amount; // 实际出粮克数 uint8_t source; // 0=定时触发 1=手动触发 2=补食 uint8_t weight_after; // 喂食后食盆重量(单位:g) } FeedLogEntry;

每条记录12字节,50条共600字节,加上前面配置项,Flash占用不到1KB。F103C8T6的13KB数据Flash有足够空间。写入用环形覆盖策略,满了就把最旧的一条重写掉。

void Log_AddEntry(FeedLogEntry *entry) { uint16_t next_idx = log_next_index; uint32_t addr = LOG_BASE_ADDR + (next_idx % MAX_LOG_COUNT) * sizeof(FeedLogEntry); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *(uint32_t*)&entry->timestamp); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + 4, *(uint32_t*)&entry->feed_amount); HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + 8, *(uint32_t*)&entry->weight_after); log_next_index++; }

查询时用串口输出,或者用OLED翻页显示最近几条。这个功能在答辩前实测了三天,日志里有每一条喂食记录的时间戳和出粮重量,评审老师直接对着数据看,不用问“怎么证明”了。我后来把这个日志模块沿用到了其他嵌入式项目上,发现“给系统一个能自证的数据出口”比任何口头解释都省事。

在那以后,我每次做嵌入式课程设计都会先想一个问题——“如果评审只给我5分钟,我拿什么证明系统真的在按预期工作”。有这个功能兜底,心里踏实很多,希望帮到你。

本文还有配套的精品资源,点击获取

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

CANN框架下AMCT模型压缩实战:量化、剪枝与蒸馏全解

这两年做端侧模型部署的朋友应该都有同样的感受&#xff1a;模型性能越来越强&#xff0c;但算力、带宽和功耗的账越来越难算。一个动辄上百兆的模型&#xff0c;直接塞进设备基本不现实&#xff0c;于是量化、剪枝、知识蒸馏这几个词几乎成了部署前的必经流程。我第一次接触CA…

作者头像 李华
网站建设 2026/10/21 12:33:18

基于TCP通讯与多线程的EMS版本信息采集功能设计与实现

站控需要随时看清下层从控EMS在跑哪个版本&#xff0c;这个需求听着简单&#xff0c;真做起来要操心的事不少。我这次开发的是一个基于TCP通讯和多线程的版本信息采集功能&#xff0c;站控作为客户端主动去连各个从控&#xff0c;把EMS的软件版本、固件编译时间、设备运行状态这…

作者头像 李华
网站建设 2026/10/21 12:32:37

EKS告警拦截器实战:用大模型把Alertmanager通知翻译成人话

凌晨两点十五分&#xff0c;手机在床头柜上震了第三遍。我眯着眼划开屏幕&#xff0c;一条来自 Alertmanager 的告警赫然写着&#xff1a;KubePodCrashLooping - payment-svc-687d8f9c7b-xxx - crash loop back-off这种告警&#xff0c;K8s 管理员和 SRE 应该都不陌生。可问题是…

作者头像 李华
网站建设 2026/10/21 12:31:16

Git分支、标签与发布:从开发到交付的三段接力

1. 把 branch、tag、release 放到同一个工地上 Git 里的 branch、tag、release 这三个概念&#xff0c;我见过太多人搞混了。有人把 tag 当分支在用&#xff0c;直接在 tag 上继续改代码&#xff1b;有人以为 release 就是打个 git tag&#xff1b;还有人发完版本之后不知道 re…

作者头像 李华
网站建设 2026/10/21 12:28:57

Docker容器化入门:从镜像管理到Dockerfile实战

1. 为什么我建议你从Docker开始上手容器化 刚接触容器化那会儿&#xff0c;我也被一堆名词绕得头晕——镜像、容器、仓库、编排&#xff0c;听起来像是另一个维度的东西。但真正用起来之后才发现&#xff0c;Docker的核心逻辑其实特别朴素&#xff1a; 把应用和它依赖的一切打…

作者头像 李华
网站建设 2026/10/21 12:26:10

AI矩阵联动:短剧批量创作与全域分发的工业化实战指南

1. 从账号搬运工到内容指挥官&#xff0c;我花了三个月才想明白做短剧账号矩阵的朋友&#xff0c;应该都有过这种状态&#xff1a;手机里装了七八个剪辑软件&#xff0c;电脑桌面堆满待发布的草稿&#xff0c;每天睁眼就是选题、写脚本、配音、剪辑、发布、回复评论&#xff0c…

作者头像 李华