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

UDP接收GPS NMEA报文异常:VTG/GGA丢包而ZDA正常求助

UDP接收GPS NMEA报文丢包问题排查

问题描述

编写Python程序通过UDP接收GPS NMEA语句,GPS设备每秒发送ZDA、VTG、GGA序列报文,但代码仅能稳定接收ZDA,VTG和GGA频繁丢失。输出示例:

['$GPZDA,204622.00,16,10,2023,,*63', '']
['$GPZDA,204623.00,16,10,2023,,*62', '']
['$GPZDA,204624.00,16,10,2023,,*65', '']
['$GPZDA,204625.00,16,10,2023,,*64', '']
['$GPZDA,204626.00,16,10,2023,,*67', '']
['$GPZDA,204627.00,16,10,2023,,*66', '']
['$GPZDA,204628.00,16,10,2023,,*69', '']
['$GPZDA,204629.00,16,10,2023,,*68', '']
['$GPZDA,204630.00,16,10,2023,,*60', '']
['$GPZDA,204631.00,16,10,2023,,*61', '']
['$GPZDA,204632.00,16,10,2023,,*62', '']
['$GPGGA,204632.00,2918.75039,N,09435.22487,W,5,26,0.6,33.64,M,-27.60,M,010,0481*53', '']
['$GPZDA,204633.00,16,10,2023,,*63', '']
['$GPZDA,204634.00,16,10,2023,,*64', '']
['$GPZDA,204635.00,16,10,2023,,*65', '']
['$GPZDA,204636.00,16,10,2023,,*66', '']
['$GPZDA,204637.00,16,10,2023,,*67', '']
['$GPZDA,204638.00,16,10,2023,,*68', '']
['$GPZDA,204639.00,16,10,2023,,*69', '']
['$GPZDA,204640.00,16,10,2023,,*67', '']
['$GPZDA,204641.00,16,10,2023,,*66', '']
['$GPZDA,204642.00,16,10,2023,,*65', '']
['$GNVTG,286.931,T,286.931,M,0.198,N,0.367,K,D*3A', '']
['$GPZDA,204643.00,16,10,2023,,*64', '']

现有代码

UDP_socket类

class UDP_socket(object):
    ''' mode = "utf8" or "binary" '''
    def __init__(self, host='', port=0, broadcast=True, mode='utf8'):
        self.host = host
        self.port = port
        self.broadcast = broadcast
        self.mode = mode
    
    def read(self):
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            if self.broadcast:
                s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            s.bind((self.host, self.port))
            if self.mode == 'binary':
                data = s.recv(1024).hex()
            else:
                data = s.recv(1024).decode('Utf8', 'replace').split('\r\n')
                print(data)
            return data

    def send(self, input, target):
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            if self.broadcast:
                s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            s.bind((self.host, self.port))
            s.sendto(input.encode('Utf8'), target)

GPS类相关代码

def connect_socket(self):
    '''Create a socket UDP or TCP''' 
    self.continuous_read = True
    self.button.configure(text="Disconnect", command=self.socket_stop)
    if self.tcpudp.get(): #0 for TCP
        self.socket = UDP_socket(self.gps_address.get(), int(self.gps_port.get()), True)
    else:
        self.socket = TCP_socket(self.gps_address.get(), int(self.gps_port.get()) )
    self.gps_marker = Map_Marker(app.map, color = self.marker_color)
    readT = threading.Thread(target= lambda: self.socket_read(FALSE))
    readT.start()

def socket_read(self, terminal):
    telegram = nmea_oi()
    while self.continuous_read:
        received_msg = self.socket.read()
        del received_msg[-1]
        for j in range(len(received_msg)):
            if telegram.decode(received_msg[j], 'All', terminal)[0] == 'GGA':
                lat = telegram.decode(received_msg[j], 'All', terminal)[1][0]
                long = telegram.decode(received_msg[j], 'All', terminal)[1][1]
                self.gps_marker.update_marker(lat, long)
            self.update_displayed_lines(received_msg[j])

已知同一电脑其他软件可正常接收所有报文,已尝试增大缓冲区到2048,怀疑线程或时序问题。


问题根源与解决方案

1. 核心问题:每次read创建新Socket

UDP_socket.read()方法中,每次调用都会创建新UDP套接字并绑定端口,这会导致:

  • 旧Socket在with块结束后自动关闭,新Socket绑定前的间隙会丢失报文
  • 频繁创建/销毁Socket引入延迟,无法及时接收连续报文

修复方案:在__init__中初始化并复用同一个Socket:

import socket

class UDP_socket(object):
    ''' mode = "utf8" or "binary" '''
    def __init__(self, host='', port=0, broadcast=True, mode='utf8'):
        self.host = host
        self.port = port
        self.broadcast = broadcast
        self.mode = mode
        # 初始化Socket并绑定
        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if self.broadcast:
            self.s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.s.bind((self.host, self.port))
    
    def read(self):
        if self.mode == 'binary':
            data = self.s.recv(2048).hex()
        else:
            # 用2048缓冲区确保接收完整多报文数据
            data = self.s.recv(2048).decode('Utf8', 'replace').split('\r\n')
            print(data)
        return data

    def send(self, input, target):
        self.s.sendto(input.encode('Utf8'), target)
    
    def close(self):
        # 添加关闭方法清理Socket
        self.s.close()

2. 数据处理优化:避免误删有效数据

del received_msg[-1]会固定删除最后一条数据,若最后一条是有效报文则会丢失。改为过滤空字符串:

def socket_read(self, terminal):
    telegram = nmea_oi()
    while self.continuous_read:
        received_msg = self.socket.read()
        # 只过滤空字符串元素
        received_msg = [msg for msg in received_msg if msg.strip()]
        for msg in received_msg:
            decode_result = telegram.decode(msg, 'All', terminal)
            if decode_result[0] == 'GGA':
                lat = decode_result[1][0]
                long = decode_result[1][1]
                self.gps_marker.update_marker(lat, long)
            self.update_displayed_lines(msg)

3. 线程与资源清理

  • 修正线程启动时的关键字错误:FALSE改为False
  • 添加断开连接时的Socket关闭逻辑:
def socket_stop(self):
    self.continuous_read = False
    if hasattr(self, 'socket'):
        self.socket.close()
    self.button.configure(text="Connect", command=self.connect_socket)

4. 额外优化

  • 移除read方法中不必要的print,减少IO延迟
  • 保持recv缓冲区为2048或更大,确保单次接收完整多报文数据

内容的提问来源于stack exchange,提问作者Kaj Ervo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 11:02:02