4. AIS数据采集软件环境搭建

AIS数据采集在软件层面要解决的核心问题只有一个:把来自不同物理通道的AIS报文稳定地读进计算机,并转成可处理的字节流。船上AIS设备通常通过RS-232/RS-422串口或以太网口对外输出报文,因此采集端需要同时具备串口通信能力和网络套接字能力。本节围绕Python开发环境、pyserial串口库、TCP/UDP套接字编程以及一个可运行的双通道接收程序展开,目标是搭好一套能实际接收AIS报文的最小可用环境。

一、Python开发环境的选择与准备

AIS采集程序属于长时间运行的I/O密集型任务,对解释器版本要求不高,但对第三方库和运行稳定性有要求。建议使用Python 3.9及以上版本,优先选择64位发行版。开发阶段可用Anaconda或官方安装包,部署到船载工控机时建议用虚拟环境隔离依赖,避免与其他软件冲突。

创建独立环境的典型命令如下,示例环境名为ais_env:

python -m venv ais_env
# Windows
ais_env\Scripts\activate
# Linux
source ais_env/bin/activate

pip install pyserial
pip list

安装完成后用python -c "import serial; print(serial.__version__)"验证,能打印出版本号即表示pyserial可用。除pyserial外,本节暂不引入其他依赖,保持环境最小化,便于排查问题。

二、串口通信库pyserial的关键参数

AIS设备串口输出最常见的配置是4800 8N1(部分设备为38400 8N1),即波特率4800、8位数据位、无校验、1位停止位。这一组参数必须与AIS设备输出设置完全一致,否则收到的将是乱码或空数据。pyserial通过serial.Serial()打开端口,关键参数包括端口名、波特率、超时时间。

端口名在Windows下形如COM3,在Linux下形如/dev/ttyUSB0。超时参数timeout决定read()的阻塞行为:设为1表示最多等待1秒,设为0表示非阻塞立即返回。采集程序通常设置0.5~1秒的超时,配合循环读取,既能及时响应又能避免CPU空转。

import serial

ser = serial.Serial(
    port="COM3",
    baudrate=4800,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    timeout=1
)
print(ser.is_open)   # True 表示端口已打开
data = ser.readline()  # 读取到换行符为止
print(data)
ser.close()

需要注意,AIS报文以!或$开头、以回车换行结束,readline()正好按行切分,是串口采集最常用的读取方式。若设备只发数据不换行,则需改用read(1024)按块读取并自行缓存拼接。

三、网络套接字编程:TCP与UDP两种接入方式

不少AIS设备或汇聚网关提供网络输出,常见形式是TCP服务端(设备监听某端口,采集端作为客户端连接)或UDP广播(设备向固定端口持续发包)。两种方式的编程模型不同:TCP面向连接、字节流连续、断线需重连;UDP无连接、以数据报为单位、可能丢包但实现简单。

TCP客户端的基本流程是创建套接字、连接、循环接收、异常重连。示例连接192.168.1.100:4001:

import socket

def tcp_receiver(host="192.168.1.100", port=4001):
    while True:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(5)
            s.connect((host, port))
            while True:
                chunk = s.recv(4096)
                if not chunk:
                    break          # 对端关闭
                print(chunk)
        except (socket.timeout, ConnectionError) as e:
            print("连接异常,3秒后重连:", e)
            time.sleep(3)
        finally:
            s.close()

UDP接收则只需绑定本地端口并循环recvfrom(),无需连接:

u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
u.bind(("0.0.0.0", 4002))
while True:
    datagram, addr = u.recvfrom(4096)
    print(addr, datagram)

选择哪种方式取决于设备手册:设备标注“TCP Server”就用TCP客户端连接;标注“UDP广播”就用UDP绑定端口接收。二者在应用层拿到的都是AIS原始报文文本,后续解析逻辑可以完全复用。

AIS双通道数据接收软件结构 AIS设备串口输出 RS-232 / 4800 8N1 AIS网关网络输出 TCP 4001 / UDP 4002 pyserial 串口读取 Serial + readline() socket 网络接收 recv() / recvfrom() 统一字节流 按行切分 缓存拼接 报文队列 待解析 落盘/转发 超时 / 断线 / 乱码 重连、校验波特率

四、数据接收基础实战:双通道采集程序

下面给出一个可直接运行的最小采集程序,同时打开串口和TCP通道,把收到的原始报文按行打印并写入文件。示例参数为串口COM3/4800、TCP192.168.1.100:4001,实际使用时按设备手册替换。

import serial, socket, threading, time

def serial_worker(port="COM3", baud=4800):
    ser = serial.Serial(port, baud, timeout=1)
    with open("ais_serial.log", "ab") as f:
        while True:
            line = ser.readline()
            if line:
                f.write(line)
                f.flush()
                print("[SER]", line.decode("ascii", "ignore").strip())

def tcp_worker(host="192.168.1.100", port=4001):
    while True:
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(5)
            s.connect((host, port))
            buf = b""
            with open("ais_tcp.log", "ab") as f:
                while True:
                    chunk = s.recv(4096)
                    if not chunk:
                        break
                    buf += chunk
                    while b"\n" in buf:
                        line, buf = buf.split(b"\n", 1)
                        f.write(line + b"\n")
                        f.flush()
                        print("[TCP]", line.decode("ascii", "ignore").strip())
        except Exception as e:
            print("TCP异常:", e)
            time.sleep(3)

threading.Thread(target=serial_worker, daemon=True).start()
tcp_worker()

程序运行后,控制台应出现形如!AIVDM,1,1,,A,13aG?P0P00PD;88MD5MTDww@2D0l,0*5C的报文行。判断采集是否成功的依据有三条:报文以!AIVDM或!AIVDO开头;每行末尾有*XX校验字段;报文持续、间隔稳定地出现。若出现大量乱码,首先核对波特率;若长时间无数据,检查端口名、设备输出开关和网络连通性(可用ping和telnet 192.168.1.100 4001验证)。

串口与TCP两路数据可能包含重复报文(同一目标经不同通道上报),后续解析阶段应按报文内容或时间戳去重,本节只负责把原始字节流完整、不丢行地收下来。

环境搭建完成后,采集端已经具备“接得上、读得到、存得下”的能力。下一阶段的工作是在这些原始报文基础上做校验、分片重组和字段解析,因此本节程序中的按行切分与缓存拼接逻辑需要保留,它是后续解析正确性的前提。