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原始报文文本,后续解析逻辑可以完全复用。
四、数据接收基础实战:双通道采集程序
下面给出一个可直接运行的最小采集程序,同时打开串口和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验证)。
环境搭建完成后,采集端已经具备“接得上、读得到、存得下”的能力。下一阶段的工作是在这些原始报文基础上做校验、分片重组和字段解析,因此本节程序中的按行切分与缓存拼接逻辑需要保留,它是后续解析正确性的前提。