最近要用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()<< "位置:" <pos_3d[0] <pos_3d[1] <pos_3d[2];
qDebug()<< "速度:" <vel_3d[0] <vel_3d[1] <vel_3d[2];
qDebug()<< "加速度:" <imu_acc_3d[0] <imu_acc_3d[1] <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#include #include 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#include #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()<< "位置:" < pos_3d[0] < pos_3d[1] < pos_3d[2]; qDebug()<< "速度:" < vel_3d[0] < vel_3d[1] < vel_3d[2]; qDebug()<< "加速度:" < imu_acc_3d[0] < imu_acc_3d[1] < 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



