毫米波雷达||网口接收雷达原始数据
目录
1、采集雷达原始数据
2、通过网口传输数据
参考文献:
1、采集雷达原始数据
包括TI或加特兰雷达在内的各种雷达设备都配备了专门的采集适配器以获取雷达原始数据。通常采用LVDS高速接口以及网络口来传输数据。例如使用参数设置为256×128×12×4×20。
包括TI或加特兰雷达在内的各种雷达设备都配备了专门的采集适配器以获取雷达原始数据。通常采用LVDS高速接口以及网络口来传输数据。例如使用参数设置为256×128×12×4×20。

31.5Mbps(即N_{\text{采样}} \times B_{\text{啁啾}} \times M_{\text{天线}} \times S_{\text{多路复用}} \times 20Hz)则要求使用高于31.5Mbps的高速接口才能实现实时视频采集系统的帧率)。当然可以通过雷达UART串口获取少量ADC数据(如一段帧的原始信号),但受串口最大传输速率限制(仅为3.125Mbps),无法实现实时更新的ADC数据采集功能。
借助现有的软件包采集雷达ADC原始数据相对便捷。
然而缺乏灵活性,并且难以实现实时处理。
鉴于上述不足,在本研究中拟通过自编Python脚本直接捕获ADC原始数据并进行处理。
例如TI雷达

此注释说明:LVDS(Low Voltage Differential Signaling, 低压差分信号)是高性能的串行通信技术。该接口具备快速数据传输性能,并且展现出极低噪声水平、高效节能设计以及良好的抗干扰性能。此外还具有长距离通信能力。

从 LVDS 至以太网流:在原始模式下, 所有 LVDS 数据将完整无误地被捕获并经由以太网接口实现数据流式的传输. 遵循 UDP 协议的 DCA1000EVM 支持 14 个预设命令, 其配置信息与状态可通过配置端口实现通信. 数据端口则用于传递两种运行模式的数据信息
在原始模式下进行UDP数据格式编码时,请注意以下几点:
该编码方案由三部分构成:包含序号字段、指定大小字段以及接收端存储原始雷达回波的数据部分。
以太网(Ethernet)的数据帧长度必须受限于其物理特性和技术规范限定,在46至1500个字符范围内。
实际上等于网络层IP数据报的长度上限。
其中IP头部占用了20个字节而UDP头部则占用了8个字节。
因此,在UDP的数据区中可承载的最大信息量为1472个字符。
从图中可以看出,在单个UDP数据包中可容纳的最大信息量为1472个字符。
然而需要注意的是,在实际应用中单次传输往往无法满足需求,
因此需要将完整的雷达原始回波信号分解成多帧进行发送。
值得注意的是,
由于UDP协议不具备重传机制,
当某一部分发生丢包时无法重新发送,
但在这种情况下可以通过将该部分置零来实现缺省值处理。

2、通过网口传输数据
使用DCA 1000的数据传输系统采用UDP报包的形式通过以太网实现通信。在DCA 1000上部署的FPGA设备已针对目的IP地址192.168.33.30进行了配置(其中该IP视为静态IP标识符)。为了满足硬件需求,在目标PC上应设置与其绑定的FPGA预期目的地址相对应的静态IP配置。这将确保通信链路能够正常运行。
网口接收雷达ADC原始数据的流程如下:
- 对雷达阵列及信号采集模块进行校准;
- 通过UART发送控制指令至雷达系统,并完成对雷达系统的初始化及设置相关参数值:
1. def serialConfig(configFileName):
2.
3. # Open the serial ports for the configuration and the data ports
4. # Windows
5. CLIport = serial.Serial('COM9', 115200)
6. Dataport = serial.Serial('COM10', 921600)
7.
8. # Read the configuration file and send it to the board
9. config = [line.rstrip('\r\n') for line in open(configFileName)]
10. for i in config:
11. CLIport.write((i+'\n').encode())
12. print(i)
13. time.sleep(0.01)
14.
15. return CLIport, Dataport
python

TI雷达由两个串口组成:一个用于传输配置信息的CLIport和一个用于接收数据的Dataport。CLIport解析并逐一线路读取配置文件中的参数设置。
- 通过网口UDP向采集板发送配置指令:
1. def __init__(self, static_ip='192.168.33.30', adc_ip='192.168.33.180',
2. data_port=4098, config_port=4096):
3.
4. # Create configuration and data destinations
5. self.cfg_dest = (adc_ip, config_port)
6. self.cfg_recv = (static_ip, config_port)
7. self.data_recv = (static_ip, data_port)
8.
9. # Create sockets
10. self.config_socket = socket.socket(socket.AF_INET,
11. socket.SOCK_DGRAM,
12. socket.IPPROTO_UDP)
13. self.data_socket = socket.socket(socket.AF_INET,
14. socket.SOCK_DGRAM,
15. socket.IPPROTO_UDP)
16.
17. # Bind data socket to fpga
18. self.data_socket.bind(self.data_recv)
19.
20. # Bind config socket to fpga
21. self.config_socket.bind(self.cfg_recv)
python

UDP因其直通性和极低延迟的特点,在对可靠性要求不高但传输速度要求较高的场景中发挥着重要作用;同时也要特别注意数据丢失等潜在问题。
- 网口UDP通道向采集单元传输启动指令;
- UART端向雷达触发传感器启动命令即"sensorStart";
- 网口UDP通道持续接收数据包,并解码并提取原始数据信息及后续 incoming data进行实时分析;
1. def _read_data_packet(self):
2. """Helper function to read in a single ADC packet via UDP
3. 4. Returns:
5. int: Current packet number, byte count of data that has already been read, raw ADC data in current packet
6. 7. """
8. data, addr = self.data_socket.recvfrom(MAX_PACKET_SIZE)
9. packet_num = struct.unpack('<1l', data[:4])[0]
10. byte_count = struct.unpack('>Q', b'\x00\x00' + data[4:10][::-1])[0]
11. packet_data = np.frombuffer(data[10:], dtype=np.uint16)
12. return packet_num, byte_count, packet_data
python

1. def read(self, timeout=1):
2. """ Read in a single packet via UDP
3. """
4. # Configure
5. self.data_socket.settimeout(timeout)
6.
7. # Frame buffer
8. ret_frame = np.zeros(UINT16_IN_FRAME, dtype=np.uint16)
9.
10. # Wait for start of next frame
11. while True:
12. packet_num, byte_count, packet_data = self._read_data_packet()
13. if byte_count % BYTES_IN_FRAME_CLIPPED == 0:
14. packets_read = 1
15. ret_frame[0:UINT16_IN_PACKET] = packet_data
16. break
17.
18. # Read in the rest of the frame
19. while True:
20. packet_num, byte_count, packet_data = self._read_data_packet()
21. packets_read += 1
22.
23. if byte_count % BYTES_IN_FRAME_CLIPPED == 0:
24. self.lost_packets = PACKETS_IN_FRAME_CLIPPED - packets_read
25. return ret_frame
26.
27. curr_idx = ((packet_num - 1) % PACKETS_IN_FRAME_CLIPPED)
28. try:
29. ret_frame[curr_idx * UINT16_IN_PACKET:(curr_idx + 1) * UINT16_IN_PACKET] = packet_data
30. except:
31. pass
32.
33. if packets_read > PACKETS_IN_FRAME_CLIPPED:
34. packets_read = 0
python

该函数通过UDP协议获取单个ADC的数据报文,并返回解析后的信息:包括当前的数据报文编号、累计接收的字节数以及该报文中存储的真实ADC数值。其中我们采用最大值设定MAX_PACKET_SIZE以确保能够接收完整的一份 ADC 数据报文。在上图中可以看到每个UDP报文由三部分组成:前述函数解析后得到的数据编号、累计接收长度以及实际采集到的 ADC 数值信息。需要注意的是,在实际应用中一张完整的 ADC 采样结果通常可能达到几百万个样本量因此需要将这些 UDP 报文拼接起来形成完整的采样结果。在获取完整的一帧 ADC 采样过程中我们首先会捕获第一部分的信息随后继续循环调用 recvfrom 函数来获取后续的所有 UDP 报文进而拼接成完整的 ADC 数组记录下来这样即使出现某一部分 UDP 报文丢失也能保证整体采样的完整性
1. @staticmethod
2. def organize(raw_frame, num_chirps, num_rx, num_samples):
3. """Reorganizes raw ADC data into a full frame
4. Returns:
5. ndarray: Reformatted frame of raw data of shape (num_chirps, num_rx, num_samples)
6. """
7. ret = np.zeros(len(raw_frame) // 2, dtype=complex)
8.
9. # Separate IQ data
10. ret[0::2] = raw_frame[0::4] + 1j * raw_frame[2::4]
11. ret[1::2] = raw_frame[1::4] + 1j * raw_frame[3::4]
12. return ret.reshape((num_chirps, num_rx, num_samples))
python

该函数将解析所得的一帧数据块整合为完整的原始ADC数据,并包含实部与虚部两个部分。(IQ数据的排列方式因雷达型号的不同而有所差异,在此采用的是两个双工分段模式(每个段落包含两个I段和两个Q段),即2I-2Q+ 2I-2Q;有些则采用四个工分段模式(每个段落包含四个I段和四个Q段),即4I-4Q。)
- 程序使用UART传输一个称为"sensorStop"的停止指令给雷达;
- 程序通过网络接口UDP向采集板发送停止采集指令,并同时关闭两个用于配置传输和数据接收的UDP端口。
参考文献:
《使用低速串行总线的实时 ADC原始数据采集方法》-TI官方文档
《DCA1000EVMDataCaptureCard》-TI官方文档
