v5.0.1#
v5.0.1 是基于 v5.0.0 的维护版本——旧的 sensor 模块变为构建于 csi 之上的 Python 兼容层,加载到帧缓冲区中的图像现在会像摄像头帧一样预览,JPEG 编码器不再有 1 MB 的输出限制并能正确处理溢出,此外还有一批针对摄像头、协议、网络和机器学习库的修复。对于基于 csi 编写的脚本,它不引入任何破坏性 API 变更;少数几处行为变更列在 破坏性 API 变更 之下。
亮点#
sensor模块现在是一个固化的 Python 模块,构建于csi之上。导入时会打印弃用警告;少数几个csi模块无法支持的函数现在会引发OSError。参见 sensor 模块变更。加载的图像像摄像头帧一样预览。 通过
copy_to_fb=True放入帧缓冲区的图像(image.Image、image.ImageIO.read()、fir.snapshot()、tof.snapshot())会在下一张图像覆盖帧缓冲区时——连同绘制在其上的任何内容——被刷出到 IDE,而不是立即刷出。参见 预览变更。JPEG 编码器全面改造。 1 MB 的输出缓冲区上限已取消(缓冲区大小根据可用内存确定),软件和硬件路径上的溢出都会得到处理而不再写出截断的文件,并且将已压缩图像以另一种压缩格式的扩展名保存时会引发异常,而不再写出垃圾数据。
新增
protocol.poll(),并为protocol.CBORChannel新增六种 CBOR 控件(text、lineedit、pushbutton、spinbox、radio、waveform)。YOLO Pro 后处理器、FLIR Lepton 3.1R / UW 支持、IMU 突发读取、OpenMV N6 上的 CAN,以及一个支持网络的模拟器目标。
新功能#
YoloPro—— 用于 Edge Impulse YOLO Pro 目标检测模型的新后处理器,并附带yolo_pro_detector.py示例。(f3f150ebd、ea7bbb398)protocol.poll()—— 按需运行一次协议任务迭代,供不由 USB 中断或poll_ms定时器驱动的自定义传输层使用。也可以再次调用protocol.init()来重新初始化协议栈。(c00c215f9、f849ffb1b)更多 CBOR 通道控件 ——
protocol.CBORChannel.add()新增text(静态字符串)、lineedit(可写字符串)、pushbutton(瞬时动作)、spinbox(步进式数值输入)、radio(选项组)和waveform(1D 时间序列,带sample_rate、series和typecode参数;赋值交错的采样字节即可流式发送)。(8016d29ba)csi.CUSTOM—— 在设置了自定义(w, h)分辨率之后,csi.CSI.framesize()现在返回一个可用的常量,而不是一个匿名整数。(af8841654)IMU 突发读取 ——
imu.__read_reg()接受一个可选的可写缓冲区,并在一次事务中用连续寄存器的内容填充它。(244ca480a)FLIR Lepton 3.1R 和 Lepton UW 现已被 Lepton 驱动识别,包括其辐射测量增益模式。(0fc356632)
模拟器网络 ——
MPS2_AN500QEMU 目标现在具备 lwIP 和用户模式网卡,固化了requests、mqtt和sensor,并新增了一个网络单元测试。(23546dfb1、61d187305、15096c3a2)OpenAI 兼容 API 示例 ——
openai_compatible.py拍摄一张 JPEG 快照并将其发送到 chat-completions 端点以进行场景描述。(79033c6aa)SX126x LoRa 示例 —— micropython-lib 的
lora-sx126x驱动(连同lora-sync/lora-async)已固化进 OpenMV RT1062 固件,并附带发送端/接收端/乒乓/图像流示例,用于将 SX1261 或 SX1262 模块接到 SPI 总线上。这是由社区贡献的支持;OpenMV 不销售 LoRa 硬件。(19576e1b1)
其他变更与改进#
requests库重写 —— 分块传输响应会被解码,公开了timeout参数(默认 5 秒),JSON 和字符串请求体会被正确序列化/编码,响应头解析不区分大小写且状态行按位置解析,调用方的headers字典会被复制(因此auth不再泄漏到后续请求中),响应体则分块读入一个bytearray。(889f0b096)mqtt会延迟导入ssl,因此该模块和普通(非 TLS)连接在不支持 SSL 的固件版本上也能工作。(c2e0899e8)JPEG 输出缓冲区大小根据可用内存确定,而不再是固定的 1 MB 上限;编码器会在图像写出之前将缓冲区裁剪到编码后的大小,因此大尺寸传感器的高质量拍摄不再失败。(aa1ca6776)
机器学习后处理清理 —— Darknet、Edge Impulse 和 Ultralytics 后处理器的边界框生成已统一,YOLOv8 输出在阈值筛选后会被转置,从而像其他模型一样按每个检测结果一行来存储。(b5c7853f2、c45f99cad)
PAG7936 安全关机 —— 新的传感器停止标志会在切换 RESET/PWDN 之前让 PAG7936 进入睡眠,修复了传感器通过延长线使用时的死锁问题。驱动还会在运行时(而非构建时)根据传感器的 I2C 地址选择 MIPI CSI-2 或并行寄存器集。(10026530c、f6acf14ec、5f0251c74)
ImageIO的回放节奏控制现在会在等待帧间间隔时处理待处理的事件,而不是在WFI中休眠。(a54790193)MicroPython 子模块更新 —— QEMU 移植版获得完整的网络支持,STM32 上通过备份 SRAM/BKP 寄存器实现
machine.mem_backup,STM32F4 上保持禁用 SDIO 硬件流控,以及对不可缓存区域复用 MPU 属性。(31ada5626、ab61d27a6、06bddf13a)differential-translation.py现在通过其csi.CSI对象采集图像,而不再使用已弃用的sensor模块;示例中的 import 间距也已统一。(68c866e7f、9ceff4513)
错误修复#
摄像头与传感器:
连续设置两个自定义
(w, h)分辨率会让传感器停留在第一个尺寸,而csi.CSI.width()/height()却报告第二个尺寸;“无变化”快速路径现在会排除自定义尺寸。(cfbb70156)csi.CSI.lens_correction()曾将radi和coef解析为布尔值,导致传给驱动的值并非所给定的值。(ab44b3a77)PAG7936:连续设置曝光和增益时二者会互相覆盖(两者共用同一个手动模式,且回读寄存器滞后一帧);驱动现在仅在进入手动模式时初始化手动寄存器,并且只写入正在设置的那个控制项。(e7a18c0a0)
在 OpenMV N6 上,PAG7936 和 PS5520 的 BAYER 采集在全分辨率以外的任何尺寸下都会以“Failed to initialize the CSI interface”失败,因为 N6 的 ISP 无法对原始 Bayer 数据进行缩放。这些传感器现在会以与所请求尺寸匹配的原生模式运行 BAYER 采集(PS5520:VGA、HD、SXGA-M、FHD、QHD、WQXGA2;QVGA 没有原生模式,因此不受支持)。(fe6790055、b7622b245)
图像编码与保存:
软件 JPEG 编码器:由于 realloc 路径被禁用,输出缓冲区溢出以前总是致命错误;现在当编码器拥有该缓冲区时会将其增大(每次增大一半),在内存耗尽时干净地回退,并裁剪结果。
jpeg_write/mjpeg_write在编码失败后不再写出缓冲区。(28e1110be)硬件 JPEG 编码器(STM32):溢出时会返回一张只编码了一部分的图像并附带分配大小,产生没有 EOI 标记的截断文件(例如以质量 100 拍摄 5 MP 图像时下半部分为空白)。现在它们会报告溢出,失败时释放缓冲区,成功时裁剪缓冲区。(22ce9619c)
VC8000 编码器以前会对每个请求都静默输出 4:2:0 色度;非 4:2:0 的子采样现在会回退到支持它的硬件编解码器。(71f2be7f2)
用
image.Image.save()将 PNG 图像保存为.jpg会写出多达 1 MB 未初始化的堆内存,将 JPEG 图像保存为.png则会写出一个空文件。两者现在都会引发异常;已经是目标格式的图像仍会原样复制。(6c1fdf822)png_write现在会检查压缩器的返回值,并在创建文件之前先进行压缩,因此失败时不再留下空文件。(557f52d66)
图像模块:
在 BAYER 和 YUV 图像上以
rgbtuple=True调用image.Image.get_pixel()时,会把转换后的像素写到栈局部变量之外由调用方控制的偏移处。(13f2a17b6)gif.Gif.add_frame()移除了一个不可达的 Bayer/YUV 转换分支;只接受 GRAYSCALE 和 RGB565 图像,这一点 Python API 原本就已强制执行。(f20d99d96)
协议:
在所有槽位都已被占用时注册动态通道会导致越界写入;现在会先检查通道 ID,并通过静态断言确保保留的通道 ID 能放入通道表。(68f635d1f)
动态通道对象在软复位后仍保留在根指针中,导致 GC 持续扫描上一次运行遗留的过期堆块;现在该表会在初始化时被清空。(ef0ebb03b、e23641145)
移植:
i.MX RT:补上了软复位时缺失的 CAN 和 Encoder 反初始化。(7cb9d3dc2)
硬件与开发板支持#
OpenMV N6 —— 现已启用 CAN(
machine.CAN)。(294f0febd、91e1c9573)OpenMV AE3 —— 为 AE3 电池扩展板 上的 microSD 插座固化了
sdcardSPI 驱动。(a258f4814)OpenMV RT1062 —— 固化了
lora-sx126x/lora-sync/lora-async(参见“新功能”)。FLIR Lepton 3.1R / UW —— 在所有支持 Lepton 的开发板上均可被 Lepton 驱动识别。
PAG7936 —— 安全关机停止、运行时接口选择、曝光/增益修复,以及 N6 上的 BAYER 采集。
PS5520 —— N6 上以原生尺寸进行 BAYER 采集。
模拟器(MPS2_AN500) —— 网络支持。
所有开发板 ——
sensor兼容模块已固化进每块开发板的清单中。
破坏性 API 变更#
v5.0.1 没有对 csi 或 image API 引入任何破坏性变更。在 v5.0.0 上运行的脚本无需修改即可继续运行,但有以下几处行为变更。
sensor 模块 (behavior)#
原生的 sensor 模块(modules/py_csi.c)已被移除,取而代之的是一个在 csi 之上实现相同 API 的固化 Python 模块。参见 sensor。
提交: 5e3d40631
import sensor会在终端打印WARNING: The sensor module is deprecated and will be removed in a future release. Please use the csi module instead.。隐藏的
csi.CSI对象会在导入时创建,因此缺少摄像头时现在会在import sensor处报错,而不是在sensor.reset()处。以下函数现在会引发
OSError,因为csi中没有对应功能:get_fb()、alloc_extra_fb()、dealloc_extra_fb()、set_auto_rotation()、get_auto_rotation()、disable_delays()、disable_full_flush()。(两个*_extra_fb函数在 v5.0.0 中就已经会引发异常。)对于最后两个,请使用csi.CSI(delays=False)/csi.CSI(fflush=False)。csi未导出的 framesize(QQCIF、QQSIF、QQQQVGA、HQQQQVGA、HQQQVGA、HQQVGA、B64X32、B64X64、B128X64、B128X128、B160X160、B320X320、LCD、QQVGA2)现在是作为自定义分辨率透传的(w, h)元组。当摄像头仍处于所设置的尺寸时,get_framesize()会返回所设置的那个元组,因此与常量的相等比较继续有效。ioctl()在请求之后最多转发四个参数。sleep()/shutdown()失败时仍像以前一样引发OSError(csi的RuntimeError会被转换)。
请迁移到 csi —— 参见 v5.0.0 迁移表。
帧缓冲区图像加载 (behavior)#
将图像加载到帧缓冲区的函数——带 copy_to_fb=True 的 image.Image、带 copy_to_fb=True 的 image.Image.copy() / image.Image.scale() 及 to_* 转换、image.ImageIO.read(),以及带 copy_to_fb=True 的 fir.snapshot() 和 tof.snapshot()——以前会立即把新图像推送到 IDE 预览,因此绘制在返回图像上的任何内容都永远看不到。它们现在会把帧缓冲区标记为待刷出,并在下一张图像覆盖它之前,连同绘制在其上的全部内容一起刷出——与 csi.CSI.snapshot() 的行为相同。
提交: 87b6db5ee、7b1a35c2f、f773c740d
读取图像流的循环无需任何修改,并且现在能显示其绘制的内容。而只加载单张图像便结束的脚本必须显式地刷出它:
img = image.Image("example.bmp", copy_to_fb=True)
img.flush() # new: send it to the IDE preview now
time.sleep_ms(1000)
机器学习库 (behavior)#
仅影响继承或复制了随附后处理器的代码。YoloV8 现在会在阈值筛选后转置模型输出,使保留下来的检测结果按每个框一行存储(bb[:, 4:]),与 Darknet 和 Edge Impulse 后处理器一致,而不再是每个框一列(bb[4:, :])。公开的 ((x, y, w, h), score) 结果保持不变。
protocol 模块 (minor)#
protocol.poll() 回归了。v5.0.0 在协议任务移至定时器时移除了它;现在它作为为自定义传输层显式驱动该任务的方式回归。其他一切均未改变——从未调用过它的脚本不受影响。
提交: c00c215f9
构建/工具链 (tooling)#
这些都不影响 MicroPython 脚本。py_image.c 已被拆分:描述符对象(级联分类器、关键点、LBP、关键点匹配)移至 py_image_descriptor.c,统计对象(相似度、统计信息、百分位、阈值、直方图)移至 py_image_stats.c。imlib 编码器 API 已与 imlib 的其余部分保持一致:jpeg_compress / png_compress 成功时返回 0,出错时返回 -1,并且 jpeg_compress 去掉了其 realloc 参数(是否调整大小取决于缓冲区所有权)。omv_protocol_channel.h 中的 OMV_PROTOCOL_MAX_CHANNELS 更名为 OMV_PROTOCOL_CHANNEL_MAX,各移植版现在会在初始化时调用 py_protocol_init0()。OMV_PAG7936_MIPI_CSI2 开发板定义已被移除(接口在运行时选择)。此外还有 CI 依赖版本升级和一个空版本更新日志的修复。与分支相关的细节请参阅 固件仓库 的历史记录。
迁移检查清单#
仍在使用
import sensor吗? 它仍可工作,但会有警告。请迁移到csi(v5.0.0 迁移);用csi.CSI()构造函数参数替换disable_delays/disable_full_flush,并移除get_fb/ 自动旋转调用。用
copy_to_fb=True加载单张图像并希望看到它? 请在加载后添加img.flush()。复制了 YOLOv8 后处理器? 请按行(
bb[:, 4:])而不是按列索引阈值筛选后的输出。在设置元组之后比较
csi0.framesize()的返回值? 它现在返回csi.CUSTOM。其他一切:重新刷写固件并运行即可。