SP70C毫米波雷达数据解析

对自己使用过的SP70C毫米波雷达解析程序进行整理,实现对雷达探测结果的获取

SP70C毫米波雷达

SP70C 是湖南纳雷科技有限公司研发的一款紧凑型 K 波段毫米波雷达, 采用 24GHz ISM 频段,双接收天线设计,测量距离远、体型小巧、灵敏度高、 重量轻、易于集成、性能稳定,满足工业测距与防碰撞、安防领域人员定位与 跟踪、汽车自动驾驶与主动安全等多领域应用需求,产品性能已得到众多合作 伙伴的认可。

相关参数

SP70C 采 用 高 集 成 度 MMIC 方 案 , 极 低 的 功 耗 (1.5W) , 较 小 尺 寸 (71x63x6mm),40m 测量距离,轻量化设计,可满足无人机、工业机械、电动车等领域的测距与防碰撞应用需求。

该款产品采用 1 路发射天线 2 路接收天线,精确测量目标的角度信息

SP70C 采用集成平面微带阵列天线,包含有 8 个垂直极化辐射单元。雷达 天线在方位面为宽波束,在俯仰面为窄波束,方位面-6dB 波束宽度超过 100°, 因此 SP70C 能够在方位面上探测到 100°范围内的运动目标。俯仰面-6dB 波束 宽度为 17°,并且采用泰勒算法对方向图进行低副瓣综合,具有优于-18dB 的副 瓣抑制比。 

 

为方便客户开发测试,SP70C 雷达提供了两种通信接口:UART 和 RS485

数据传输格式

SP70C 雷达传感器采用的是 UART-TTL 接口,使用预设的默认传输速率为 115200 波特,8 bit 数据位,1 位停止位,无奇偶校验位,无流控。每一个数据报文 以一个起始序列开始,同时以一个终止序列结束;在 SP70C 的每个数据循环周期 (20ms),都会输出 SP70C 的系统状态和目标输出状态报文,如果检测到目标即目 标输出状态报文的探测到目标个数字段为 1,目标输出状态报文后会紧接着输出目 标信息报文,目标信息报文包含目标的高度参数。

其中起始序列 Start Sequence 为定值 0xAAAA,报文 Message ID 定义如下表 所示,报文 Data Payload 根据 Message ID 定义,终止序列 End Sequence 为定值 0x5555。 

数据解析

串口调试助手

借助USB-TTL转换器将数据线通过USB与电脑连接,同时,将GND与POWER IN与12V直流电源连接,具体如下

雷达上电后即开始工作,使用串口调试助手,可发现雷达发送的探测报文。其中06 0A为系统状态,说明连接无误,接口已联通; 07 0B为目标状态,07 0C为目标信息,说明可探测到目标,雷达正常工作。

 解析代码

借助pyserial模块,构建串口解析代码,并封装成为单独的类,方便后续调用

import threading
import serial
import time
import logging
import struct
import queue


class sp70Comm:
    def __init__(self, MsgQue):
        self.msgQue = MsgQue
        # 构造串口的属性
        self.l_serial = None
        self.thread_read = None
        self.alive = False
        self.waitEnd = None
        # 串口号
        self.port = ''
        # 波特率
        self.baudrate = 115200
        # 校验位
        self.parity = serial.PARITY_NONE
        # 数据位
        self.bytesize = serial.EIGHTBITS
        self.ID = None
        self.data = None
        self.device_id = 'XX'

    def set_param(self, DeviceId='XX', Port='COM3', Baudrate=115200, Parity=serial.PARITY_NONE,
                  Bytesize=serial.EIGHTBITS):
        # 串口号
        self.port = Port
        # 波特率
        self.baudrate = Baudrate
        # 校验位
        self.parity = Parity
        # 数据位
        self.bytesize = Bytesize
        self.device_id = DeviceId

    # 定义串口等待的函数
    def waiting(self):
        logging.debug("定义串口等待的函数")
        if not self.waitEnd is None:
            self.waitEnd.wait()

    def SetStopEvent(self):
        logging.debug("设置stop event")
        if not self.waitEnd is None:
            self.waitEnd.set()
        self.alive = False
        self.stop()

    # 启动串口的函数
    def start(self):
        self.alive = True
        try:
            logging.debug("启动串口的函数")
            self.l_serial = serial.Serial()
            self.l_serial.port = self.port
            self.l_serial.baudrate = self.baudrate
            self.l_serial.parity = self.parity
            self.l_serial.bytesize = self.bytesize
            # 设置等待时间,若超出这停止等待
            logging.debug("设置等待时间,若超出这停止等待")
            self.l_serial.timeout = 2
            logging.debug(
                '参数--串口:' + str(self.port) + ',位/秒:' + str(self.baudrate) + ',奇偶校验:' + str(
                    self.parity) + ',数据位:' + str(
                    self.bytesize))

            self.l_serial.open()
            # 判断串口是否已经打开
            logging.debug("判断串口是否已经打开:" + str(self.l_serial.isOpen()))
            if self.l_serial.isOpen():
                # 这里写自己的逻辑,我这里是开了一个线程监听串口读数据
                self.waitEnd = threading.Event()
                self.alive = True
                self.thread_read = None
                logging.debug("激活标志alive:" + str(self.alive))
                self.thread_read = threading.Thread(target=self.msgParse)
                self.thread_read.setDaemon(True)
                self.thread_read.start()
                logging.debug("线程启动。。。")
                print(str(self.port) + ' open, start processing')
                return True
            else:
                return False
        except Exception as e:
            logging.warning(str(self.port) + "开启异常:" + str(e))
            print(str(self.port) + ' not open, ' + str(e))
            return False

    # 发送数据
    def SendData(self, send):
        # lmsg = ''
        isOK = False
        lmsg = send.encode('gb18030')
        com_ack = ''
        try:
            # 发送数据到相应的处理组件
            self.l_serial.write(lmsg)
            _cli_ack = self.l_serial.readline()
            # print('[0]', _cli_ack)
            _done_ack = self.l_serial.readline()
            # print('[1]', _done_ack)
            com_ack = _cli_ack + _done_ack
        except Exception as ex:
            print(lmsg, ' failed: ', ex)
            pass
        return com_ack

    def msgParse(self):
        logging.debug(":数据监控开始。。。")
        # head_word, tail_word = 0xAA, 0x55
        head_word, tail_word = bytes.fromhex('AA'), bytes.fromhex('55')
        package_length = 14
        bytes_data = bytes()
        msg_cnt = 0

        try:
            while self.alive:
                n = self.l_serial.inWaiting()  # 获取接收到的数据长度
                # if n == 0:
                #     # time.sleep(0.001)
                #     continue
                logging.debug(f'reading {n} bytes')
                # print(f'reading {n} bytes')
                bytes_data = bytes_data + self.l_serial.read(n)
                ready_flag = False
                logging.debug(f'begin header seeking, bytes len {len(bytes_data)}')
                # print(f'begin header seeking, bytes len {len(bytes_data)}')
                while len(bytes_data) > package_length:
                    raw_word = struct.unpack('14c', bytes_data[:package_length])
                    if (raw_word[0] != head_word) and \
                            (raw_word[1] != head_word) and \
                            (raw_word[-1] != tail_word) and \
                            (raw_word[-2] != tail_word):
                        # wrong head word, increment pointer by 1 and try again
                        bytes_data = bytes_data[1:]
                        continue
                    else:
                        # got correct word, proceed to parse
                        msg_cnt += 1
                        # logging.warning(f'header found, before bytes len {len(bytes_data)}, frameId {msg_cnt}')
                        bytes_data = bytes_data[package_length:]
                        if len(bytes_data) > 10 * package_length:
                            logging.warning(f'msg remain unprocessed {len(bytes_data) / package_length}')
                        # logging.warning(f'header found, remain bytes len {len(bytes_data)}, frameId {msg_cnt}')
                        msg_str = raw_word[2:-2]
                        msg_tag, msg_info = extract_msg(msg_str)
                        if msg_tag == 'target info':
                            msg_pack = {'tag': gdf.TAG_SP70,
                                        'mid': self.device_id,
                                        'time': time.time(),
                                        'payload': [msg_info['index'], msg_info['rcs'], msg_info['range'],
                                                    msg_info['azimuth'], msg_info['vrel'], msg_info['roll_count'],
                                                    msg_info['snr']]}
                            self.msgQue.put(msg_pack)
                            # print(f'{self.device_id} info {msg_info}')
                        # print(f'header found, bytes len {len(bytes_data)}, frameId {frameId} tlvNum {numTLVs} packetLength {packetLength}')
                        break
                # QApplication.processEvents()
        except Exception as e:
            logging.warning('获取com数据异常2:' + str(e))
            print(str(self.port) + ' parse error: ' + str(e))
        self.waitEnd.set()
        self.alive = False

    def stop(self):
        try:
            self.alive = False
            self.data = None
            if self.thread_read is not None:
                self.thread_read.join()
                logging.debug(str(self.thread_read.ident) + '线程关闭。。。')
                print(str(self.thread_read.ident) + ' thread terminated')

            if self.l_serial is not None and self.l_serial.isOpen():
                self.l_serial.close()
                logging.debug(str(self.l_serial.port) + '串口关闭。。。')
                print(str(self.l_serial.port) + ' port closed')
        except Exception as e:
            logging.warning('关闭' + str(self.port) + '线程失败:' + str(e))
            print(str(self.port) + 'close failed: ' + str(e))

经过封装后,便可在主函数中指定对应的端口号,直接调用。

if __name__ == '__main__':
    logging.basicConfig(level=logging.WARN)

    msg_queue = queue.Queue(maxsize=2000)

    sp70_t1 = sp70Comm(MsgQue=msg_queue)  # sp70c #1
    sp70_t1.set_param(DeviceId='001', Port='COM12')  # sp70c #1
    sp70_t1.start()

测试结果

使用SP70C观察公路上行驶的车辆,将得到的探测结果以散点图的形式作图。

 从图中可看到近雷达侧的路面,车辆探测比较连续,散点反映了车辆直线行驶的轨迹。而在对侧车道上行驶的车辆,由于遮挡及信噪比原因,探测结果较为分散。

参考链接

SP70C-工业短距防碰撞雷达_纳雷科技-精准测量,智能感知 (nanoradar.cn)

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LU70C51刷机包是针对LU70C51手机的一种固件文件,用来替换原有手机系统版本。刷机包可以提供更多的功能、修复系统bug、提升手机性能以及个性化定制手机的外观和功能等。 刷机的过程需要一定的技术操作,需要注意一些风险。首先,在刷机之前,我们需要备份手机中的重要数据,以防刷机失败导致数据丢失。其次,需要确定使用的刷机包是适合LU70C51手机的,确保刷机包来源可信,以免遭受恶意软件攻击。还需要确保手机的电量充足,刷机过程中不能断电,以免导致刷机失败。最后,在刷机之前,建议查阅相关的资料,了解刷机的具体操作流程和注意事项,以便顺利完成刷机。 刷机包的安装过程一般分为以下几个步骤:首先,通过手机进入刷机模式。具体的进入方式因手机不同而不同,一般是通过按住手机特定的组合键关机后,再按住特定组合键开机,进入到刷机模式。然后,将刷机包通过数据线连接手机与电脑,将刷机包放置到指定的刷机工具中,进行刷机包的安装。安装完成后,手机会自动重启,重新启动后系统将切换至新的刷机包的版本。 刷机的好处在于可以给手机带来更多的功能,提升手机的性能和稳定性,以及个性化定制手机的外观和功能。但是刷机也有一些风险,比如刷机失败可能会导致手机无法正常开机,甚至变砖,还可能会丢失手机中的数据,因此在刷机之前需要谨慎考虑并备份重要数据。最好在有一定相关知识的指导下进行刷机操作,以避免不可预料的问题发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值