You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派3B通过TCP Socket向Ubuntu笔记本传输摄像头图像故障排查

解决树莓派TCP传输摄像头图像到Ubuntu后结果异常的问题

我之前也碰到过完全一样的情况!用树莓派摄像头通过TCP传图到PC,结果保存的图片要么花屏、要么只有一半,甚至根本打不开——大概率是TCP流式传输的粘包/分包问题,或者图像数据的编码解码没对齐。结合你的场景,我整理了一套能正常工作的代码,顺便给你拆解下关键问题:

常见问题排查点

  • TCP粘包问题:TCP是流式协议,连续发送的字节会被合并成一个流,接收端不知道每帧图像的边界,导致拼接错误。
  • 图像编码不一致:直接传输PiRGBArray的原始数据体积大,且容易因为格式(比如RGB/BGR顺序)导致显示异常,最好先编码成JPG再传输。
  • 摄像头参数不匹配:树莓派摄像头初始化的分辨率、格式要和接收端处理逻辑统一。

完整代码实现

树莓派端(client.py)

补全你提供的代码片段,加入了图像编码、先传数据长度的逻辑:

from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
import socket
import struct

# 服务器(Ubuntu笔记本)的IP和端口
TCP_IP = '192.168.0.109'
TCP_PORT = 5000

# 初始化摄像头
camera = PiCamera()
camera.resolution = (640, 480)  # 统一分辨率,避免接收端处理异常
camera.framerate = 32
rawCapture = PiRGBArray(camera, size=(640, 480))

# 等待摄像头预热
time.sleep(0.1)

# 创建TCP客户端socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((TCP_IP, TCP_PORT))

try:
    for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
        # 获取当前帧的图像数组
        image = frame.array
        
        # 将图像编码成JPG,减少传输体积,同时统一格式
        encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
        result, img_encode = cv2.imencode('.jpg', image, encode_param)
        
        # 先发送图像数据的长度(用struct打包成4字节的整数)
        data_len = struct.pack('!I', len(img_encode))
        client_socket.sendall(data_len)
        
        # 再发送图像编码后的字节数据
        client_socket.sendall(img_encode)
        
        # 清空缓存,准备下一帧
        rawCapture.truncate(0)
        
        # 可选:按q退出
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
finally:
    client_socket.close()
    camera.close()
    cv2.destroyAllWindows()

Ubuntu笔记本端(server.py)

接收端需要先读取数据长度,再接收对应字节数的图像数据,避免粘包:

import socket
import struct
import cv2
import numpy as np

TCP_IP = '0.0.0.0'  # 监听所有网卡
TCP_PORT = 5000

# 创建TCP服务器socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((TCP_IP, TCP_PORT))
server_socket.listen(1)

print(f"等待树莓派连接... 监听端口 {TCP_PORT}")
conn, addr = server_socket.accept()
print(f"已连接:{addr}")

try:
    while True:
        # 先读取4字节的图像长度
        len_data = conn.recv(4)
        if not len_data:
            break
        img_len = struct.unpack('!I', len_data)[0]
        
        # 接收完整的图像数据
        img_data = b''
        while len(img_data) < img_len:
            chunk = conn.recv(min(img_len - len(img_data), 4096))
            if not chunk:
                break
            img_data += chunk
        
        # 解码图像并保存/显示
        if len(img_data) == img_len:
            img_np = np.frombuffer(img_data, dtype=np.uint8)
            image = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
            
            # 显示图像(可选)
            cv2.imshow('Received Image', image)
            
            # 保存图像到本地
            cv2.imwrite('received_image.jpg', image)
            
            # 按q退出
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
finally:
    conn.close()
    server_socket.close()
    cv2.destroyAllWindows()

关键注意事项

  1. 先传长度再传数据:用struct把图像长度打包成固定4字节的整数,接收端先读这4字节,就知道接下来要收多少图像数据,完美解决粘包问题。
  2. 统一图像编码:用cv2.imencode把图像转成JPG字节流,比直接传原始RGB数组小很多,而且跨平台兼容性更好。
  3. WiFi稳定性:如果WiFi信号不好,可能会出现丢包,你可以在代码里加简单的重传逻辑,或者用UDP(但UDP会丢包,适合对实时性要求高的场景)。
  4. 防火墙设置:确保Ubuntu笔记本的5000端口没有被防火墙挡住,可以临时关闭防火墙测试:sudo ufw disable(测试完记得打开)。

你可以先运行Ubuntu端的server.py,再运行树莓派的client.py,应该就能得到正常的图像了!

内容的提问来源于stack exchange,提问作者Kyaw Soe Han

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:01:25