【UWB定位】 - DWM1000模块调试简单心得 - 3

2023-05-16

UWB定位 - DWM1000模块调试简单心得 - 1

UWB定位 - DWM1000模块调试简单心得 - 2

前俩篇介绍了简单的一基站一标签TOF方式测距,第三篇我们来搭建一个 一标签三基站 的定位demo。

目的 : 标签与三个基站分别测距,基站得到数据后统一汇总到一个总基站,总基站通过串口将同一时刻三基站与标签的距离值输出到串口调试助手,或者想实现定位的话,我们可以直接写到管道、文件里,由电脑linux端处理数据,进而结合三点定位算法,实现标签简单定位,今天我们主要完成1对3测距输出。

环境 : 4个stm32+DWM1000模块(3基站1标签),keli软件、标签供电电池、usb 转 TTL、sscom串口调试助手。

正文:

1、其实三基站一标签就是在 1对 1的情况多了俩路数据而已,然后就是将多的俩路数据汇总到一个总基站上面总基站。 我们知道TOF测距方式其实就是标签与基站的数据包的发送与回应。数据包是什么,就是带有帧头、帧尾、目的、源的一帧数据。即代码里( W A V E 即为目的地址和源地址,这是标签的第一次请求数据包,对应的我们看基站的即是  V E W A ,这个我们可以自行修改,基站与标签对应就行)

/* Frames used in the ranging process. See NOTE 2 below. */
static uint8 tx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x21, 0, 0};
static uint8 rx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'V', 'E', 'W', 'A', 0x10, 0x02, 0, 0, 0, 0};
static uint8 tx_final_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

具体的数据包解析代码最后有注释,我在这粘一下吧,大家自行翻译对着位数看一下哈,很简单

 *    The first 10 bytes of those frame are common and are composed of the following fields:
 *     - byte 0/1: frame control (0x8841 to indicate a data frame using 16-bit addressing).
 *     - byte 2: sequence number, incremented for each new frame.
 *     - byte 3/4: PAN ID (0xDECA).
 *     - byte 5/6: destination address, see NOTE 3 below.
 *     - byte 7/8: source address, see NOTE 3 below.
 *     - byte 9: function code (specific values to indicate which message it is in the ranging process).

2、经过上面我们了解,我们先把一标签对三基站的测距做好之后在做基站数据的汇总不就OK了吗。上边说过,基站与标签是通过数据包的收发来进行的,1基站1标签是一路数据的收发,那我们要实现1标签3基站就多加俩个数据包,然后标签轮询发送(不要阻塞)或者以多任务的形式来发送接收不就OK了。数据包的主要区别就在于目的和源地址的区别。我们可以自己从新定义2类数据包或是用指针数组都可以,咱就用看起来麻烦点的重新分别定义来写吧,如下标签、基站部分参考。

标签:

//标签部分数据包定义
/* Frames used in the ranging process. See NOTE 2 below. */
static uint8 tx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x21, 0, 0};
static uint8 rx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'V', 'E', 'W', 'A', 0x10, 0x02, 0, 0, 0, 0};
static uint8 tx_final_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static uint8 tx_poll_msg_station2[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'T', 'I', 'O', 'N', 0x21, 0, 0};
static uint8 rx_resp_msg_station2[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'N', 'O', 'T', 'I', 0x10, 0x02, 0, 0, 0, 0};
static uint8 tx_final_msg_station2[] = {0x41, 0x88, 0, 0xCA, 0xDE,'T', 'I', 'O', 'N', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	
static uint8 tx_poll_msg_station3[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'F', 'I', 'G', 'H', 0x21, 0, 0};
static uint8 rx_resp_msg_station3[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'H', 'G', 'I', 'F', 0x10, 0x02, 0, 0, 0, 0};
static uint8 tx_final_msg_station3[] = {0x41, 0x88, 0, 0xCA, 0xDE,'F', 'I', 'G', 'H', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

总基站1:(其中 rx_station2_msg[]与 rx_station3_msg[]为接收分基站1号和2号的数据帧包格式,即数据汇总)

//总基站1号 rx_station2_msg[]与 rx_station3_msg[]为接收分基站1/2的数据帧包
/* Frames used in the ranging process. See NOTE 2 below. */
static uint8 rx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x21, 0, 0};
static uint8 tx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'V', 'E', 'W', 'A', 0x10, 0x02, 0, 0, 0, 0};
static uint8 rx_final_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'W', 'A', 'V', 'E', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static uint8 rx_station2_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'S', 'I', 'G', 'N', 0x23, 0, 0, 0, 0,0,0,0,0,0,0};
	
static uint8 rx_station3_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'T', 'H', 'I', 'R', 0x23, 0, 0, 0, 0,0,0,0,0,0,0};
/* Length of the common part of the message (up to and including the function code, see NOTE 2 below). */

分基站2(数据包与总基站和标签都是对应的):

//分基站1
/* Frames used in the ranging process. See NOTE 2 below. */
static uint8 rx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'T', 'I', 'O', 'N', 0x21, 0, 0};
static uint8 tx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'N', 'O', 'T', 'I', 0x10, 0x02, 0, 0, 0, 0};
static uint8 rx_final_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE,'T', 'I', 'O', 'N', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static uint8 tx_final_msg_station2[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'S', 'I', 'G', 'N', 0x23, 0, 0, 0, 0,0,0,0,0,0,0};//发送给总基站的数据包

分基站 3:

/* Frames used in the ranging process. See NOTE 2 below. */
static uint8 rx_poll_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'F', 'I', 'G', 'H', 0x21, 0, 0};
static uint8 tx_resp_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'H', 'G', 'I', 'F', 0x10, 0x02, 0, 0, 0, 0};
static uint8 rx_final_msg[] = {0x41, 0x88, 0, 0xCA, 0xDE,'F', 'I', 'G', 'H', 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

static uint8 tx_final_msg_station3[] = {0x41, 0x88, 0, 0xCA, 0xDE, 'T', 'H', 'I', 'R', 0x23, 0, 0, 0, 0,0,0,0,0,0,0};

然后就是在标签中的代码里多定义俩个帧序列号(标签对应的3个基站 所以需要3个)(分基站其实只需要定义俩个就可以了,一个是与标签的,一个是与总基站的):

//总基站1
/* Frame sequence number, incremented after each transmission. */
static uint8 frame_seq_nb = 0;
static uint8 frame_seq_nb_station2 = 0;
static uint8 frame_seq_nb_station3 = 0;	

剩下的其实就是标签的轮询发送了,其实就是在之前1对1基础上copy俩次完整的发送接收步骤,代码就不贴了太多了自己懒没有精简,具体结构框架为(其中需要替换的确定替换对啊,比如各个数据包的名字和序列帧号名字,细心点):

while(1)

{
   发送与第一个基站交互的数据包
   if (status_reg & SYS_STATUS_RXFCG)
    {
        与基站1 数据 交互处理 
    }
    else
     {
           /* Clear RX error events in the DW1000 status register. */
           dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
     }

     延时几毫秒;

   发送与第二个基站交互的数据包
   if (status_reg & SYS_STATUS_RXFCG)
    {
        与基站2 数据 交互处理 // 相应的发送接收数据包要记得替换,还有序列帧号
    }
    else
     {
           /* Clear RX error events in the DW1000 status register. */
           dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
     }

     延时几毫秒;

     发送与第三个基站交互的数据包
   if (status_reg & SYS_STATUS_RXFCG)
    {
        与基站3 数据 交互处理// 相应的发送接收数据包要记得替换,还有序列帧号
    }
    else
     {
           /* Clear RX error events in the DW1000 status register. */
           dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_ALL_RX_ERR);
     }

     延时几毫秒;

}

检查无误的话,这样其实1标签与3基站的测距已经完成了,可以自行将各个分基站接收的数据输出显示一下。接下来是将我们分基站2和3号收到的数据立马发送给总基站1。

如下:

之前我们已经定义好了基站与基站之前发送的数据包(其实和标签与基站的原理一样,只不过基站与基站更简单而已,类似于ss测距方法)

分基站 要做的就是解析到数据将距离数据装入数据包中,发送出去

sprintf(dist_str, "DIST1#%3.2f#m     ", distance);	
USART_putstr(dist_str);									
USART_putc('\n');
for(i=10;i<18;i++)
{
  tx_final_msg_station2[i] = dist_str[i-4];
}										  

tx_final_msg_station2[ALL_MSG_SN_IDX] = frame_seq_nb_tx_station2;	
dwt_writetxdata(sizeof(tx_final_msg_station2), tx_final_msg_station2, 0);
dwt_writetxfctrl(sizeof(tx_final_msg_station2), 0);
/* Start transmission. */
dwt_starttx(DWT_START_TX_IMMEDIATE);
 while (!(dwt_read32bitreg(SYS_STATUS_ID) & SYS_STATUS_TXFRS)){};	
dwt_write32bitreg(SYS_STATUS_ID, SYS_STATUS_TXFRS);

总基站就负责接收(框架如下):


                if (status_reg & SYS_STATUS_RXFCG)
                {
                  if (memcmp(rx_buffer, rx_final_msg, ALL_MSG_COMMON_LEN) == 0)
                    {
                           /*处理与标签的数据交互*/
                        uint32 poll_tx_ts, resp_rx_ts, final_tx_ts;
                        uint32 poll_rx_ts_32, resp_tx_ts_32, final_rx_ts_32;
                        double Ra, Rb, Da, Db;
                        int64 tof_dtu;

                        /* Retrieve response transmission and final reception timestamps. */
                        resp_tx_ts = get_tx_timestamp_u64();
                        final_rx_ts = get_rx_timestamp_u64();

                        /* Get timestamps embedded in the final message. */
                        final_msg_get_ts(&rx_buffer[FINAL_MSG_POLL_TX_TS_IDX], &poll_tx_ts);
                        final_msg_get_ts(&rx_buffer[FINAL_MSG_RESP_RX_TS_IDX], &resp_rx_ts);
                        final_msg_get_ts(&rx_buffer[FINAL_MSG_FINAL_TX_TS_IDX], &final_tx_ts);

                        /* Compute time of flight. 32-bit subtractions give correct answers even if clock has wrapped. See NOTE 10 below. */
                        poll_rx_ts_32 = (uint32)poll_rx_ts;
                        resp_tx_ts_32 = (uint32)resp_tx_ts;
                        final_rx_ts_32 = (uint32)final_rx_ts;
                        Ra = (double)(resp_rx_ts - poll_tx_ts);
                        Rb = (double)(final_rx_ts_32 - resp_tx_ts_32);
                        Da = (double)(final_tx_ts - resp_rx_ts);
                        Db = (double)(resp_tx_ts_32 - poll_rx_ts_32);
                        tof_dtu = (int64)((Ra * Rb - Da * Db) / (Ra + Rb + Da + Db));

                        tof = tof_dtu * DWT_TIME_UNITS;
                        distance = tof * SPEED_OF_LIGHT;
                        if(distance <= 0.00) distance = 0.00;
                        /* Display computed distance on LCD. */
                        sprintf(dist_str, "DIST2@%3.2f@m    ", distance);
		        USART_putstr(dist_str);
                        USART_putc('\n');
                        //lcd_display_str(dist_str);                            
                    }
                   else if(memcmp(rx_buffer, rx_station2_msg, ALL_MSG_COMMON_LEN) == 0)
	             {	 		
                        //USART_putstr("rec data from station2 :\n");
			for(i=6;i<14;i++)
			{
			  dist_str_rx_station2[i] = rx_buffer[i+4];
			}
		        USART_putstr(dist_str_rx_station2);
		        USART_putc('\n');
		     }
                   else if(memcmp(rx_buffer, rx_station3_msg, ALL_MSG_COMMON_LEN) == 0)
	             {	 		
                        //USART_putstr("rec data from station3 :\n");
			for(i=6;i<14;i++)
			{
			  dist_str_rx_station3[i] = rx_buffer[i+4];
			}
		        USART_putstr(dist_str_rx_station3);
		        USART_putc('\n');
		     }
                }

之前没问题的话,然后将总基站数据发送到串口调试助手上,我们就会看到实时刷新的标签到3基站的距离,至此,目的完成。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【UWB定位】 - DWM1000模块调试简单心得 - 3 的相关文章

  • 普通程序员如何入门AI

    毫无疑问 xff0c 人工智能是目前整个互联网领域最火的行业 xff0c 随着AlphaGo战胜世界围棋冠军 xff0c 以及各种无人驾驶 智能家居项目的布道 xff0c 人们已经意识到了AI就是下一个风口 当然 xff0c 程序员是我见过
  • 扩展卡尔曼线性化近似与仿真

    扩展卡尔曼线性化近似与仿真 关于线性化直入主题 上例子小车运动方式 xff1a 沿着圆心在原点 半径为5的圆进行匀速圆周运动 xff0c 其角速度为w 即每次更新变化w个角度 仿真结果总结Matlab测试代码 xff08 EKF test
  • Ubuntu挂载硬盘

    Ubuntu挂载硬盘 1 查看磁盘信息命令 fdisk l 2 查看硬盘的UUID命令sudo blkid 3 mkdir创建挂载点WorkpaceP2和WorkpaceP2 4 永久性挂载分区 xff0c 修改分区文件 xff0c 输入如
  • FreeRTOS——创建任务

    FreeRTOS的设计小巧且简易 xff0c 整个核心代码只有3到4个C文件 xff0c 为了让代码容易阅读 移植和维护 xff0c 大部分的代码都是以C语言编写 xff0c 只有一些函数 xff08 多数是架构特定排班副程序 xff09
  • QT二次开发Kvaser

    前言 最近工作中需要自己去开发一个上位机 xff0c 上位机的通讯方式是CAN xff0c 利用Kvaser将CAN信息传递到上位机 xff0c 所以就需要二次开发Kvaser xff0c 保证上位机的正常通讯 原本是本着前人栽树 xff0
  • Ubuntu 安装ROS (解决rosdep init 失败)

    当前网络上有很多的ROS安装教程 xff0c 但是由于国内的网络问题 xff0c 所以在教程进行到rosdep init时 xff0c 会出现问题 xff0c 所以这篇博客主要解决这个问题 xff0c 以下为教程全部内容 xff1a 引用教
  • Ubuntu20.04部署编译LVI-SAM

    该动图来自LVI SAM开源地址 xff08 https github com TixiaoShan LVI SAM xff09 1 写在开头 1 1 为何诞生此文 近期在学习SLAM相关知识 xff0c 拜读了此篇经典论文LVI SAM
  • QT中的强制类型转换

    当使用C语言那种形式的强制转换 xff0c 发现QT会给出一个使用旧的方式的警告 所以在QT中使用如下类型转换 xff0c 就不会有警告 xff0c 而且这种方式的强制转换更加的安全 xff08 1 xff09 dynamic cast l
  • QT之QCharts的使用(绘制折线图)

    一 画折线图 1 修改 pro文件 在里面添加QT 43 61 charts 2 MyWidget h程序 ifndef MYWIDGET H define MYWIDGET H include lt QWidget gt 添加以下三个头文
  • 恢复经过软件处理过的U盘导致的U盘空间显示不正确等问题

    1 win 43 R xff0c 打开运行 xff0c 输入CMD xff0c 点击确定 2 在命令行中输入DISKPART并回车 xff0c 会跳出一个窗口 xff0c 这就进入了diskpart 3 在跳出的窗口diskpart 中输入
  • 关于STM32 CAN 发送失败问题解释

    首先解释一下CAN几个配置的功能 xff1a 1 CAN InitStruct CAN TTCM 61 DISABLE 这个只在某些CAN标准中使用 xff0c 就设置为DISABLE 2 CAN InitStruct CAN ABOM 6
  • VS2022调试vector无法显示详细信息

    使用vs2022调试vector发现这样的现象 xff1a 为了显示vector大小以及详细的元素 xff0c 需要编写natvis文件 span class token operator lt span span class token

随机推荐