基于Qt平台的UWB LinkTrack模块数据解析(附完整C++代码)

12 篇文章 5 订阅

        最近要用Nooploop的LinkTrack模块做定位,分享数据解析代码。

        LinkTrack相关文档的下载地址:资料下载 – Nooploophttps://www.nooploop.com/download/

目录

UWB技术介绍

 用户手册的协议解析

 数据解析

 完整Qt项目

Qt工程文件


UWB技术介绍

        LinkTrack用到的是UWB技术。UWB 是一种无载波通信技术,利用纳秒至微秒级的非正弦波窄脉冲传输数据。UWB 具备时间分辨率高、穿透力强、功耗低、抗多径效果好、安全性高等优点,因此常被应用于通信与定位领域,尤其是在GNSS(如GPS、BDS、Glonass、Galileo)信号覆盖不到的场合。

        UWB 定位原理与GPS 相似,其中:ANCHOR(基站)相当于天上的卫星,TAG(标签)相当于用户端的GNSS 接收机,CONSOLE(控制台)相当于地面的监控站。ANCHOR 一般作为参考位置点,一般安装于固定参考点;TAG 一般作为待定位点,一般安装于待定位载体(如无人机、无人车)上;CONSOLE 一般用于监控系统的运行状态并向其他节点(ANCHOR、TAG)下发指令,一般接到Terminal(终端),如计算机、平板电脑等。UWB 属于电磁波,其在真空中的传播速度与光速相同。通过测量TAG 到ANCHOR 的TOF(飞行时间),乘以光速后,TAG 可以获得到ANCHOR 的距离。通过到多个ANCHOR 距离与参考ANCHOR 的坐标,可以列出多组球面方程,进而由数学方法可以求解出标签的坐标。图7为常见的三边定位原理示意图。

 用户手册的协议解析

http://ftp.nooploop.com/software/products/uwb/doc/LinkTrack_User_Manual_V2.2_zh.pdf

 数据解析

https://github.com/nooploop-dev/nlink_unpack

        官网给出了帧数据的解析代码,通过此代码可以很好地把串口传来的数据解析成我们想要的数据,从而大大减少开发时间。但是,在接收串口数据时,由于帧数据比较大,分两次才接收完,这对于数据处理很不友好。这个问题我花了一天时间才想出解决办法。在此之前,我们先看下串口通信为什么出现数据分包情况。

关于字节

一个字节多少位 - 一颗蘋果 - 博客园
 

下面是接收LinkTrack模块的数据并解析的核心代码

void MainWindow::ReadData()
{
    /******************此处尽量不要放过多的函数处理,否则影响数据接收******************/
    QByteArray arr;
    static QByteArray buffer;

    arr = m_port->readAll();
    static bool flag = false;

    //判断是不是55开头的数据,如果是的话,就存起来,然后设置flag位true,下次就接收另外一组数据
    if(arr.toHex().startsWith("55"))
    {
        buffer.append(arr); //存储第一组数据
        flag = true;
        return;
    }

    if(flag)
    {

        buffer.append(arr); //存储第二组数据
        char *string1 = buffer.toHex().data();

        uint8_t data[1024];
        size_t data_length;
        data_length = NLink_StringToHex(string1, data);  //将字符串转换为Hex格式,并将数据存储在data中

        if (g_nlt_nodeframe2.UnpackData(data, data_length))
        {
          nlt_nodeframe2_result_t *result = &g_nlt_nodeframe2.result;
          qDebug()<<"LinkTrack NodeFrame0 data unpack successfully:n";

    //      和上位机对比,数据是正确的
          qDebug()<< "位置:" <<result->pos_3d[0] <<result->pos_3d[1] <<result->pos_3d[2];
          qDebug()<< "速度:" <<result->vel_3d[0] <<result->vel_3d[1] <<result->vel_3d[2];
          qDebug()<< "加速度:" <<result->imu_acc_3d[0] <<result->imu_acc_3d[1] <<result->imu_acc_3d[2];

         }

        buffer.clear();//因为buffer是静态变量,用完一次后要记得清空,否则会使得内存溢出
        flag=false;
    }

}

        此代码解析的是g_nlt_nodeframe2协议类型,如果需要解析其他协议,只需要修改三处位置即可。

 完整Qt项目

首先配置pro文件,将用到的模块添加进去。

QT       += core gui serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp \
    nlink_linktrack_anchorframe0.c \
    nlink_linktrack_aoa_nodeframe0.c \
    nlink_linktrack_nodeframe0.c \
    nlink_linktrack_nodeframe1.c \
    nlink_linktrack_nodeframe2.c \
    nlink_linktrack_nodeframe3.c \
    nlink_linktrack_nodeframe5.c \
    nlink_linktrack_nodeframe6.c \
    nlink_linktrack_tagframe0.c \
    nlink_tofsense_frame0.c \
    nlink_utils.c

HEADERS += \
    mainwindow.h \
    nlink_linktrack_anchorframe0.h \
    nlink_linktrack_aoa_nodeframe0.h \
    nlink_linktrack_nodeframe0.h \
    nlink_linktrack_nodeframe1.h \
    nlink_linktrack_nodeframe2.h \
    nlink_linktrack_nodeframe3.h \
    nlink_linktrack_nodeframe5.h \
    nlink_linktrack_nodeframe6.h \
    nlink_linktrack_tagframe0.h \
    nlink_tofsense_frame0.h \
    nlink_utils.h \
    nlink_typedef.h


FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target


main.cpp代码不变,就不展示了

 mainwindow.h代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void _Init();//初始化函数声明

private slots:
    void on_open_clicked();

    void on_send_clicked();

    void ReadData();//数据读取

    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;

    //声明串口
    QSerialPort *m_port;

};
#endif // MAINWINDOW_H

mainwindow.cpp代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QBuffer>


#include "nlink_linktrack_nodeframe0.h"
#include "nlink_linktrack_nodeframe1.h"
#include "nlink_tofsense_frame0.h"
#include "nlink_utils.h"
#include "nlink_linktrack_nodeframe2.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::_Init()
{
    //查找可用的串口
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort port;
        port.setPort(info);
        if(port.open(QIODevice::ReadWrite))
        {
            ui->comboBox_port->addItem(port.portName());
            port.close();
        }
    }
    //设置波特率下拉菜单默认显示第0项
    ui->comboBox_baud->setCurrentIndex(0);

    //连接信号槽,用于接收信息
    m_port = new QSerialPort();
    connect(m_port, SIGNAL(readyRead()), this, SLOT(ReadData()));
}

void MainWindow::on_open_clicked()
{
    if(ui->open->text() == tr("打开串口"))
    {
        //设置串口名
        m_port->setPortName(ui->comboBox_port->currentText());
        //打开串口
        m_port->open(QIODevice::ReadWrite);
//        设置波特率
        m_port->setBaudRate(ui->comboBox_baud->currentText().toInt());
//        设置数据位
        switch(ui->comboBox_bit->currentText().toInt())
        {
            case 8:
                m_port->setDataBits(QSerialPort::Data8);
                break;
            case 7:
                m_port->setDataBits(QSerialPort::Data7);
                break;
            case 6:
                m_port->setDataBits(QSerialPort::Data6);
                break;
            case 5:
                m_port->setDataBits(QSerialPort::Data5);
                break;
            default:
                break;
        }

//        设置控制流
        m_port->setFlowControl(QSerialPort::NoFlowControl);

//        关闭菜单使能
        ui->comboBox_port->setEnabled(false);
        ui->comboBox_baud->setEnabled(false);
        ui->comboBox_bit->setEnabled(false);
        ui->comboBox_parity->setEnabled(false);
        ui->comboBox_stop->setEnabled(false);

        ui->open->setText(tr("关闭串口"));

    }
    else
    {
//        关闭串口
        m_port->clear();
        m_port->close();
//        m_port->deleteLater(); //这句是删除串口

//        恢复菜单使能
        ui->comboBox_port->setEnabled(true);
        ui->comboBox_baud->setEnabled(true);
        ui->comboBox_bit->setEnabled(true);
        ui->comboBox_parity->setEnabled(true);
        ui->comboBox_stop->setEnabled(true);

        ui->open->setText(tr("打开串口"));

    }
}


void MainWindow::on_send_clicked()
{
    m_port->write(ui->lineEdit_send->text().toUtf8());
}

void MainWindow::ReadData()
{
    /******************此处尽量不要放过多的函数处理,否则影响数据接收******************/
    QByteArray arr;
    static QByteArray buffer;

    arr = m_port->readAll();
    static bool flag = false;

    //判断是不是55开头的数据,如果是的话,就存起来,然后设置flag位true,下次就接收另外一组数据
    if(arr.toHex().startsWith("55"))
    {
        buffer.append(arr); //存储第一组数据
        flag = true;
        return;
    }

    if(flag)
    {

        buffer.append(arr); //存储第二组数据
        char *string1 = buffer.toHex().data();

        uint8_t data[1024];
        size_t data_length;
        data_length = NLink_StringToHex(string1, data);  //将字符串转换为Hex格式,并将数据存储在data中

        if (g_nlt_nodeframe2.UnpackData(data, data_length))
        {
          nlt_nodeframe2_result_t *result = &g_nlt_nodeframe2.result;
          qDebug()<<"LinkTrack NodeFrame0 data unpack successfully:n";

    //      和上位机对比,数据是正确的
          qDebug()<< "位置:" <<result->pos_3d[0] <<result->pos_3d[1] <<result->pos_3d[2];
          qDebug()<< "速度:" <<result->vel_3d[0] <<result->vel_3d[1] <<result->vel_3d[2];
          qDebug()<< "加速度:" <<result->imu_acc_3d[0] <<result->imu_acc_3d[1] <<result->imu_acc_3d[2];

          ui->textEdit_get->clear();
          QString str1, str2, str3;

          str1 ="位置:" + QString::number(result->pos_3d[0]) + " " + QString::number(result->pos_3d[1]) + " " + QString::number(result->pos_3d[2]);
          str2 ="速度:" + QString::number(result->vel_3d[0]) + " " + QString::number(result->vel_3d[1]) + " " + QString::number(result->vel_3d[2]);
          str3 ="加速度:" + QString::number(result->imu_acc_3d[0]) + " " + QString::number(result->imu_acc_3d[1]) + " " + QString::number(result->imu_acc_3d[2]);

          ui->textEdit_get->append(str1);
          ui->textEdit_get->append(str2);
          ui->textEdit_get->append(str3);

         }

        buffer.clear();//因为buffer是静态变量,用完一次后要记得清空,否则会使得内存溢出
        flag=false;
    }

}


void MainWindow::on_check_clicked()
{

    ui->comboBox_port->clear();

    _Init();
}

Qt工程文件

在我的博客里面找一下,0积分下载

https://download.csdn.net/download/laoxue123456/36761662

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维度攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值