教程:VENTUNO Q/MIPI-CSI 相机

来自WindwizPi wiki

概述

本指南介绍了如何从 Arduino® VENTUNO™ Q 上的 MIPI-CSI 摄像头模块 连接、检测和流式传输视频。该板具有由 Qualcomm® Dragonwing™ QCS8275 内置图像信号处理器 (ISP) 驱动的三个专用摄像头连接器,可直接在板上实现多摄像头边缘 AI 应用。


ventuno-camera-connector.png


说明:

目前仅支持IMX577摄像头模组。

相机硬件由单个后台服务“cam-server”(QMMF、高通多媒体框架、相机守护进程)管理,本指南中的每个捕获管道都作为客户端进行通信。下面进一步提供了三个即用型 Python 脚本,涵盖了查看 feed 的最常见方式:

脚本 输出 查看与 质量/效率
camera_test.py 连接到 VENTUNO Q 的本地显示器 没有什么额外的,视频出现在板上的一个窗口中 硬件 H264 往返(编码 + 解码),最适合测量本地管道延迟
camera_rtsp_server.py 通过网络传输 RTSP 流 VLC、“ffplay”或任何支持 RTSP 的播放器 最佳,低带宽下的硬件 H264
camera_mjpeg_server.py 网络上的 HTTP MJPEG 流 任何网络浏览器、纯 URL 或“<img>”标签 最简单的客户端,但带宽更高(每帧完整的 JPEG)

说明:

一次只能运行这些脚本之一(或任何其他相机客户端),“cam-server”为硬件提供单个活动捕获会话。在开始另一项之前先停止一项。

目标

  • 了解三个板载 MIPI CSI-2 相机连接器。
  • 安装硬件加速相机捕获所需的 GStreamer 插件。
  • 从板的外壳检测连接的摄像头模块。
  • 将视频流录制到文件中。
  • 将摄像机源本地流式传输到连接到 VENTUNO Q 的显示器。
  • 通过网络将摄像机源流式传输到 VLC(或其他 RTSP 播放器)。
  • 通过网络将摄像机源流传输到 Web 浏览器 (MJPEG)。

硬件与软件要求

硬件要求

软件要求

硬件概述

VENTUNO Q 具有三个专用 MIPI CSI-2 相机连接器(CAMERA0、CAMERA1、CAMERA2),每个连接器均由 Dragonwing™ QCS8275 内的 Qualcomm® Spectra™ 692 ISP 驱动。这允许同时连接最多三个独立摄像头,用于多摄像头边缘 AI 应用,例如立体视觉、360° 捕捉或多角度物体检测。

每个连接器都是一个 22 针 FPC 连接器(TF31-22S-0.5SH,0.5 mm 间距),带有完整的 4 通道 MIPI CSI-2 接口、一条 I²C 控制总线、一条 GPIO 控制线和一条 3.3 V 电源轨。所有高速数据通道 (RCLAMP0524TCT) 以及电源和 I²C 线路 (DF2B7ASL、L3F) 均提供 ESD 保护。

连接器 标签 界面 控制 力量
J3_1 相机2 MIPI CSI-2 4 通道 I²C4 + 1× GPIO (3.3V) 3.3V
J3_3 相机1 MIPI CSI-2 4 通道 I²C2 + 1× GPIO (3.3V) 3.3V
J3_2 相机0 MIPI CSI-2 4 通道 I²C0 + 1× GPIO (3.3V) 3.3V

每个连接器上的 GPIO 线(引脚 17)通常用于相机重置和电源启用控制。 I²C 总线(SCL/SDA,3.3 V)用于配置相机传感器寄存器。

访问开发板 Shell

要按照本指南中的步骤操作,您需要访问 VENTUNO Q 的 shell。这可以使用 ssh、adb 或通过在单板计算机 (SBC) 模式 下直接在开发板上打开终端来完成。

要通过 adb 连接,用 USB-C® 线缆连接 VENTUNO Q 与电脑,然后运行:

adb shell

要通过 ssh 连接,请确保 VENTUNO Q 与电脑连接到同一网络,然后运行:

ssh arduino@<ip-address>

说明:

有关远程访问开发板的更多替代方法,请参阅VENTUNO Q 用户手册。

连接相机模块

csi-connector.png


警告:

将摄像头模块连接到 CSI 连接器之一时,请确保 VENTUNO Q 电源已关闭。

将基于 IMX577 的摄像头模块连接到三个 CAMERA 连接器(CAMERA0、CAMERA1 或 CAMERA2)中的任意一个,确保柔性电缆的触点朝向连接器闩锁所示的正确方向。连接后,给开发板通电。

安装 GStreamer

要使用通过 MIPI-CSI 连接的相机,我们需要安装特定版本的“gstreamer”,更具体地说是“gstreamer1.0-plugins-qcom”。

在板上打开一个 shell(如上文所述),然后安装软件包:

sudo apt update
sudo apt upgrade -y
sudo apt install gstreamer1.0-plugins-qcom -y

安装后重启板子:

sudo reboot

检测相机

板重新启动后,打开一个新 shell 并运行以下命令来检查启动过程中是否检测到摄像头传感器:

sudo dmesg | grep Probe

您应该看到类似于以下内容的内容:

[   28.182686] CAM_INFO: CAM-SENSOR: cam_sensor_driver_cmd: 1250: Probe failed for cmk_imx577 slot:23, slave_addr:0x34, sensor_id:0x577
[   28.213671] CAM_INFO: CAM-SENSOR: cam_sensor_driver_cmd: 1250: Probe failed for cmk_imx577 slot:24, slave_addr:0x34, sensor_id:0x577
[   28.231128] CAM_INFO: CAM-SENSOR: cam_sensor_driver_cmd: 1250: Probe failed for cmk_imx577 slot:25, slave_addr:0x34, sensor_id:0x577
[   28.268640] CAM_INFO: CAM-SENSOR: cam_sensor_driver_cmd: 1290: Probe success for cmk_imx577 slot:26,slave_addr:0x34,sensor_id:0x577
[   28.688703] CAM_INFO: CAM-SENSOR: cam_sensor_driver_cmd: 1250: Probe failed for cmk_imx577 slot:27, slave_addr:0x34, sensor_id:0x577

查找“探测成功”行(在本例中为第四个条目)。这意味着相机已被识别并且正在工作。

注意:

预计会出现“探测失败”行。内核会扫描多个传感器驱动程序插槽,然后找到与您连接的相机相匹配的传感器驱动程序插槽,只有“探测成功”行才能确认工作连接。

捕捉视频记录

为了测试相机,我们可以录制视频流样本,并将其保存在本地。

运行以下命令,这将开始录制。使用“CTRL + C”结束录制。视频文件将位于运行命令的同一目录中(如果使用“location”标志指定,则位于不同的位置)。

gst-launch-1.0 -e qtiqmmfsrc camera=0 name=camsrc video_0::type=preview ! video/x-raw,format=NV12_Q08C,width=3840,height=2160,framerate=30/1,interlace-mode=progressive,colorimetry=bt601 ! queue ! v4l2h264enc capture-io-mode=4 output-io-mode=5 ! h264parse ! mp4mux ! queue ! filesink location=capture.mp4

说明:

“camera=0”参数选择 CAMERA0。请使用“camera=1”或“camera=2”从 CAMERA1 或 CAMERA2 进行录制。

要在本地计算机上查看录制内容,只需使用“adb pull /home/arduino/<recording>”或通过“scp pull arduino@<board-ip>:/home/arduino/<recording>”从保存视频的位置拉取它。

本地显示流

要在直接连接到 VENTUNO Q(通过 HDMI)的显示器上预览相机源,请使用“camera_test.py”脚本。它从相机捕获,通过硬件 H264 编码/解码来回传输帧,并通过“waylandsink”在窗口中渲染结果,每秒打印一次捕获到显示的延迟。

管道:qtiqmmfsrc → v4l2h264enc → h264parse → v4l2h264dec → waylandsink

注意:

您需要一台通过 HDMI 连接到 VENTUNO Q 的显示器才能使用此示例。

依赖关系

包裹 提供
qcom 相机服务器 拥有相机硬件的“cam-server”服务
gstreamer1.0-plugins-qcom-qmmfsrc qtiqmmfsrc,相机源元素
gstreamer1.0-plugins-qcom-good v4l2h264enc / v4l2h264dec,硬件 H264 编码/解码
gstreamer1.0-plugins-qcom-bad h264解析
libgstreamer-qcom1.0-0 核心管道元素(“队列”、总线)
python3-gi GStreamer 的 Python 绑定(import gi)

上述所有软件包均已预安装在 VENTUNO Q 板映像上。如果缺少一个,例如在自定义映像构建之后,请使用以下命令安装它们:

sudo apt update
sudo apt install -y qcom-camera-server gstreamer1.0-plugins-qcom-qmmfsrc gstreamer1.0-plugins-qcom-good gstreamer1.0-plugins-qcom-bad libgstreamer-qcom1.0-0 python3-gi

创建脚本

使用“nano”直接在板上创建脚本:

nano camera_test.py

或者,如果您想在计算机上写入文件,请使用“scp”或“adb”将其传输到开发板:

scp camera_test.py arduino@<ip-address>:/home/arduino/
# or, over USB-C®:
adb push camera_test.py /home/arduino/

粘贴下面脚本的内容,然后保存并退出(“CTRL+X”,然后“Y”,然后“Enter”)。

camera_test.py

import os
import signal
import sys
import time

# Must be set before GStreamer/Wayland touches the display — do this first,
# so the script works whether it's launched from the graphical session or
# from a plain SSH/serial shell that never inherited these.
os.environ.setdefault("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
os.environ.setdefault("WAYLAND_DISPLAY", "wayland-0")

import gi

gi.require_version("Gst", "1.0")
from gi.repository import Gst, GLib

_wayland_socket = os.path.join(
    os.environ["XDG_RUNTIME_DIR"], os.environ["WAYLAND_DISPLAY"]
)
HAS_DISPLAY = os.path.exists(_wayland_socket)

if HAS_DISPLAY:
    _SINK = "waylandsink name=sink sync=false"
else:
    print(
        f"no wayland compositor socket at {_wayland_socket} — "
        "running headless, dropping frames via fakesink instead of displaying them",
        file=sys.stderr,
    )
    _SINK = "fakesink name=sink sync=false"

PIPELINE_DESC = (
    "qtiqmmfsrc camera=0 name=camsrc video_0::type=preview ! "
    "video/x-raw,format=NV12_Q08C,width=1920,height=1080,framerate=30/1,"
    "interlace-mode=progressive,colorimetry=bt601 ! "
    "queue ! v4l2h264enc capture-io-mode=4 output-io-mode=5 ! h264parse ! "
    "v4l2h264dec ! video/x-raw,format=NV12 ! "
    f"queue ! {_SINK}"
)

PRINT_INTERVAL_S = 1.0


def main():
    Gst.init(None)
    pipeline = Gst.parse_launch(PIPELINE_DESC)
    sink = pipeline.get_by_name("sink")
    sinkpad = sink.get_static_pad("sink")

    last_print = [0.0]

    def on_probe(pad, info):
        buf = info.get_buffer()
        if buf is None or buf.pts == Gst.CLOCK_TIME_NONE:
            return Gst.PadProbeReturn.OK

        now_wall = time.monotonic()
        if now_wall - last_print[0] < PRINT_INTERVAL_S:
            return Gst.PadProbeReturn.OK
        last_print[0] = now_wall

        clock = pipeline.get_clock()
        if clock is None:
            return Gst.PadProbeReturn.OK
        running_time = clock.get_time() - pipeline.get_base_time()
        latency_ms = (running_time - buf.pts) / 1e6
        print(f"latency (capture -> encode -> decode -> display): {latency_ms:.2f} ms")
        return Gst.PadProbeReturn.OK

    sinkpad.add_probe(Gst.PadProbeType.BUFFER, on_probe)

    loop = GLib.MainLoop()

    def on_bus_message(bus, message):
        t = message.type
        if t == Gst.MessageType.ERROR:
            err, debug = message.parse_error()
            print(f"gst error: {err} ({debug})", file=sys.stderr)
        elif t == Gst.MessageType.EOS:
            loop.quit()
        return True

    bus = pipeline.get_bus()
    bus.add_signal_watch()
    bus.connect("message", on_bus_message)

    def shutdown():
        print("\nstopping...")
        pipeline.set_state(Gst.State.NULL)
        loop.quit()
        return GLib.SOURCE_REMOVE

    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, shutdown)

    pipeline.set_state(Gst.State.PLAYING)
    try:
        loop.run()
    finally:
        pipeline.set_state(Gst.State.NULL)


if __name__ == "__main__":
    main()

运行脚本

python3 camera_test.py

运行脚本后,您应该在终端中看到日志(延迟),并且显示屏上应该出现实时摄像头源。


ventuno-camera-stream-mipi-csi.png


VLC 流 (RTSP)

为了远程查看摄像头源,“camera_rtsp_server.py”将相同的捕获和硬件 H264 编码封装在嵌入式 RTSP 服务器(“GstRtspServer”)中,因此网络上任何支持 RTSP 的播放器都可以按需提取流。本地不显示任何内容,客户端自行解码。

管道(由 GstRtspMediaFactory 在内部构建和运行):qtiqmmfsrc → v4l2h264enc → h264parse → rtph264pay (pay0)

依赖关系

它建立在与本地显示流相同的软件包之上,加上:

包裹 提供
libgstrtspserver-1.0-0 RTSP服务器共享库
gir1.2-gst-rtsp-server-1.0 GstRtspServer 的 Python 绑定(from gi.repository import GstRtspServer)

libgstrtspserver-1.0-0 已预安装,但 gir1.2-gst-rtsp-server-1.0 未安装。安装它:

sudo apt update
sudo apt install -y gir1.2-gst-rtsp-server-1.0

您还需要在用于观看流的计算机或手机上安装 VLC 媒体播放器(或“ffplay”)。

创建脚本

使用“nano”直接在板上创建脚本:

nano camera_rtsp_server.py

或者,如果您想在计算机上写入文件,请使用“scp”或“adb”将其传输到开发板:

scp camera_rtsp_server.py arduino@<ip-address>:/home/arduino/
# or, over USB-C®:
adb push camera_rtsp_server.py /home/arduino/

粘贴下面脚本的内容,然后保存并退出(“CTRL+X”,然后“Y”,然后“Enter”)。

camera_rtsp_server.py

import os
import signal
import sys

# Must be set before GStreamer touches the display — do this first, before
# any gi import, so the script works from SSH/serial too, not just a
# graphical session.
os.environ.setdefault("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
os.environ.setdefault("WAYLAND_DISPLAY", "wayland-0")

import gi

gi.require_version("Gst", "1.0")
gi.require_version("GstRtspServer", "1.0")
from gi.repository import Gst, GstRtspServer, GLib

RTSP_PORT = "8554"
RTSP_MOUNT = "/test"

# Same qtiqmmfsrc capture + hardware h264 encode as camera_test.py, but
# payloaded as RTP instead of decoded back to a local waylandsink — the
# remote client does its own decoding.
PIPELINE_DESC = (
    "qtiqmmfsrc camera=0 name=camsrc video_0::type=preview ! "
    "video/x-raw,format=NV12_Q08C,width=1920,height=1080,framerate=30/1,"
    "interlace-mode=progressive,colorimetry=bt601 ! "
    "queue ! v4l2h264enc capture-io-mode=4 output-io-mode=5 ! h264parse ! "
    "rtph264pay name=pay0 pt=96"
)


def main():
    Gst.init(None)

    server = GstRtspServer.RTSPServer()
    server.set_service(RTSP_PORT)

    factory = GstRtspServer.RTSPMediaFactory()
    factory.set_launch(PIPELINE_DESC)
    # Shared: one capture/encode session feeds every connected client,
    # instead of trying to open the camera again per client.
    factory.set_shared(True)

    mounts = server.get_mount_points()
    mounts.add_factory(RTSP_MOUNT, factory)

    server.attach(None)

    loop = GLib.MainLoop()

    def shutdown(*_):
        print("\nstopping...")
        loop.quit()
        return GLib.SOURCE_REMOVE

    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, shutdown)
    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGTERM, shutdown)

    print(f"RTSP stream ready — connect from another device with:")
    print(f"  rtsp://<board-ip>:{RTSP_PORT}{RTSP_MOUNT}")
    sys.stdout.flush()

    try:
        loop.run()
    finally:
        pass


if __name__ == "__main__":
    main()

运行脚本

python3 camera_rtsp_server.py

找到开发板的 IP 地址(需要从其他设备连接):

hostname -I

在 VLC 中查看流

  1. 在与 VENTUNO Q 连接到同一网络的计算机上打开 VLC,然后转到 文件 > 打开网络...

cam-vlc-access.png


  1. 输入流 URL rtsp://<board-ip>:8554/test,将 <board-ip> 替换为您开发板的 IP 地址,然后单击 Open:

cam-vlc-access-2.png


ffplay rtsp://<board-ip>:8554/test
# or
gst-launch-1.0 rtspsrc location=rtsp://<board-ip>:8554/test ! rtph264depay ! h264parse ! avdec_h264 ! autovideosink

说明:

您还可以从终端使用“ffplay”或“gst-launch-1.0”而不是 VLC:

浏览器流 (MJPEG)

为了获得最简单的客户端体验,“camera_mjpeg_server.py”直接从“qtiqmmfsrc”捕获硬件编码的 JPEG 帧(不需要软件“jpegenc”/“videoconvert”,ISP 本身会发出 JPEG),并通过纯 HTTP 作为“multipart/x-mixed-replace”流提供它们,这是任何浏览器都可以直接渲染的经典 MJPEG 格式,无需额外的插件或播放器。

管道:“qtiqmmfsrc(图像/jpeg 输出)→ appsink”,后台 Python“http.server”在每个新帧到达时将其推送到连接的客户端。

依赖关系

它建立在与本地显示流相同的基础包之上,此外:

包裹 提供
gstreamer1.0-插件库 应用程序接收器

该脚本所需的所有软件包均已预安装在 VENTUNO Q 板映像上,无需额外安装。除了“python3-gi”之外,脚本本身仅依赖于Python的标准库(“threading”、“http.server”)。

创建脚本

使用“nano”直接在板上创建脚本:

nano camera_mjpeg_server.py

或者,如果您想在计算机上写入文件,请使用“scp”或“adb”将其传输到开发板:

scp camera_mjpeg_server.py arduino@<ip-address>:/home/arduino/
# or, over USB-C®:
adb push camera_mjpeg_server.py /home/arduino/

粘贴下面脚本的内容,然后保存并退出(“CTRL+X”,然后“Y”,然后“Enter”)。

camera_mjpeg_server.py

import os
import signal
import sys
import threading

# Must be set before GStreamer touches the display — do this first, before
# any gi import, so the script works from SSH/serial too, not just a
# graphical session.
os.environ.setdefault("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
os.environ.setdefault("WAYLAND_DISPLAY", "wayland-0")

import gi

gi.require_version("Gst", "1.0")
from gi.repository import Gst, GLib
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

HTTP_PORT = 8080
BOUNDARY = "frame"

# qtiqmmfsrc's video pad can emit hardware-encoded JPEG directly — no
# software jpegenc/videoconvert needed. Lower resolution/framerate than the
# H264 pipeline since each MJPEG frame is a full independent JPEG.
PIPELINE_DESC = (
    "qtiqmmfsrc camera=0 name=camsrc video_0::type=preview ! "
    "image/jpeg,width=1280,height=720,framerate=15/1 ! "
    "appsink name=sink emit-signals=true max-buffers=1 drop=true sync=false"
)

latest_frame = None
frame_lock = threading.Lock()
frame_available = threading.Condition(frame_lock)


def on_new_sample(sink):
    global latest_frame
    sample = sink.emit("pull-sample")
    if sample is None:
        return Gst.FlowReturn.OK
    buf = sample.get_buffer()
    ok, mapinfo = buf.map(Gst.MapFlags.READ)
    if not ok:
        return Gst.FlowReturn.OK
    data = bytes(mapinfo.data)
    buf.unmap(mapinfo)
    with frame_available:
        latest_frame = data
        frame_available.notify_all()
    return Gst.FlowReturn.OK


class MjpegHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path not in ("/", "/stream"):
            self.send_error(404)
            return
        self.send_response(200)
        self.send_header("Age", "0")
        self.send_header("Cache-Control", "no-cache, private")
        self.send_header("Pragma", "no-cache")
        self.send_header(
            "Content-Type", f"multipart/x-mixed-replace; boundary={BOUNDARY}"
        )
        self.end_headers()
        last_sent = None
        try:
            while True:
                with frame_available:
                    frame_available.wait(timeout=2.0)
                    frame = latest_frame
                if frame is None or frame is last_sent:
                    continue
                last_sent = frame
                self.wfile.write(f"--{BOUNDARY}\r\n".encode())
                self.wfile.write(b"Content-Type: image/jpeg\r\n")
                self.wfile.write(f"Content-Length: {len(frame)}\r\n\r\n".encode())
                self.wfile.write(frame)
                self.wfile.write(b"\r\n")
        except (BrokenPipeError, ConnectionResetError):
            pass

    def log_message(self, format, *args):
        pass  # quiet by default; comment out to debug per-request logging


def main():
    Gst.init(None)
    pipeline = Gst.parse_launch(PIPELINE_DESC)
    sink = pipeline.get_by_name("sink")
    sink.connect("new-sample", on_new_sample)

    bus = pipeline.get_bus()
    bus.add_signal_watch()

    def on_bus_message(bus, message):
        if message.type == Gst.MessageType.ERROR:
            err, debug = message.parse_error()
            print(f"gst error: {err} ({debug})", file=sys.stderr)
        return True

    bus.connect("message", on_bus_message)
    pipeline.set_state(Gst.State.PLAYING)

    server = ThreadingHTTPServer(("0.0.0.0", HTTP_PORT), MjpegHandler)
    threading.Thread(target=server.serve_forever, daemon=True).start()

    loop = GLib.MainLoop()

    def shutdown(*_):
        print("\nstopping...")
        server.shutdown()
        pipeline.set_state(Gst.State.NULL)
        loop.quit()
        return GLib.SOURCE_REMOVE

    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, shutdown)
    GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGTERM, shutdown)

    print("MJPEG stream ready — open in a browser at:")
    print(f"  http://<board-ip>:{HTTP_PORT}/stream")
    sys.stdout.flush()

    try:
        loop.run()
    finally:
        pipeline.set_state(Gst.State.NULL)


if __name__ == "__main__":
    main()

运行脚本

python3 camera_mjpeg_server.py

找到开发板的 IP 地址(需要从其他设备连接):

hostname -I

在浏览器中查看流

在与 VENTUNO Q 连接到同一网络的任何设备上打开浏览器,然后导航至“http://<board-ip>:8080/stream”。


cam-browser-access.png


您还可以使用普通的“<img>”标记将流嵌入网页中:

<img src="http://<board-ip>:8080/stream">

故障排除

`dmesg` 中没有“探测成功”行

如果找不到“探测成功”行,请验证:

  • 摄像头模块基于IMX577传感器。目前不支持其他传感器(将添加更多模块)
  • 柔性电缆完全插入并锁定在连接器的闩锁中。请注意,即使是最轻微的未对准也可能足以导致连接错误。另外,请确保电缆的方向正确。
  • 安装gstreamer1.0-plugins-qcom后**重新启动了开发板。

本地流显示黑屏

  • 在打开开发板电源之前,确认显示器已通过 HDMI 连接。
  • 检查脚本的输出是否报告正在无头运行。如果是,则未找到 Wayland 合成器套接字,请仔细检查显示连接。

“打开相机失败!”错误

cam-server 仅支持单个活动捕获会话。该错误通常意味着:

  • 另一个脚本(或客户端)已在使用相机。首先停止它,一次只能运行“camera_test.py”、“camera_rtsp_server.py”或“camera_mjpeg_server.py”之一。
  • 之前的脚本被不干净地终止(例如使用“kill -9”或崩溃),导致“cam-server”的内部会话状态卡住。通过重新启动服务来清除它:
  sudo systemctl restart cam-server

无法连接到 RTSP 或 MJPEG 流

  • 确认开发板和查看设备位于同一网络,并且您使用的是开发板当前的 IP 地址(“主机名 -I”)。
  • 检查板上没有任何东西(防火墙或“ufw”)阻止所需的端口,“8554/tcp”(用于 RTSP)或“8080/tcp”(用于 MJPEG)。
  • 两个流都不使用身份验证或 TLS,这在受信任的本地网络上很好,但在不添加您自己的身份验证或 VPN 的情况下,端口不应直接暴露到互联网。

总结

在本指南中,您学习了如何将 MIPI-CSI 摄像头模块连接到 Arduino® VENTUNO™ Q、安装所需的 GStreamer 插件、确认检测到摄像头以及使用 GStreamer 录制视频。您还可以设置三种不同的方式来查看实时源:本地在连接的显示器上、通过 RTSP 在 VLC 中远程以及通过 MJPEG 在 Web 浏览器中远程。从这里,您可以在这些管道的基础上进行构建,将相机帧输入到板上本地运行的边缘人工智能应用程序中。

说明:

要了解有关 VENTUNO Q 相机连接器的更多信息,请访问 VENTUNO Q 用户手册。


原文:https://docs.arduino.cc/tutorials/ventuno-q/mipi-csi-camera/ 本页为 Arduino 官方教程的中文翻译,供内部查阅。命令与代码保持原文。