13.3.1.6. API 参考¶
openmv 包对外暴露的接口主要是用于与摄像头通信的 Camera 类,以及用于表示协议错误的 OMVException 异常体系。两者均在本页中说明。
13.3.1.6.1. Camera 类¶
- class openmv.Camera(port: str, *, baudrate: int = 921600, crc: bool = True, seq: bool = True, ack: bool = True, events: bool = True, timeout: float = 1.0, max_retry: int = 3, max_payload: int = 4096, drop_rate: float = 0.0)¶
通过 USB 串口与 OpenMV 摄像头通信的主机端代理。
- 参数:
port -- 串口设备路径。在 Linux 上,USB CDC 设备为
/dev/ttyACMx,USB 转 UART 桥接设备为/dev/ttyUSBx。在 macOS 上为/dev/tty.usbmodem...或/dev/cu.usbmodem...。在 Windows 上为COMx。baudrate -- 串口波特率。通过 USB 连接时,
921600是一个魔法值,它会将摄像头从 MicroPython REPL 切换到 OpenMV 协议——在 USB 链路上使用其他任何值都会让摄像头停留在 REPL 模式,因此必须使用默认值。通过 UART 链路时,该值是实际的线路波特率,可以在两端自由设置。crc -- 对每个数据包启用 CRC 校验。
seq -- 启用每个数据包的序列号。
ack -- 要求数据包确认。
events -- 启用来自摄像头的事件通知。
timeout -- 每次操作的超时时间,单位为秒。
max_retry -- 数据包失败后引发异常前的重试次数。
max_payload -- 协商的最大有效负载大小,单位为字节。摄像头可能会向下协商。
drop_rate -- 仅供测试使用的丢包概率,取值范围为
[0.0, 1.0]。生产环境中请保持为0.0。
该类支持上下文管理器协议;
with Camera(port) as cam:会在进入时调用connect(),在退出时调用disconnect()。
13.3.1.6.2. 连接¶
13.3.1.6.3. 脚本执行¶
13.3.1.6.4. 流式传输¶
- Camera.streaming(enable: bool, raw: bool = False, resolution: tuple[int, int] | None = None) None¶
打开或关闭帧流,并选择传输时使用的格式。
- 参数:
enable --
True启用流式传输,False禁用它。raw -- 当为
False(默认)时,摄像头会在将每一帧放入流通道之前对其进行 JPEG 压缩,并由read_frame()在主机端解压缩。当为True时,摄像头会发送未经压缩的已捕获像素缓冲区——对于没有硬件 JPEG 支持的摄像头,这是正确的选择,因为在这种情况下软件压缩是循环中最慢的环节。resolution --
(width, height)指定摄像头在发送前将每一帧原始图像缩小到的目标尺寸,因为未压缩的帧比 JPEG 压缩后的帧大得多。当raw=True时为必填项;否则将被忽略。
13.3.1.6.5. 自定义通道¶
- Camera.channel_write(name: str, data: bytes) bool¶
将
data写入自定义通道。超过有效负载大小的写入会自动拆分到多个数据包中。- 参数:
name -- 由摄像头端脚本注册的通道名称。
data -- 要发送的类字节有效负载。
- 返回:
如果通道存在且写入已发送,则为
True,否则为False。
13.3.1.6.6. 设备自省¶
- Camera.version() dict¶
返回摄像头的协议、引导加载程序和固件版本三元组。在
connect()之后缓存。每个三元组都是一个由int组成的(major, minor, patch)元组:protocol_version—— 摄像头实现的 OpenMV 线路协议版本。bootloader_version—— 驻留在闪存中的引导加载程序映像。firmware_version—— 当前运行的 MicroPython 固件。
- Camera.system_info() dict¶
返回摄像头的硬件能力和内存信息。在
connect()之后缓存。返回字典的键分为四组。标识
cpu_id—— 32 位 CPU 标识符。device_id—— 由 3 个 32 位字组成的元组,即固化在芯片中的唯一设备序列号。chip_id—— 由 3 个 32 位字组成的元组,连接到摄像头的每个图像传感器对应一项。usb_vid—— USB 厂商 ID。usb_pid—— USB 产品 ID。
内存大小(均以千字节为单位)
flash_size_kb—— 内部闪存总量。ram_size_kb—— RAM 总量。framebuffer_size_kb—— 为图像捕获保留的 RAM。stream_buffer_size_kb—— 为向主机传送帧的流通道保留的 RAM。
能力标志(每个功能对应一个布尔值,均命名为
<feature>_present)gpu_present—— 图形处理单元。npu_present—— 神经处理单元。isp_present—— 图像信号处理器。venc_present—— 视频编码器。jpeg_present—— JPEG 硬件编码器。dram_present—— 外部 DRAM。crc_present—— CRC 加速器。pmu_present—— 性能监控单元。wifi_present—— Wi-Fi 无线电。bt_present—— 蓝牙无线电。sd_present—— SD 卡插槽。eth_present—— 以太网 PHY。multicore_present—— 多个 CPU 内核。
其他
usb_highspeed—— 布尔值,当 USB 以高速模式(USB 2.0 HS,480 Mbps)枚举时为True。pmu_eventcnt—— 可用的 PMU 事件计数器数量;没有 PMU 时为0。
13.3.1.6.7. 诊断¶
13.3.1.6.8. 性能分析器¶
性能分析器会针对已插桩的固件模块——目前为 image、ml 和 ulab——报告每个函数的调用次数以及最小 / 最大 / 总执行时间。函数的进入和退出会在编译期被拦截;运行时在每次进出时采样一个单调微秒计数器,按函数累加结果,并通过 profile 通道将该表暴露给主机。
只有在向 make 传入 PROFILE_ENABLE=1 时,性能分析器才会被编译进固件。出厂固件映像不包含它——构建系统为被跟踪模块添加的 -finstrument-functions 标志会带来不小的运行时开销,因此性能分析构建是针对特定调试会话从源码专门生成的。当固件未使用该标志构建时,profile 通道不会被注册,本页中的每个性能分析器方法都会静默返回而不执行任何操作。
Arm 性能监控单元(PMU)是 Cortex-M55 的硬件计数器块——一小组可配置的计数器,可在不拖慢被测代码的情况下跟踪周期数、缓存命中与未命中、分支行为以及其他架构定义的事件。在配备了 PMU 的摄像头上——即 AE3 和 N6,这两款是 OpenMV 产品线中基于 M55 构建的摄像头——性能分析器会在采集时序数据的同时采样这些计数器,事件总数会出现在每个按函数记录的条目中。没有 PMU 的摄像头仍会产生时序记录;事件字段会返回零,而 profiler_event() 是一个空操作。
- Camera.profiler_mode(exclusive: bool = False) None¶
在包含式和排他式计时之间切换。包含式计时会将被调用者的时间计入调用者;排他式计时则不会。
- 参数:
exclusive --
True选择排他式计时,False选择包含式计时。
- Camera.profiler_reset(config: list | None = None) None¶
清除所有性能分析计数器。
config=None还会恢复默认的 PMU 事件分配。- 参数:
config -- 为将来的每计数器配置覆盖预留。传入
None以保留默认值。
13.3.1.6.9. 子类化与通道内部机制¶
上面记录的方法涵盖了本包的所有常见用法。少数模式——处理主机希望响应的摄像头端事件、为多步交换锁定通道、与承载结构化数据而非字节流的通道通信,或驱动通道特定的控制命令——需要用到 Camera 以下划线为前缀保留的方法。这些名称按约定是私有的(Python 并不会对它们进行名称修饰),需要使用它们的应用程序应当对 Camera 进行子类化,或直接调用这些方法。
通过子类化来响应事件。 摄像头发出的每个事件都会经由 Camera._handle_event() 到达。对 Camera 进行子类化并重写该方法,是应用程序响应其摄像头端脚本所引发事件的方式;事件 页面完整地介绍了这一模式。
- Camera._handle_event(channel_id: int, event: int) None¶
分派来自摄像头的一个事件。每当事件数据包到达时,由传输层调用。在子类中重写以添加应用程序特定的处理;调用
super()._handle_event(...)以保留默认行为(在CHANNEL_REGISTERED时刷新通道列表、在stream通道上跟踪帧就绪状态、stdin通道的启动 / 停止日志记录)。- 参数:
channel_id -- 系统事件为
0,否则为已注册的通道 ID。event -- 事件标识符;系统事件的值来自
EventType枚举,通道事件的值则来自摄像头端通道后端所选定的值。
添加自身协议通信方法的子类应当用 retry_if_failed() 装饰它们,以便它们继承本页所有出厂方法都具有的同样的重新同步与重试行为。
- static Camera.retry_if_failed(func)¶
装饰器。包装一个实例方法,使其在传输层引发
ResyncException时重试一次。任何调用_send_cmd_wait_resp()(直接调用或通过某个_channel_*包装器调用)的方法都应携带此装饰器:class MyCamera(Camera): @Camera.retry_if_failed def my_custom_command(self, payload): return self._send_cmd_wait_resp(Opcode.MY_CMD, 0, payload)
通道锁定可确保通道状态在两个相关操作之间不会发生变化(例如,在一个不断追加数据的通道上,一次 _channel_size() 之后跟着一次 _channel_read())。read_frame() 和 read_profile() 在内部使用了它;以多步访问驱动自定义通道的应用程序也是如此。
- Camera._channel_lock(channel_id: int) bool¶
获取通道的独占锁。在锁被释放之前,对同一通道的其他主机操作将被阻塞。
- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。- 返回:
授予锁时为
True。
- Camera._channel_unlock(channel_id: int) bool¶
释放先前通过
_channel_lock()获取的锁。始终与加锁调用配对;使用try/finally以确保即使其间的读取引发异常,解锁也会发生。- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。
结构化通道承载的是结构化记录而非扁平的字节流。性能分析器通道就是出厂示例:其形状为 (record_count, record_size),想要知道有多少条记录在等待的主机读取的是形状而非字节大小。
- Camera._channel_shape(channel_id: int) tuple[int, ...]¶
读取通道的形状描述符。
- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。- 返回:
一个由无符号 32 位整数组成的元组,描述通道的布局。其含义因通道而异。
通道特定的控制命令——启动、停止、复位、配置——通过单个操作码(CHANNEL_IOCTL)传输,并附带一个通道特定的命令编号和一个可选的 struct.pack 有效负载。诸如 stop()、exec() 和 streaming() 等出厂方法都是对针对 stdin 和 stream 通道的 _channel_ioctl() 调用的薄封装;定义了自身 ioctl 菜单的自定义摄像头端通道也以同样的方式驱动。
- Camera._channel_ioctl(channel_id: int, cmd: int, fmt: str | None = None, *args) bytes | None¶
对通道发出一个 ioctl 命令。
- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。cmd -- 由摄像头端通道后端定义的命令编号。
args -- 与
fmt匹配的值。
- 返回:
通道返回的任何有效负载,或为
None。
公共通道方法的按 ID 字节流变体会跳过名称到 ID 的查找,并接受一个显式的字节 offset——这对于从大缓冲区中间读取一块数据很有用(例如 profile 通道的记录)。
- Camera._channel_size(channel_id: int) int¶
- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。- 返回:
通道上当前可用的字节数。
- Camera._channel_read(channel_id: int, offset: int, length: int) bytes¶
从
offset开始读取length个字节。多数据包读取会自动重新组装。- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。offset -- 开始读取的字节偏移量。
length -- 要读取的字节数。
- Camera._channel_write(channel_id: int, data: bytes, offset: int = 0) None¶
在给定的
offset处写入data。多数据包写入会自动拆分到多个数据包中。- 参数:
channel_id -- 数字通道 ID,通常通过
get_channel()解析得到。data -- 要写入的类字节有效负载。
offset -- 开始写入的字节偏移量。
协议原语是该类暴露的最底层接口——原始的发送命令、获取原始通道列表以及手动重新同步等条目,上面的一切最终都建立在它们之上。当发送该类尚未封装的操作码,或在子类中实现自定义恢复逻辑时,应用程序会用到它们。
- Camera._send_cmd_wait_resp(opcode: int, channel: int = 0, data: bytes = b'') bytes | None¶
发送一个协议命令并等待摄像头的响应。本节中其他每个方法都建立在这个原语之上。
- 参数:
opcode -- 命令编号。出厂的
Opcode枚举列出了固件附带的代码,但该参数只是一个整数——自定义固件构建可以定义并响应其自己的命令。channel -- 通道 ID,系统命令则为
0。data -- 命令特定的有效负载。
- 返回:
响应有效负载,对于像
Opcode.SYS_RESET和Opcode.SYS_BOOT这类会断开连接的命令则为None。
- Camera._channel_list() dict¶
从摄像头获取当前通道列表,而不触及
update_channels()所填充的缓存字典channels_by_id和channels_by_name。这对于想要直接检查摄像头通道状态的子类很有用。- 返回:
将通道 ID 映射到
{'name': str, 'flags': int}的字典。
- Camera._resync() None¶
从头重新运行协议握手。在初始连接时由
connect()自动调用,并由每个捕获传输层OMVException的公共方法自动调用。在子类中实现自身恢复循环的应用程序可以在处理完底层错误后直接调用此方法。
13.3.1.6.10. 异常¶
- exception openmv.OMVException¶
所有协议级错误的基类。下面的三个子类都继承自它,因此单个
except OMVException即可覆盖整个错误范围。
- exception openmv.TimeoutException¶
摄像头未在配置的超时时间内响应。是
OMVException的子类。
- exception openmv.ChecksumException¶
数据包的 CRC 不匹配。在协议耗尽其重试预算后引发。是
OMVException的子类。
- exception openmv.SequenceException¶
重试后到达的数据包带有意外的序列号。是
OMVException的子类。