protocol --- Các kênh giao thức OpenMV#
Mô-đun protocol cung cấp giao thức máy chủ OpenMV cho Python. Nó cho phép ngăn xếp giao thức phía firmware được khởi tạo và cấu hình, đồng thời cho phép mã người dùng đăng ký các kênh logic tùy chỉnh được hỗ trợ bởi một đối tượng Python triển khai giao diện kênh (read, write, size, poll, v.v.). Đây là thứ mà các công cụ đồng hành trên máy tính để bàn nói chuyện khi chúng truyền trực tiếp dữ liệu ảnh hoặc hiển thị các tiện ích tương tác cho camera đã kết nối.
Ví dụ#
Truyền trực tiếp ảnh RGB565 tới công cụ máy chủ sử dụng backend tùy chỉnh triển khai giao diện kênh thô (backend.size(), backend.shape(), backend.poll(), backend.read()):
import csi
import protocol
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.HD)
img = csi0.snapshot()
img_mv = memoryview(img.bytearray())
frame_ready = True
class FrameChannel:
def size(self):
return len(img_mv)
def shape(self):
return (img.height(), img.width(), len(img_mv))
def poll(self):
return frame_ready
def read(self, offset, size):
global frame_ready
end = offset + size
chunk = img_mv[offset:end]
if end >= len(img_mv):
frame_ready = False
return chunk
protocol.register(name="frame", backend=FrameChannel())
while True:
if not frame_ready:
img = csi0.snapshot()
img_mv = memoryview(img.bytearray())
frame_ready = True
Tập lệnh phía máy chủ tương ứng, sử dụng gói Python openmv (pip install openmv) để kết nối, đẩy tập lệnh trên camera và kéo từng khung hình:
import cv2
import numpy as np
from openmv.camera import Camera
# The on-cam script above, stored as a string (or read from a file).
SCRIPT = open("frame_streamer_on_cam.py").read()
with Camera("/dev/ttyACM0", baudrate=921600) as cam:
cam.stop()
cam.exec(SCRIPT)
while True:
status = cam.read_status()
if not cam.has_channel("frame") or not status.get("frame"):
continue
h, w, size = cam._channel_shape(cam.get_channel(name="frame"))
if cam.channel_size("frame") < size:
continue
data = cam.channel_read("frame", size)
rgb565 = np.frombuffer(data, dtype="<u2").reshape(h, w)
# Unpack RGB565 to an HxWx3 uint8 RGB image.
r = ((rgb565 >> 11) & 0x1F) << 3
g = ((rgb565 >> 5) & 0x3F) << 2
b = ( rgb565 & 0x1F) << 3
frame = np.dstack([r, g, b]).astype(np.uint8)
# Display with OpenCV (cv2 expects BGR, not RGB).
cv2.imshow("OpenMV", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
if cv2.waitKey(1) == ord("q"):
break
cv2.destroyAllWindows()
Thay /dev/ttyACM0 bằng cổng serial của camera (ví dụ: COM3 trên Windows). Hàm khởi tạo openmv.camera.Camera chấp nhận các tham số giao thức tương tự như init (crc / seq / ack / events / max_payload / max_retry / timeout) khi ngăn xếp phía camera đã được cấu hình lại để khớp.
Hàm#
- protocol.init(crc: bool = True, seq: bool = True, ack: bool = True, events: bool = True, max_payload: int = ..., rtx_retries: int = 3, rtx_timeout_ms: int = 500, lock_interval_ms: int = 10, poll_ms: int = 0) None#
Khởi tạo (hoặc cấu hình lại) ngăn xếp giao thức và đăng ký các kênh dữ liệu logic mặc định (
stdin,stdout,streamvà, nếu được biên dịch kèm,profile). Ném raRuntimeErrornếu khởi tạo thất bại. Firmware khởi động với một ngăn xếp giao thức USB mặc định đã chạy sẵn, nên chỉ cần gọi hàm này khi muốn đổi transport hoặc ghi đè các tham số đóng khung mặc định. Gọi lại hàm này sẽ khởi tạo lại ngăn xếp: bộ hẹn giờ polling trước đó bị gỡ bỏ và các kênh được đăng ký lại từ đầu.crcbật kiểm tra CRC trên các khung giao thức.seqbật theo dõi số thứ tự.ackbật xác nhận từng khung.eventsbật thông báo sự kiện kênh.max_payloadlà kích thước tải trọng tối đa tính bằng byte. Nếu bỏ qua, giá trị mặc định của từng camera bên dưới sẽ được sử dụng; nó được lấy từ kích thước bộ đệm giao thức của mỗi bo mạch theo công thứcbuffer - 10 (header) - 4 (CRC).Camera
Kích thước bộ đệm
Tải trọng tối đa
OpenMV Cam M4 (
OPENMV2)512
498
OpenMV Cam M7 (
OPENMV3)512
498
OpenMV Cam H7 (
OPENMV4)512
498
OpenMV Cam H7 Plus (
OPENMV4P)4096
4082
OpenMV Pure Thermal (
OPENMVPT)4096
4082
OpenMV Cam RT1062 (
OPENMV_RT1060)4096
4082
OpenMV Cam N6 (
OPENMV_N6)8192
8178
OpenMV AE3 (
OPENMV_AE3)8192
8178
Arduino Portenta H7 (
ARDUINO_PORTENTA_H7)4096
4082
Arduino Giga (
ARDUINO_GIGA)4096
4082
Arduino Nicla Vision (
ARDUINO_NICLA_VISION)4096
4082
rtx_retrieslà số lần thử truyền lại. Mặc định là3.rtx_timeout_mslà thời gian chờ truyền lại tính bằng mili giây (tăng gấp đôi sau mỗi lần hết thời gian chờ). Mặc định là500.lock_interval_mslà khoảng thời gian khóa tối thiểu tính bằng mili giây. Mặc định là10.poll_mslà khoảng thời gian thăm dò tính bằng mili giây.0(mặc định) vô hiệu hóa thăm dò bằng bộ định thời.
- protocol.is_active() bool#
Trả về
Truenếu máy chủ đang kết nối và ngăn xếp giao thức đang hoạt động, ngược lại trả vềFalse.
- protocol.poll() int#
Chạy một vòng lặp của tác vụ giao thức: xả bộ đệm nhận của transport, phân tích mọi khung đã hoàn chỉnh, điều phối các lệnh và xử lý các sự kiện kênh. Thông thường firmware tự làm việc này cho bạn từ ngắt USB và từ bộ hẹn giờ
poll_ms, nên hàm này chỉ cần thiết cho các transport tùy chỉnh không được điều khiển bởi cả hai cơ chế đó -- ví dụ khipoll_ms=0và transport được poll từ vòng lặp chính của tập lệnh.Trả về
0khi thành công và-1nếu không có transport đang hoạt động nào được đăng ký hoặc lời gọi được thực hiện từ ngữ cảnh ngắt.
- protocol.register(name: str, *, backend: object, flags: int = 0) ProtocolChannel#
Đăng ký đối tượng Python
backendnhư một kênh logic mới và trả về handleProtocolChannel. Các phương thức khả dụng của đối tượngbackend(xem Giao diện Backend bên dưới) xác định khả năng của kênh;protocol.CHANNEL_FLAG_READ,protocol.CHANNEL_FLAG_WRITEvàprotocol.CHANNEL_FLAG_LOCKđược tự động thêm vàoflagskhi các phương thức tương ứng được triển khai.namelà tên kênh dưới dạng chuỗi. Bị cắt ngắn theo kích thước bộ đệm tên kênh của firmware. Bắt buộc.backendlà đối tượng Python triển khai giao diện backend. Bắt buộc. Thường được truyền theo từ khóa (backend=...).flagslà các bit cờ kênh bổ sung (xem hằng sốCHANNEL_FLAG_*). Tùy chọn; mặc định là0.Phát sinh
RuntimeErrornếu kênh không thể được đăng ký (ví dụ: không còn khe kênh trống).
Lớp#
- class protocol.ProtocolChannel#
Handle được trả về bởi
protocol.register. Các thực thể không được khởi tạo trực tiếp.
Giao diện Backend#
Đối tượng backend được truyền tới protocol.register có thể triển khai bất kỳ tập hợp con nào của các phương thức sau. Chỉ các phương thức hiện diện trên đối tượng mới được kết nối với lớp giao thức C; các phương thức bị thiếu sẽ để khả năng tương ứng bị vô hiệu hóa.
- class protocol.backend#
Đối tượng backend kênh được truyền tới
protocol.register. Các phương thức bên dưới mô tả giao diện tùy chọn mà một backend Python có thể triển khai.- init() object#
Được gọi một lần khi kênh được khởi tạo. Trả về bất kỳ giá trị nào khác
Nonekhi thành công; ngoại lệ hoặc thiếu giá trị trả về được coi là lỗi.
- shape() tuple#
Trả về một tuple gồm tối đa bốn số nguyên mô tả hình dạng dữ liệu (ví dụ: kích thước ảnh). Tối đa bốn phần tử được lớp giao thức sử dụng.
- read(offset: int, size: int) bytes#
Trả về tối đa
sizebyte bắt đầu từoffsetdưới dạng đối tượng giốngbyteshỗ trợ giao thức bộ đệm.
- readp(offset: int, size: int) bytes#
Biến thể không sao chép của
read. Trả về bộ đệm có bộ nhớ cơ bản được lớp giao thức đọc trực tiếp; bộ đệm phải hợp lệ trong suốt quá trình truyền.
- write(offset: int, data: bytearray) int#
Ghi
datatạioffset.datalàbytearraytham chiếu trực tiếp tới bộ đệm C. Trả về số byte đã ghi, hoặc0khi thành công mặc định.
- class protocol.CBORChannel(on_read: Callable | None = None, on_write: Callable | None = None)#
Một backend Python cấp cao hơn (do gói
protocolđóng băng cung cấp) tuần tự hóa các trường có tên sang CBOR bằng các khóa số nguyên tương thích SenML. Hỗ trợ các widget hiển thị (label,text,depth,waveform) và các điều khiển tương tác (toggle,pushbutton,slider,spinbox,select,radio,lineedit) với các callbackon_read/on_write.on_readlà callable tùy chọnon_read(channel)được gọi trước khi kênh được tuần tự hóa cho máy chủ. Dùng để làm mới giá trị trường.on_writelà callable tùy chọnon_write(channel, name, value)được gọi khi máy chủ ghi giá trị mới cho một trường có tên.- add(name: str, type: str, value: Any = None, unit: str | None = None, min: int | float | None = None, max: int | float | None = None, step: int | float | None = None, options: list | None = None, width: int | None = None, height: int | None = None, sample_rate: float | None = None, series: int = 1, typecode: str | None = None) None#
Thêm một trường có tên vào kênh.
namelà tên hiển thị; phải là duy nhất trong kênh này.typelà loại widget:"label"-- hiển thị số chỉ đọc vớiunittùy chọn."text"-- hiển thị chuỗi chỉ đọc."toggle"-- công tắc boolean."pushbutton"-- hành động tức thời (kích hoạt, hiệu chuẩn, ...). Máy chủ ghiTruekhi nhấp và không hiển thị trạng thái nào."slider"-- điều khiển số vớimin/max/step."spinbox"-- nhập số chính xác theo bước; dùng chung các trường của slider và dạng cập nhật bằng tuple(min, max, value)."select"-- danh sách thả xuống gồm các chuỗioptions."radio"--optionsđược hiển thị dưới dạng một nhóm nút radio."lineedit"-- ô nhập chuỗi có thể ghi."depth"-- hiển thị bản đồ độ sâu 2D kích thướcwidthxheight."waveform"-- đồ thị chuỗi thời gian 1D của các mẫu xen kẽ.
valuelà giá trị ban đầu. Giá trị mặc định phụ thuộc vàotype:0cholabel,""chotext/lineedit/select/radio,Falsechotoggle/pushbutton,minchoslider/spinbox.unitlà chuỗi đơn vị cholabel/slider/spinbox/waveform(ví dụ"Cel","%RH").minlà giá trị nhỏ nhất (phạm vi của slider/spinbox, hoặc phạm vi hiển thị của depth/waveform).maxlà giá trị lớn nhất (phạm vi của slider/spinbox, hoặc phạm vi hiển thị của depth/waveform).steplà kích thước bước (slider/spinbox).optionslà danh sách các chuỗi tùy chọn (select/radio), hoặc danh sách tên của từng chuỗi dữ liệu (waveform).widthlà chiều rộng tính bằng điểm ảnh (depth).heightlà chiều cao tính bằng điểm ảnh (depth).sample_ratelà tốc độ lấy mẫu tính bằng số mẫu mỗi giây (waveform). Được gửi tới máy chủ dưới dạng chu kỳ lấy mẫu.serieslà số chuỗi dữ liệu xen kẽ trong mỗi khối mẫu (waveform), ví dụ3cho một đường ghi gia tốc kế XYZ.typecodelà typecodearraycủa các mẫu waveform (ví dụ"H","h","f"). Mặc định là"H"(16-bit không dấu).
- __getitem__(name: str) object#
Trả về giá trị hiện tại của trường có tên đã cho. Với các trường
depthvàwaveformthì bộ đệm dữ liệu nhị phân được trả về, ngược lại là giá trị vô hướng.
- __setitem__(name: str, value: Any) None#
Đặt giá trị của trường có tên đã cho. Với các trường
slidervàspinbox, một tuple(min, max, value)cập nhật đồng thời phạm vi và giá trị hiện tại. Với các trườngdepth,valuelà bộ đệm dữ liệu nhị phân. Với các trườngwaveform,valuelà một khối dạng bytes gồm các mẫu xen kẽ theotypecodecủa trường; số mẫu trên mỗi chuỗi dữ liệu được suy ra từ độ dài của khối và một dấu thời gian đơn điệu (tính bằng micro giây, tích lũy từtime.ticks_us()) được đính kèm để máy chủ có thể đặt khối dữ liệu lên trục thời gian của nó.
- poll() bool#
Phương thức giao diện backend. Trả về
Truekhi dữ liệu đã tuần tự hóa sẵn sàng cho máy chủ.
- size() int#
Phương thức giao diện backend. Gọi
on_read(nếu được đặt) và trả về kích thước của bộ đệm đã tuần tự hóa.
Hằng số#
Các bit cờ kênh (kết hợp theo bit; được truyền tới protocol.register qua flags hoặc được đặt tự động dựa trên các phương thức của backend).
- protocol.CHANNEL_FLAG_PHYSICAL: int#
Kênh đại diện cho một phương tiện truyền tải vật lý (khác với kênh dữ liệu logic).
Định danh kênh tích hợp sẵn.