嘉楠科技
直播中

王尚岱

9年用户 1616经验值
擅长:电源/新能源
私信 关注
[问答]

请问在k230的Socket、MQTT等常用网络编程应用中如何实现图像传输呢?

在Socket、MQTT,或者网络通信应用中如何实现图像传输呢?
能给几个提示或者参考例程吗。谢谢
        micropython 请参考如下例子
https://developer.canaan-creative.com/k230_canmv/zh/main/zh/example/network/http_server.html
https://developer.canaan-creative.com/k230_canmv/zh/main/zh/example/media/sensor.html
        佬知道怎么传输了吗  方便说一下不
        

回帖(1)

张亮

2025-6-20 17:40:32

好的,明白了。在 K230 (CanMV) 上使用 MicroPython 通过 Socket 或 MQTT 传输图像,核心思路是将摄像头捕获的图像数据进行压缩(通常是 JPEG 格式),然后将压缩后的字节流通过网络发送出去。


你提供的两个例程(http_serversensor)非常关键,它们是实现图像传输的基础构件:




  1. sensor 例程 (media/sensor.html): 这是图像来源的关键。它展示了如何:



    • 初始化摄像头 (sensor.reset())

    • 设置图像分辨率 (sensor.set_framesize())

    • 捕获一帧图像 (img = sensor.snapshot())

    • 最重要的是: 将图像压缩为 JPEG 格式的字节流: img_bytes = img.compress(quality=XX) (其中 XX 是 JPEG 质量,1-99)




  2. http_server 例程 (network/http_server.html): 这是网络传输的基础。它展示了如何:



    • 连接到 Wi-Fi (wlan.connect())

    • 创建一个 TCP Socket 服务器 (socket.socket(), s.bind(), s.listen())

    • 接受客户端连接 (conn, addr = s.accept())

    • 接收 HTTP 请求数据 (recv())

    • 发送 HTTP 响应数据 (send(), sendall())。这里发送响应的部分,特别是发送 Content-Length 和二进制数据体的部分,是将 img_bytes 发送出去的模板。




如何实现图像传输:核心思路与步骤


你需要将上面两个例程的知识融合起来:




  1. 获取图像字节流


    import sensor
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)  # 常用格式
    sensor.set_framesize(sensor.QVGA)     # 根据网络带宽选择合适分辨率,如 QQVGA (160x120)
    sensor.skip_frames(time=2000)         # 等待摄像头稳定

    # 捕获一帧
    img = sensor.snapshot()
    # 压缩为JPEG,质量控制很重要(50-70是个平衡点)
    img_bytes = img.compress(quality=65)  # 这就是你要发送的核心数据!



  2. 建立网络连接



    • 选择协议:

      • Socket (TCP/UDP):

        • 服务器端: 建立一个 TCP Socket 服务器 (如 HTTP 例程)。

        • 客户端: 建立一个 TCP Socket 客户端 (可以使用 usocket 创建客户端连接)。


      • MQTT:

        • 导入 umqtt.simple 库 (MicroPython 常用 MQTT 库)。

        • 创建 MQTT 客户端 (client = umqtt.simple.MQTTClient(...)),设置好服务器地址、端口、客户端ID、用户名密码等。

        • 连接到 MQTT Broker (client.connect())。






  3. 设计数据传输方案



    • TCP Socket (Server):

      1. 监听客户端连接 (s.listen(1)).

      2. 接受连接 (conn, addr = s.accept()).

      3. 发送数据长度信息 (可选但推荐): 告诉接收端图像数据有多大。这有助于接收端正确解析。
        img_size = len(img_bytes)
        conn.sendall(str(img_size).encode('utf-8') + b'rnrn')  # 简单分隔符协议
        # 或者更规范的,构造自定义协议头

      4. 发送图像数据:
        conn.sendall(img_bytes)  # 一次性发送(小图没问题)
        # 或者循环发送(避免大图耗尽内存或网络缓冲区)
        # sent = 0
        # while sent < img_size:
        #     chunk_size = min(1024, img_size - sent)  # 每次发送1024字节
        #     conn.send(img_bytes[sent:sent+chunk_size])
        #     sent += chunk_size

      5. 关闭当前客户端连接 (conn.close()),或者保持连接传输多帧(需要设计帧分隔协议)。


    • TCP Socket (Client):

      1. 连接到服务器 (sock.connect()).

      2. 发送请求指令(如果需要特定指令触发图像传输)。

      3. 接收长度信息 (如果服务端发送了):
        # 简易读取直到分隔符(效率不高,仅示例)
        size_header = b''
        while b'rnrn' not in size_header:
            size_header += sock.recv(1)
        img_size = int(size_header.split(b'rn')[0])

      4. 接收图像数据:
        img_bytes = b''
        received = 0
        while received < img_size:
            chunk = sock.recv(min(4096, img_size - received))  # 每次接收最多4096字节
            if chunk:  # 处理可能的中途断连
                img_bytes += chunk
                received += len(chunk)
            else:
                break
        # 现在img_bytes包含了完整的一帧JPEG数据

      5. 关闭连接 (sock.close()).


    • MQTT:

      1. 连接到 Broker (client.connect()).

      2. 创建主题名,例如 "k230/camera/frame".

      3. 准备消息:

        • 基本方式: 直接将 img_bytes 作为消息负载 (payload) 发布。这是最简单的方式。
          client.publish("k230/camera/frame", img_bytes)

        • 带长度信息 (可选): 如果需要将长度信息或时间戳等元数据一起发送,可以先打包:
          import ustruct  # 用于打包基本数据类型
          # 打包一个简单的头:假设包含时间戳和图像大小 (4字节时间戳 + 4字节长度)
          timestamp_ms = time.ticks_ms()  # 获取当前时间戳(毫秒)
          img_size = len(img_bytes)
          header = ustruct.pack("II", timestamp_ms, img_size)  # I=4字节无符号整数
          # 发送:头 + 图像数据
          client.publish("k230/camera/frame", header + img_bytes)


      4. 注意 MQTT 消息大小限制: 不同的 Broker 有默认最大消息大小限制(例如 Mosquitto 默认约 268MB)。虽然理论上可以发送大图,但网络延迟和内存消耗巨大。强烈推荐降低分辨率和/或提高压缩率(降低 JPEG 质量)来减小发送包大小(最好控制在几十KB内)。

      5. 断开连接 (client.disconnect()),或者保持连接周期性地发布图像(使用定时器或循环)。

      6. (接收端也需要一个MQTT客户端订阅该主题,收到后将 payload 解包即可得到图片数据)。





  4. 循环
    大多数实时图像传输需求需要连续发送。在你主循环的核心部分:


    while True:
        # 1. 捕获并压缩图像
        img = sensor.snapshot()
        img_bytes = img.compress(quality=65)

        # 2. [可选] 可在此显示图像到本地LCD,方便调试
        # lcd.display(img)

        # 3. 通过网络传输 img_bytes (选择上面Socket或MQTT中一种方式实现)
        # ... (这里是上面第3步中对应协议的发送代码)

        # 4. 控制发送帧率,避免CPU/网络过载
        time.sleep_ms(100)  # 例如每秒大约10帧 (1000ms/100 = 10)



关键提示与优化建议:



  1. 性能与分辨率/质量: K230 资源有限。高分辨率、高帧率、高压缩质量会导致CPU和网络不堪重负。务必降低分辨率(如 QQVGA, HQVGA),降低 JPEG 质量(50-80 是常用范围),并控制发送帧率(time.sleep_ms(X))。 测试不同组合以找到平衡点。

  2. 错误处理: 网络可能不稳定。在 send/recv/publish/connect 等网络操作周围使用 try...except 块捕获异常(如 OSError, TimeoutError),并进行重试或重启连接。

  3. 分块传输: 对于较大的图像,分块发送或接收更安全,能避免大内存分配失败。上面 Socket 例子提供了循环发送的方式。MQTT 在消息层面没有分块标准,如果必须发大图,你可能需要在应用层做分片(如分成多个主题 frame/part1, frame/part2 等)。

  4. 图像格式: img.compress() 默认生成 JPEG。它是传输最紧凑的格式。避免发送原始 RGB565/RGB888 图像,数据量太大。

  5. 非阻塞/异步考虑 (高级): 单线程 MicroPython 中,耗时的网络发送会阻塞图像捕获。如果你需要高帧率:

    • 尽可能减少发送前的处理时间。

    • 将网络发送操作放在单独的线程中(如果 K230 MicroPython 支持且你有信心管理)。

    • 使用 uasycio (如果 K230 支持) 进行异步操作。

    • 使用内存缓存(如 bytearray)和 micropython.schedule 在中断或循环间隙中处理发送。


  6. 协议设计: 对于 Socket TCP 应用(非 HTTP),设计一个简单的应用层协议很有必要,用来区分不同的帧(尤其是传输多帧时)。常用的简单协议如:

    • 每次连接只传一帧(每次只连一次)。

    • 在图像数据前发送一个固定长度的头部,包含帧长度信息(如上文例子),便于接收端拆分连续到达的数据流。

    • 在帧之间发送分隔符(如 b'FRAMEEND'),接收端以此切分。但分隔符可能在图像数据中出现。


  7. K230 双核特性 (优化参考): 虽然你在用 MicroPython,但理解 K230 硬件架构(CPU+KPU)有助于未来优化。CPU 负责网络逻辑和上层控制,KPU 理论上能用于加速视频压缩(如 H.264/H.265)。但当前阶段的 MicroPython SDK 似乎主要通过 CPU 处理 JPEG 压缩(如 img.compress())。实时高效的视频流传输(如 RTSP)通常在裸机/C SDK 中实现更佳。


总结一下实现路径:



  1. 确定协议: 选 Socket (TCP/UDP) 还是 MQTT?Socket 更底层灵活,MQTT 有更成熟的 Broker/Client 生态。

  2. 实现图像捕获与压缩: 基于 sensor 例程,用 sensor.snapshot().compress(quality=XX) 获取 img_bytes

  3. 实现网络连接初始化:

    • Socket 服务器: 类似 http_server 的创建流程。

    • Socket 客户端: 使用 usocket 创建客户端并 connect

    • MQTT: 创建 MQTTClientconnect


  4. 实现数据传输逻辑:

    • Socket:

      • Server:accept() 循环内,发送 img_bytes (直接 sendall 或分块+可选包头)。

      • Client: 连接后,发送 img_bytes (或带包头/分隔符)。


    • MQTT: 使用 client.publish(topic, img_bytes)。考虑添加元数据包头。


  5. 集成到主循环:while True 循环中,连续执行捕获->压缩->传输->适当休眠。

  6. 测试与调优: 监控 CPU 使用率、网络带宽、帧率和延迟。调整分辨率、压缩质量、发送帧率参数达到最佳平衡。


虽然没有直接提供一个现成的 "socket_send_image.py" 或 "mqtt_send_image.py" 完整例程,但将 sensor 中的图像捕获/压缩和 http_server/MQTT 库中的网络发送部分按照上述逻辑组合起来,就是实现图像传输的核心。希望这些详细的思路和实现步骤能帮到你!开始动手组合代码测试吧,遇到具体问题可以再详细讨论。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分