STM32H5_IAP升级项目复盘——从缺陷到改进
STM32H5工业互联项目复盘二、缺陷一IAP 烧写失败后的假成功2.1 升级流程回顾2.1.1 整体流程PC 上位机 主控 STM32H5 │ │ │ 1. 写寄存器 0 0x55 │ │ (MODBUS_PRIVATE_CMD_ENTER_BOOT) │ │ ─────────────────────────────→ │ process_emergency_cmd() │ │ → ResetToBootloader() │ │ 写 FirmwareInfo(bEnterBootloader1) │ │ 软件复位 │ │ │ 2. 复位后进入 Bootloader │ │ │ isNeedToUpdate() 1 │ │ 停留在 Bootloader等待固件 │ │ │ 3. 文件头 (record_no0) │ │ ─────────────────────────────→ │ 解析 FileInfo擦除 Flash │ │ 擦除 APP 区 擦除 CFG 区 │ │ │ 4. 数据包 #1 (record_no1) │ │ ─────────────────────────────→ │ WriteFirmware() ← 写入 Flash │ │ │ 5. 数据包 #2..N │ │ ─────────────────────────────→ │ 同上 │ │ │ 6. 收完所有包后 │ │ │ WriteFirmwareInfo() │ │ → bEnterBootloader 0 │ │ → 下次启动进入 APP2.1.2 V1.0 核心代码burn_firmware()voidburn_firmware(uint8_t*msg,uint16_tmsg_len){// 省略解析过程...if(record_no0){// 文件头擦除 FlashEraseFlash(APP_LOAD_ADDR,tFileInfo.file_len);/* 擦除APP区 */EraseFlash(CFG_OFFSET,SECTOR_SIZE);/* 擦除配置区 */recv_len0;flash_addrAPP_LOAD_ADDR;}else{cur_lenmsg[2]-7;recv_lencur_len;WriteFirmware(msg[10],cur_len,flash_addr);// ← ① 返回值未检查flash_addrcur_len;if(recv_lentFileInfo.file_len){// ② 只要收够了长度就写启动 APP标志tFirmwareInfo.bEnterBootloader0;WriteFirmwareInfo(tFirmwareInfo);}}}2.2 缺陷分析2.2.1 缺陷触发场景假设如下场景步骤事件结果1PC 下发文件头✅ 擦除 APP 区和 CFG 区成功2PC 下发数据包 #1✅WriteFirmware()成功3PC 下发数据包 #2✅ 成功………N-1PC 下发数据包 #N-1❌WriteFirmware()失败Flash 写入错误NPC 下发最后一个数据包 #N❌ 也失败—recv_len file_len成立bEnterBootloader被设为 0—WriteFirmwareInfo()✅ 写入 CFG 区复位isNeedToUpdate()返回 0❌跳转到损坏的 APP → 跑飞2.2.2 根因分析问题出在两处缺陷①WriteFirmware()的返回值被忽略WriteFirmware(msg[10],cur_len,flash_addr);// 返回值被丢弃WriteFirmware()内部如果HAL_FLASH_Program()失败会返回-1staticintWriteFirmware(uint8_t*firmware_buf,uint32_tlen,uint32_tflash_addr){for(inti0;ilen;i16){if(HAL_OK!HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD,flash_addr,(uint32_t)firmware_buf)){g_bFlashProgramming0;HAL_FLASH_Lock();return-1;// ← 失败了但没人检查}flash_addr16;firmware_buf16;}return0;}缺陷②判断烧写完成的依据是recv_len file_len而不是检查WriteFirmware()的成功与否if(recv_lentFileInfo.file_len){// ← 只看收了多少数据tFirmwareInfo.bEnterBootloader0;// ← 没问 Flash 写进去没有WriteFirmwareInfo(tFirmwareInfo);}这个条件只能说明数据已经通过串口收完了但不能说明数据已经正确写入 Flash 了。协议层和应用层的成功被混淆了。2.2.3 问题严重性评估维度评估触发概率低正常通信正常 Flash 基本不会失败危害程度极高设备变砖需要人工拆机烧录恢复检测难度低运行中无报错重启后才暴露修复成本低几行代码2.3 改进方案2.3.1 方案一最小改动检查 WriteFirmware 返回值else{cur_lenmsg[2]-7;recv_lencur_len;// ★ 修复检查写入结果if(0!WriteFirmware(msg[10],cur_len,flash_addr)){// 写入失败不回写 bEnterBootloader0// 复位后 CFG 区全 FF → 停留在 Bootloader → 可重试SetUpdateStatus(0);return;// 放弃后续烧写}flash_addrcur_len;if(recv_lentFileInfo.file_len){tFirmwareInfo.bEnterBootloader0;WriteFirmwareInfo(tFirmwareInfo);}}效果一旦任何一包写入失败bEnterBootloader不会被设为 0CFG 区保持全0xFF。复位后isNeedToUpdate()返回 1设备停留在 BootloaderPC 可以重新开始升级。2.3.2 方案二更完善加入 CRC 校验代码中FirmwareInfo结构体已经预留了crc32字段typedefstructFirmwareInfo{uint32_tversion;uint32_tfile_len;uint32_tload_addr;uint32_tcrc32;// ← 已预留但未使用uint8_tfile_name[16];uint32_tbEnterBootloader;}FirmwareInfo;bootloader.c中也已经实现了 CRC32 计算函数GetCRC32()。可以利用起来// 文件头发送时上位机计算整个固件的 CRC32 并存入 FileInfo// 接收完毕后主控重新计算 CRC32 对比if(recv_lentFileInfo.file_len){// 计算接收到的固件数据的 CRC32uint32_tcalc_crcGetCRC32((constchar*)APP_LOAD_ADDR,tFileInfo.file_len);if(calc_crctFileInfo.crc32){// 校验通过tFirmwareInfo.bEnterBootloader0;WriteFirmwareInfo(tFirmwareInfo);}else{// CRC 不匹配不写 bEnterBootloader0// 复位后进入 Bootloader可重试Draw_String(0,64,CRC ERROR! Retry...,0xFF0000,0);}}2.3.3 方案三终极方案双备份 回滚在 Bank2 中保留一份上一次正常运行的回退固件Flash 布局改进版 0x0800_0000 Bootloader (256KB) 0x0804_0000 APP 主分区 (Active) ... 0x0810_0000 APP 备份分区 (Backup) ← 新加 ... 0x081F_E000 FirmwareInfo升级策略1. 新固件写入备份分区而不是主分区 2. 全部写入完成 CRC 校验通过 → 再写 FirmwareInfo 切换启动分区 3. 如果新固件启动失败 → Bootloader 检测到启动超时 → 自动切回旧固件这是工业级产品中常见的A/B 双备份 (Dual Bank) 升级方案但需要更大的 Flash 空间支持。2.4 三种方案对比方案改动量防护效果缺点方案一检查返回值3 行代码✅ 防止写入失败后假成功无法检测数据在传输中损坏CRC 正确但内容错乱方案二 CRC 校验10 行代码✅ ✅ 同时检测写入错误和数据损坏需要上位机配合计算 CRC无法回滚方案三双备份较大重构✅ ✅ ✅ 完全防止变砖需要额外 Flash 空间启动逻辑更复杂推荐路线以方案一为底线必须修方案二为标准推荐方案三为长期目标。三、缺陷二分析DMA 半传输中断导致数据重复推送除了 IAP 烧写的缺陷UART 接收部分也有一个值得复盘的 Bug位于HAL_UARTEx_RxEventCallback中。3.1 背景DMA IDLE 接收的 HT/TC 机制项目使用 STM32H5 的DMA 空闲中断方式接收 UART 数据DMA 缓冲区大小为 260 字节模式为DMA_NORMAL非循环。HAL 库在两种情况下触发回调HAL_UARTEx_RxEventCallbackRxEventType触发条件Size 值HAL_UART_RXEVENT_HTDMA 传输达到半缓冲区(130字节)130HAL_UART_RXEVENT_TC空闲线检测到 或 DMA 传完整个缓冲区实际接收到的字节数正常情况下一帧数据可能只收到几十字节此时只触发 TC空闲线检测不会触发 HT。但如果数据量较大超过 130 字节就会先触发 HT再触发 TC。3.2 V1.0 核心代码没有 old_posV1.0 版本中这个回调函数的实现非常简单voidHAL_UARTEx_RxEventCallback(UART_HandleTypeDef*huart,uint16_tSize){PUART_Data pdata;if(huarthuart2)pdatag_uart2_data;if(huarthuart4)pdatag_uart4_data;/* 把 rx_buf 中 0~Size 的所有字节送入队列 */for(inti0;iSize;i)xQueueSendFromISR(pdata-rxQueue,pdata-rx_buf[i],NULL);/* 重新启动 DMA IDLE 接收 */HAL_UARTEx_ReceiveToIdle_DMA(pdata-huart,pdata-rx_buf,UART_RX_BUF_LEN);}3.3 缺陷触发场景当一帧数据超过 130 字节时DMA 会先填满前半缓冲区触发HT 事件DMA 缓冲区 (260字节): ┌──────────────────────┬──────────────────────┐ │ 0 1 ... 129 │ 130 131 ... 259 │ └──────────────────────┴──────────────────────┘ ↑ HT 触发 (Size130) → for(i0; i130; i) → 推送字节 0~129 到队列 ✅ → 重启 DMA ← ★ 问题 1问题 1HT 时不应重启 DMA。重启后DMA 重新从缓冲区头部开始写入后半缓冲区130~259中尚未到达的数据会被覆盖。即使修复了重启 DMA 的问题还有第二个问题。假设 HT 时不重启 DMADMA 继续向后半缓冲区填充数据DMA 缓冲区 (260字节): ┌──────────────────────┬──────────────────────┐ │ 0 1 ... 129 │ 130 131 ... 259 │ └──────────────────────┴──────────────────────┘ ↑ TC/空闲触发 (Size260) → for(i0; i260; i) → 推送字节 0~259 到队列 → 字节 0~129 被第二次推送 ★ 问题 2问题 2TC 事件时for(i0; iSize; i)从 0 开始把整个缓冲区都推送了HT 时已经推送过的前半部分数据被重复推送。3.4 数据重复的后果以 Modbus RTU 通信为例假设一帧数据是01 03 04 00 64 00 65 78 B69 字节没有超过 130只触发 TC没问题。但如果收到大于 130 字节的数据流实际收到的数据: AA BB CC DD ... (200 字节) ↓ HT 事件 (Size130): 推送 AA~第130个字节 ✅ ↓ TC 事件 (Size200): 推送 AA~第130个字节 (重复) 第131~200字节 ↓ 队列中的数据: [AA..130] [AA..130] [131..200] ↑ 重复了两次对于 Modbus 协议来说数据重复意味着帧边界错乱接收方收到多余的字节无法正确解析帧CRC 校验失败多余的字节破坏了帧结构CRC 通不过通信中断连续 CRC 失败导致从机无响应3.5 根因分析这个 Bug 根因是没有区分 HT 事件和 TC 事件的处理逻辑。V1.0 的代码用同一个for(i0; iSize; i)处理两种事件。对于 HT应该只处理前半缓冲区0~Size对于 TC应该只处理后半缓冲区old_pos~Size。这两种情况下新数据的起始位置是不同的。3.6 问题严重性评估维度评估触发概率中只在单次接收超过 130 字节时触发Modbus 命令帧通常很小但固件升级分包传输时可能触发危害程度高数据重复 → CRC 错乱 → 通信中断检测难度低逻辑清晰代码审查即可发现修复成本极低引入一个 old_pos 变量即可3.7 改进方案引入old_pos变量来记录 HT 事件时已经处理到的位置TC 事件时从这个位置开始继续推送voidHAL_UARTEx_RxEventCallback(UART_HandleTypeDef*huart,uint16_tSize){PUART_Data pdata;staticuint16_told_pos0;// 记录 HT 已处理位置if(huarthuart2)pdatag_uart2_data;if(huarthuart4)pdatag_uart4_data;/* 从 old_pos 开始推送新数据到队列 */for(intiold_pos;iSize;i)xQueueSendFromISR(pdata-rxQueue,pdata-rx_buf[i],NULL);old_posSize;// 记录已处理位置if(HAL_UART_RXEVENT_HT!huart-RxEventType)/* 非 HT → TC 事件 */{/* TC 事件一帧结束重启 DMA 并重置 old_pos */old_pos0;HAL_UARTEx_ReceiveToIdle_DMA(pdata-huart,pdata-rx_buf,UART_RX_BUF_LEN);}/* HT 事件不重启 DMA让 DMA 继续填后半缓冲区 */}效果事件触发条件V1.0错误V2.0修正HTSize130推 0~129重启 DMA❌推0~129old_pos130不重启 DMA✅TCSizeN推0~N数据重复❌推130~Nold_pos0重启 DMA✅3.8 一点深入思考这个old_pos变量被声明为static在当前项目中其实不会出问题——因为 UART2 和 UART4 有互斥锁保护底层 DMA 接收不会同时进行。但如果将来系统扩展需要更多 UART 通道或更高的并发度static共享就会成为隐患。更规范的做法是将old_pos放入UART_Data结构体中作为实例成员不过那是另一层面的改进了。对比缺陷一缺陷一中WriteFirmware()的返回值被忽略属于忘了这个缺陷属于不知道 DMA 半传输中断的存在。两种 Bug 类型不同但都说明了对硬件外设工作机制的深入理解是嵌入式开发的硬门槛。四、Cortex-M0 中断向量表重定位STM32F030 IAP4.1 背景问题为什么 APP 的中断会跑飞STM32F030 使用Cortex-M0内核它没有SCB-VTOR向量表偏移寄存器。VTOR 是 Cortex-M3/M4/M7 才有的功能。在 M0 上CPU 永远从0x00000000获取中断向量表。IAP 方案中Bootloader 需要跳转到 APP位于0x08020000执行。如果没有特殊处理APP 启动后一旦发生中断CPU 发生中断 → 从 0x00000000 取向量 (此时仍映射到 Bootloader 所在 Flash) → 拿到 Bootloader 的向量 → 跳转到 Bootloader 的中断服务函数 → APP 崩溃 ❌4.2 解决方案RelocateVector解决方法是把 APP 的中断向量表拷贝到 SRAM 开头再通过内存重映射让 0x00000000 指向 SRAMvoidRelocateVector(void){memcpy((void*)VECTOR_ADDR_AT_RAM,(void*)APP_LOAD_ADDR,VECTOR_SIZE_AT_RAM);__HAL_SYSCFG_REMAPMEMORY_SRAM();}步骤动作效果①拷贝向量表SRAM 开头 (0x20000000) 拥有了 APP 的向量表②内存重映射0x00000000不再指向 Flash改为指向 SRAM0x20000000③中断响应CPU 从0x00000000取向量 → 实际访问 SRAM → 找到 APP 的中断处理函数4.3 内存布局// bootloader.h 中的定义#defineAPP_LOAD_ADDR0x08020000// APP 在 Flash 中的起始地址#defineVECTOR_ADDR_AT_RAM0x20000000// 向量表在 SRAM 中的目标地址#defineVECTOR_SIZE_AT_RAM200// 向量表大小 (50 个向量 × 4 字节)Flash 布局: 0x08000000 ┌────────────────────┐ │ Bootloader 代码 │ 0x08020000 ├────────────────────┤ │ APP 代码 (原地执行) │ ← CPU 直接从 Flash 读取指令执行 (XIP) │ APP 只读常量 │ 0x0803F800 ├────────────────────┤ │ 固件配置信息 │ 0x08040000 └────────────────────┘ SRAM 布局: 0x20000000 ┌────────────────────┐ │ 向量表 (200 字节) │ ← RelocateVector() 将 APP 向量表拷贝至此 0x200000C7 ├────────────────────┤ 0x200000C8 │ │ │ APP 变量/堆栈 │ ← IRAM1 (起始地址 0x200000C8) │ (.data / .bss) │ 0x2000XXXX └────────────────────┘IRAM1 的起始地址设为0x200000C8正是为了预留前 200 字节 (0xC8) 给向量表让应用代码的变量和堆栈不占用这段保留空间。五、更深层的反思5.1 为什么这个 Bug 会写出来分析 V1.0 的代码有两个原因static 变量的惯性recv_len和tFileInfo被声明为static意味着它们跨多次函数调用保持状态。开发者可能更关注数据收完了没有而忽略了硬件写入是否成功。缺少防御性编程思维嵌入式开发中应该不会失败的地方恰好是最容易出问题的地方。Flash 编程虽然通常可靠但在电源不稳、EMC 干扰等工业现场失败是现实存在的。5.2 从这次复盘学到的原则原则说明所有硬件操作函数都必须检查返回值HAL_FLASH_Program()、HAL_FLASHEx_Erase()等“看起来完成不等于实际完成”协议层的成功不代表应用层的成功升级失败要能回退宁可停留在 Bootloader不要跳转到损坏的 APP预留字段及时用上crc32字段已经预留了但没有真正用起来等于没留失败要显式处理不要因为不会发生就忽略异常分支5.3 关于变砖的进一步思考在这个项目中即使修复了上述 Bug最坏情况下——比如在EraseFlash()执行中途断电——设备依然可能变砖因为 Bootloader 自身代码区可能被擦出问题不会Bootloader 位于0x0800_0000擦除的是0x0804_0000开始的 APP 区不会擦到自己。更安全的设计是Bootloader 自身固件永不擦除通过写保护且 Bootloader 只烧写 APP 区确保自身独立于升级流程之外。本项目符合这个设计原则这是值得肯定的。六、总结IAP 烧写缺陷 ──────────────────────────────────────────────────────────── V1.0 缺陷 改进 效果 WriteFirmware() 检查返回值 失败后不写入 返回值被忽略 提前 return bEnterBootloader0 仅靠 recv_len 判断 CRC 校验 全面防护数据完整性 烧录完成 无备份机制 双备份升级 最大程度避免变砖 UART DMA 接收缺陷 ──────────────────────────────────────────────────────────── V1.0 缺陷 改进 效果 HT/TC 事件未区分 old_pos 记录已处理位置 数据不会重复推送 HT 时重启 DMA HT 时不重启 DMA 后半缓冲区数据不丢失 TC 时从 0 开始推送 TC 时从 old_pos 开始推送 数据不重复 Cortex-M0 向量表重定位 ──────────────────────────────────────────────────────────── 知识点 说明 无 VTOR 寄存器 CPU 永远从 0x00000000 取向量 RelocateVector() 拷贝向量表到 SRAM 重映射 IRAM1 起始 0x200000C8 预留前 200 字节给向量表 代码在 Flash 原地执行 (XIP) 仅 200 字节向量表拷贝到 SRAM

相关新闻

终极Xbox 360模拟器教程:在PC上重温经典游戏

终极Xbox 360模拟器教程:在PC上重温经典游戏

终极Xbox 360模拟器教程:在PC上重温经典游戏 【免费下载链接】xenia-canary Xbox 360 Emulator Research Project 项目地址: https://gitcode.com/gh_mirrors/xe/xenia-canary 想要在现代电脑上重温那些经典的Xbox 360游戏吗?Xenia Canary就是你…

2026/10/21 0:22:56 阅读更多 →
用 Skill 精读 OCRBench v2 是什么体验?

用 Skill 精读 OCRBench v2 是什么体验?

最近用我的 Paper Close Reading Skill,完整精读了 OCRBench v2。 这次没有让模型一次性概括整篇论文,而是采用“引导式+单元级”的方式推进: 从论文定位和整体框架开始,逐节阅读正文、图表、公式与附录,…

2026/10/18 6:43:03 阅读更多 →
高品质铁氟龙高温胶带|工业高温防粘优选,源头工厂靠谱供货

高品质铁氟龙高温胶带|工业高温防粘优选,源头工厂靠谱供货

在 SMT 电子制造、食品包装、热封设备、模具加工、流水线辊筒生产当中,高温粘连、残胶残留、胶带不耐高温频繁更换,一直是困扰众多工厂的难题。一款品质过硬的铁氟龙(PTFE)高温胶带,既能减少设备损耗,又能提…

2026/10/18 6:43:05 阅读更多 →

最新新闻

共模滤波器在Layout时的小说明

共模滤波器在Layout时的小说明

深圳市韬略科技有限公司,专注一站式EMC电磁兼容解决方案,提供EMC整改服务、实验检测、企业培训、器件销售业务。致力于为各行业客户解决电磁兼容测试、整改难题。官网:www.topleve.com一、前言磁环是电磁兼容整改项目中常用的器件&#xff0c…

2026/10/21 2:35:57 阅读更多 →
连锁品牌的老客唤醒,怎么做才算有效

连锁品牌的老客唤醒,怎么做才算有效

很多品牌的操作路径是:拉出一份半年没消费的名单,群发一条优惠消息,然后看有多少人回来。头一轮效果还行,第二轮弱一点,第三轮几乎没人反应。不是客户变了,而是这套做法本身就存在问题,只是前几…

2026/10/21 2:35:57 阅读更多 →
三分修,七分养,设备管理才能赢!

三分修,七分养,设备管理才能赢!

设备一停,整条生产线跟着受影响;故障刚修好,没过几天又出问题;维修人员天天忙着抢修,生产部门却始终不敢放松警惕。 更让管理者头疼的是,维修费用年年在花,备件库存越积越多,设备故障…

2026/10/21 2:35:57 阅读更多 →
Git存储模型与分支协作实践:从快照原理到工程化流程

Git存储模型与分支协作实践:从快照原理到工程化流程

写这篇东西的时候,我正在帮一个新组建的跨职能团队梳理协作流程。团队里每个人都装了 Git,都在用,但聊到"为什么一个 commit 会变成这样""为什么 merge 完历史乱成一团"的时候,会议室就安静了。这大概也是多数…

2026/10/21 2:35:57 阅读更多 →
二进制拆弹实验全流程解析:从汇编逆向到隐藏关卡实战

二进制拆弹实验全流程解析:从汇编逆向到隐藏关卡实战

1. 拆弹之前:这个实验到底在练什么计算机系统基础这门课里,二进制拆弹实验算是把“读汇编、看内存、推逻辑”三件事揉在一起的高强度训练。标题里的“拆除二进制炸弹”,说白了就是一个编译好的可执行程序,里面埋了若干关卡&#x…

2026/10/21 2:35:57 阅读更多 →
混沌拓扑学(HDT)——现代物理方法论危机的系统性剖析

混沌拓扑学(HDT)——现代物理方法论危机的系统性剖析

前言 近现代理论物理的核心危机,并非源于局部实验数据偏差或个别公式计算缺陷,而是根植于百年不变的错误研究方法论。当代主流物理研究长期倒置认知逻辑:以预设的数学公理、理想化刚体模型、绝对化约束条件为前置根基,再将自然现象…

2026/10/21 2:34:56 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →