毫米波雷达||网口接收雷达原始数据

目录 

1、采集雷达原始数据

 2、通过网口传输数据

参考文献:

1、采集雷达原始数据

像TI或加特兰雷达都有相应的采集板可以用于雷达原始数据获取,一般通过LVDS高速接口和网口传输数据,需要比如256*128*12*4*20 \approx 31.5Mbps(即采样点*啁啾数*天线数*复采样*20Hz)即>31.5Mbps的高速接口才可以实时采集20Hz刷新率的数据。当然通过雷达UART串口也可以获取一点ADC数据,不过受限于串口最大传输速率3.125Mbps,无法实时的获取ADC数据,只能获得单个frame的ADC原始数据。

采用官方自带的软件采集雷达ADC原始数据很方便,但是不够灵活,也不便于实时处理。所以考虑跳过官方软件通过python实时采集ADC原始数据并处理。

以TI雷达为例,官方文档中介绍了配置DCA1000EVM并通过以太网将ADC数据从毫米波传感器捕获到PC的方法。雷达板和采集板之间通过LVDS高速接口连接,采集板通过网口传输数据到PC。

注:LVDS(Low Voltage Differential Signaling,低压差分信号)是一种高速串行数据传输技术,LVDS接口具有以下特点:高速传输能力、低噪声、低功耗、抗电磁干扰和传输距离远。

LVDS 到以太网流:–原始模式:在此模式下,所有 LVDS 数据均按原样捕获并通过以太网接口流式传输。DCA1000EVM遵循 UDP 协议,支持14个预定义命令。DCA1000EVM的配置和状态通过配置端口进行通信。数据端口用于传输原始模式/数据分离模式数据。

原始模式下UDP数据格式如下图,包括三个部分:数据包序号、字节大小和雷达原始数据部分。以太网(Ethernet)数据帧长度必须在46~1500字节之间,这是以太网的物理特性决定的。这个1500字节为链路层的最大传输单元MTU,实际是网络层IP数据报的长度限制。因为IP数据报首部20字节,UDP数据报首部8字节,所以UDP数据报的数据区最大长度为1472字节。下图中UDP数据报数据区的最大长度即为1472字节,当然一帧雷达原始数据远比这大,所以会分为若干个传输。由于UDP特性,某一个数据传送中丢失时,无法重传,不过由于数据中包含序号信息可以将该部分数据置0。

 2、通过网口传输数据

使用DCA 1000,数据以UDP数据包的形式通过以太网传输。DCA 1000上的FPGA已使用目的地址192.168.33.30进行编程(该IP地址即为静态ip地址,用于采集板发送数据,而采集板接收配置命令的为FPGA绑定的地址192.168.33.180)。因此,我们必须将目标PC配置为与FPGA的预期目的地址匹配的静态IP地址,以便从EVM接收数据。

网口接收雷达ADC原始数据的流程如下:

  1. 重置雷达板和采集板;
  2. 通过UART向雷达发送配置,初始化雷达并配置相应参数:
    def serialConfig(configFileName):
    
        # Open the serial ports for the configuration and the data ports
        # Windows
        CLIport = serial.Serial('COM9', 115200)
        Dataport = serial.Serial('COM10', 921600)
    
        # Read the configuration file and send it to the board
        config = [line.rstrip('\r\n') for line in open(configFileName)]
        for i in config:
            CLIport.write((i+'\n').encode())
            print(i)
            time.sleep(0.01)
    
        return CLIport, Dataport
    TI雷达需要一个发送配置的串口CLIport和一个接收数据的串口Dataport,CLIport读取cfg配置文件逐行发送配置参数。
  3. 通过网口UDP向采集板发送配置指令:
    def __init__(self, static_ip='192.168.33.30', adc_ip='192.168.33.180',
                     data_port=4098, config_port=4096):    
    
            # Create configuration and data destinations
            self.cfg_dest = (adc_ip, config_port)
            self.cfg_recv = (static_ip, config_port)
            self.data_recv = (static_ip, data_port)
    
            # Create sockets
            self.config_socket = socket.socket(socket.AF_INET,
                                               socket.SOCK_DGRAM,
                                               socket.IPPROTO_UDP)
            self.data_socket = socket.socket(socket.AF_INET,
                                             socket.SOCK_DGRAM,
                                             socket.IPPROTO_UDP)
    
            # Bind data socket to fpga
            self.data_socket.bind(self.data_recv)
    
            # Bind config socket to fpga
            self.config_socket.bind(self.cfg_recv)

    UDP因其简单性和低延迟性,在需要快速传输且对可靠性要求不高的场景中非常有用,当然也要注意数据丢失等问题。首先,基于UDP协议的网口通信通过UDP套接字完成,套接字(socket)可以看作是两个程序在网络中进行通信的接口,它允许程序通过网络发送和接收数据,就好像是在不同计算机或不同进程之间直接通信一样。在python中使用socket模块创建UDP套接字,socket.AF_INET 表示套接字使用IPv4地址族,socket.SOCK_DGRAM 表示套接字类型为UDP数据报,socket.IPPROTO_UDP 是UDP协议的协议号。然后是绑定套接字,将套接字和IP与端口关联起来,这样套接字就可以接收发送到该地址的数据包。注意:这里adc_ip对应采集板上FPGA的地址,static_ip是将计算机配置为与采集板目的地址匹配的静态IP地址,还有config_port接收配置的端口号和data_port发送数据的端口号。也就是说,cfg_dest指向发送配置的ip端口,cfg_recv则指向接收数据的ip和配置端口,data_recv则指向接收数据的ip端口。

  4. 通过网口UDP向采集板发送开始采集指令;
  5. 通过UART向雷达发送启动雷达命令即“sensorStart”;
  6. 通过网口UDP循环接收数据包+解析出原始数据+后续数据实时处理:
    def _read_data_packet(self):
            """Helper function to read in a single ADC packet via UDP
    
            Returns:
                int: Current packet number, byte count of data that has already been read, raw ADC data in current packet
    
            """
            data, addr = self.data_socket.recvfrom(MAX_PACKET_SIZE)
            packet_num = struct.unpack('<1l', data[:4])[0]
            byte_count = struct.unpack('>Q', b'\x00\x00' + data[4:10][::-1])[0]
            packet_data = np.frombuffer(data[10:], dtype=np.uint16)
            return packet_num, byte_count, packet_data
    def read(self, timeout=1):
            """ Read in a single packet via UDP
            """
            # Configure
            self.data_socket.settimeout(timeout)
    
            # Frame buffer
            ret_frame = np.zeros(UINT16_IN_FRAME, dtype=np.uint16)
    
            # Wait for start of next frame
            while True:
                packet_num, byte_count, packet_data = self._read_data_packet()
                if byte_count % BYTES_IN_FRAME_CLIPPED == 0:
                    packets_read = 1
                    ret_frame[0:UINT16_IN_PACKET] = packet_data
                    break
    
            # Read in the rest of the frame            
            while True:
                packet_num, byte_count, packet_data = self._read_data_packet()
                packets_read += 1
    
                if byte_count % BYTES_IN_FRAME_CLIPPED == 0:
                    self.lost_packets = PACKETS_IN_FRAME_CLIPPED - packets_read
                    return ret_frame
    
                curr_idx = ((packet_num - 1) % PACKETS_IN_FRAME_CLIPPED)
                try:
                    ret_frame[curr_idx * UINT16_IN_PACKET:(curr_idx + 1) * UINT16_IN_PACKET] = packet_data
                except:
                    pass
    
                if packets_read > PACKETS_IN_FRAME_CLIPPED:
                    packets_read = 0

    _read_data_packet(): 通过UDP读取单个ADC数据包,返回当前数据包编号、已读取的字节数和当前数据包中的原始ADC数据。recvfrom(MAX_PACKET_SIZE),MAX_PACKET_SIZE即单个UDP数据包的最大大小。上面图中,一个UDP数据包包含三个部分,即函数_read_data_packet返回的解析数据包编号、解析字节计数、解析ADC数据。不过一帧ADC原始数据可能有几M大小,需要多个UDP数据包才能传输完。所以通过read函数 通过循环读取单个UDP数据包,返回一个完整的数据帧。它会先读取一帧的开始部分,在第二个循环中读取帧的剩余部分,有数据包编号packet_num,可以知道当前UDP数据包在一帧中的位置,所以对于数据包丢失的情况,一帧数据对应位置的数据即为0。

    @staticmethod
    def organize(raw_frame, num_chirps, num_rx, num_samples):
    """Reorganizes raw ADC data into a full frame
       Returns:
         ndarray: Reformatted frame of raw data of shape (num_chirps, num_rx, num_samples)
    """
       ret = np.zeros(len(raw_frame) // 2, dtype=complex)
    
       # Separate IQ data
       ret[0::2] = raw_frame[0::4] + 1j * raw_frame[2::4]
       ret[1::2] = raw_frame[1::4] + 1j * raw_frame[3::4]
       return ret.reshape((num_chirps, num_rx, num_samples))
    

    organize函数将解析出的一帧数据组织为一帧完整的原始ADC数据,包括实部和虚部。(IQ数据的排列因雷达型号而异,这里是2I2Q+2I2Q,而有的是4I4Q。)

  7. 通过UART向雷达发送停止雷达命令即“sensorStop”;
  8. 通过网口UDP向采集板发送停止采集指令,并关闭用于发送配置和接收数据的两个UDP套接字。

参考文献:

《使用低速串行总线的实时 ADC原始数据采集方法》-TI官方文档

《DCA1000EVMDataCaptureCard》-TI官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值