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_AN500 QEMU 目标现在具备 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 插座固化了 sdcard SPI 驱动。(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) 结果保持不变。

提交: c45f99cad、b5c7853f2

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。

  • 其他一切:重新刷写固件并运行即可。