UWB定位的两种解法

news2024/11/28 19:42:25

UWB(Ultra-Wideband)技术是一种短脉冲无线电技术(短脉冲意味着信号的带宽很大,因此称为超宽带),其应用非常广泛,其中之一就是室内定位,通过计算信号传播的时间差,可以得到标签和基站之间的距离,如果有足够多的基站,就可以通过三角定位等方法计算出标签的位置。如下图所示:

在这里插入图片描述

本文将介绍两种利用标签与基站的距离信息求解标签位置的方法,分别是线性最小二乘和非线性优化。

线性最小二乘

对于线性最小二乘方法,假设有 m m m个基站,第 i i i个基站的坐标为 a i = ( a i , 1 , a i , 2 , a i , 3 ) a_i=(a_{i,1},a_{i,2},a_{i,3}) ai=(ai,1,ai,2,ai,3),标签与第 i i i个基站之间的距离为 r i r_i ri,标签的坐标为 x = ( x 1 , x 2 , x 3 ) x=(x_1,x_2,x_3) x=(x1,x2,x3)。则可以得到以下方程组:

{ ( x 1 − a 1 , 1 ) 2 + ( x 2 − a 1 , 2 ) 2 + ( x 3 − a 1 , 3 ) 2 = r 1 2 ( x 1 − a 2 , 1 ) 2 + ( x 2 − a 2 , 2 ) 2 + ( x 3 − a 2 , 3 ) 2 = r 2 2 ⋯ ( x 1 − a m , 1 ) 2 + ( x 2 − a m , 2 ) 2 + ( x 3 − a m , 3 ) 2 = r m 2 \begin{cases} (x_1-a_{1,1})^2+(x_2-a_{1,2})^2+(x_3-a_{1,3})^2=r_1^2 \\ (x_1-a_{2,1})^2+(x_2-a_{2,2})^2+(x_3-a_{2,3})^2=r_2^2 \\ \cdots \\ (x_1-a_{m,1})^2+(x_2-a_{m,2})^2+(x_3-a_{m,3})^2=r_m^2 \end{cases} (x1a1,1)2+(x2a1,2)2+(x3a1,3)2=r12(x1a2,1)2+(x2a2,2)2+(x3a2,3)2=r22(x1am,1)2+(x2am,2)2+(x3am,3)2=rm2

使用 a i a_i ai表示第 i i i个基站的坐标向量,向量的模表示为:

∣ ∣ a i ∣ ∣ = a i , 1 2 + a i , 2 2 + a i , 3 2 ||a_i||=\sqrt{a_{i,1}^2+a_{i,2}^2+a_{i,3}^2} ∣∣ai∣∣=ai,12+ai,22+ai,32

将第二行以及之后的方程减去第一行,可以得到以下方程组:

{ 2 ( a 1 − a 2 ) T x = r 2 2 − r 2 2 − ∣ ∣ a 2 ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 2 ( a 1 − a 3 ) T x = r 3 2 − r 1 2 − ∣ ∣ a 3 ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 ⋯ 2 ( a 1 − a m ) T x = r m 2 − r 1 2 − ∣ ∣ a m ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 \begin{cases} 2(a_1-a_2)^Tx=r_2^2 - r_2^2 - ||a_2||^2 + ||a_1||^2 \\ 2(a_1-a_3)^Tx=r_3^2 - r_1^2 - ||a_3||^2 + ||a_1||^2 \\ \cdots \\ 2(a_1-a_m)^Tx=r_m^2 - r_1^2 - ||a_m||^2 + ||a_1||^2 \end{cases} 2(a1a2)Tx=r22r22∣∣a22+∣∣a122(a1a3)Tx=r32r12∣∣a32+∣∣a122(a1am)Tx=rm2r12∣∣am2+∣∣a12

将上述方程组写成矩阵形式,可以得到:

[ 2 ( a 1 − a 2 ) T 2 ( a 1 − a 3 ) T ⋮ 2 ( a 1 − a m ) T ] x = [ r 2 2 − r 2 2 − ∣ ∣ a 2 ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 r 3 2 − r 1 2 − ∣ ∣ a 3 ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 ⋮ r m 2 − r 1 2 − ∣ ∣ a m ∣ ∣ 2 + ∣ ∣ a 1 ∣ ∣ 2 ] \begin{bmatrix} 2(a_1-a_2)^T \\ 2(a_1-a_3)^T \\ \vdots \\ 2(a_1-a_m)^T \end{bmatrix}x= \begin{bmatrix} r_2^2 - r_2^2 - ||a_2||^2 + ||a_1||^2 \\ r_3^2 - r_1^2 - ||a_3||^2 + ||a_1||^2 \\ \vdots \\ r_m^2 - r_1^2 - ||a_m||^2 + ||a_1||^2 \end{bmatrix} 2(a1a2)T2(a1a3)T2(a1am)T x= r22r22∣∣a22+∣∣a12r32r12∣∣a32+∣∣a12rm2r12∣∣am2+∣∣a12

A A A为上述矩阵, b b b为上述向量,则可以得到以下方程:

A x = b Ax=b Ax=b

使用矩阵的伪逆求解上述方程,即可得到标签的位置 x x x

非线性优化

非线性优化方法通过最小化标签与基站之间距离的误差来求解标签的位置,优化变量即为标签的位置。

具体来说,假设有 m m m个基站,第 i i i个基站的坐标为 a i = ( a i , 1 , a i , 2 , a i , 3 ) a_i=(a_{i,1},a_{i,2},a_{i,3}) ai=(ai,1,ai,2,ai,3),标签与第 i i i个基站之间的距离为 r i r_i ri,标签的坐标为 x = ( x 1 , x 2 , x 3 ) x=(x_1,x_2,x_3) x=(x1,x2,x3),则标签与第 i i i个基站之间距离的误差为:

f i ( x ) = ( x 1 − a i , 1 ) 2 + ( x 2 − a i , 2 ) 2 + ( x 3 − a i , 3 ) 2 − r i f_i(x)=\sqrt{(x_1-a_{i,1})^2+(x_2-a_{i,2})^2+(x_3-a_{i,3})^2}-r_i fi(x)=(x1ai,1)2+(x2ai,2)2+(x3ai,3)2 ri

将所有基站的误差平方和作为目标函数,即:

min ⁡ x ∑ i = 1 m f i 2 ( x ) \min_{x} \sum_{i=1}^{m} f_i^2(x) xmini=1mfi2(x)

该目标函数是一个非线性函数,可以使用非线性最小二乘法进行求解。具体来说,可以使用LM算法(Levenberg-Marquardt算法)对目标函数进行优化,求解出标签的位置 x x x

LM算法

LM算法是一种非线性最小二乘法的优化算法,可以用于求解非线性优化问题。它是一种介于Gauss-Newton算法和梯度下降算法之间的方法,具有收敛速度快、收敛性好等优点。

LM算法的流程如下:

  1. 初始化参数 x x x,设置初始步长 λ \lambda λ,设置容差 ϵ \epsilon ϵ,设置最大迭代次数 m a x _ i t e r max\_iter max_iter
  2. 计算误差向量 f ( x ) f(x) f(x)和雅可比矩阵 J ( x ) J(x) J(x)
  3. 计算增量 Δ x \Delta x Δx,其中 Δ x \Delta x Δx满足以下方程:

( J T J + λ I ) Δ x = − J T f ( x ) (J^TJ+\lambda I)\Delta x=-J^Tf(x) (JTJ+λI)Δx=JTf(x)

  1. 计算新的参数 x n e w = x + Δ x x_{new}=x+\Delta x xnew=x+Δx,计算新的误差向量 f ( x n e w ) f(x_{new}) f(xnew)
  2. 如果新的误差向量的平方和小于容差 ϵ \epsilon ϵ,则停止迭代;否则,判断新的误差向量是否比原来的误差向量更小,如果更小,则减小步长 λ \lambda λ,重复步骤2-5;否则,增加步长 λ \lambda λ,重复步骤2-5。

附录中给出了LM算法的C++实现,其中雅可比矩阵的计算使用了数值微分,也可以使用解析求导的方法进行计算:

∂ f i ∂ x j = x j − a i , j ( x 1 − a i , 1 ) 2 + ( x 2 − a i , 2 ) 2 + ( x 3 − a i , 3 ) 2 \frac{\partial f_i}{\partial x_j}=\frac{x_j-a_{i,j}}{\sqrt{(x_1-a_{i,1})^2+(x_2-a_{i,2})^2+(x_3-a_{i,3})^2}} xjfi=(x1ai,1)2+(x2ai,2)2+(x3ai,3)2 xjai,j

其中 j j j x x x的第 j j j个分量,分母表示标签与第 i i i个基站之间的距离。

附录:C++实现

本文代码基于Eigen库实现,包含的头文件如下:

#include <Eigen/Core>
#include <Eigen/Dense>

线性最小二乘

以下代码使用线性最小二乘法求解标签的位置,它是一个lambda表达式,输入参数为基站的坐标向量和标签与基站之间的距离,输出为标签的位置。

auto trilateration = [](const std::vector<Eigen::Vector3d>& uwb_stations, const std::vector<double>& uwb_range) {
    Eigen::MatrixXd A(uwb_stations.size() - 1, 3);
    Eigen::VectorXd b(uwb_stations.size() - 1);
    for (int i = 1; i < uwb_stations.size(); i++) {
        A.row(i - 1) = 2 * (uwb_stations[0] - uwb_stations[i]).transpose();
        b(i - 1) = uwb_range[i] * uwb_range[i] - uwb_range[0] * uwb_range[0] - (uwb_stations[i].squaredNorm() - uwb_stations[0].squaredNorm());
    }
    // 使用SVD求解线性最小二乘问题
    Eigen::Vector3d pos = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
    return std::vector<double>({pos[0], pos[1], pos[2]});
};

非线性优化

以下代码使用LM算法对上述目标函数进行优化,求解出标签的位置,求导过程使用了数值微分。

// 定义非线性优化问题
struct UWBProblem {
    // 测量值
    std::vector<double> ranges;
    // anchor坐标
    std::vector<std::vector<double>> anchors;
    // 优化变量
    std::vector<double> x;
    // 重载()运算符,计算误差
    int operator()(const double* x, double* fvec) const {
        for (int i = 0; i < ranges.size(); ++i) {
            double r = ranges[i];
            std::vector<double> a = anchors[i];
            double dist = sqrt(pow(x[0] - a[0], 2) + pow(x[1] - a[1], 2) + pow(x[2] - a[2], 2));
            fvec[i] = dist - r;
        }
        return 0;
    }
};

// 非线性最小二乘法求解器
void levenbergMarquardt(UWBProblem& problem, double lambda, double tol, int max_iter) {
    Eigen::VectorXd x = Eigen::Map<Eigen::VectorXd>(problem.x.data(), problem.x.size());
    int n = x.size();
    int m = problem.ranges.size();
    Eigen::VectorXd fvec(m);
    Eigen::MatrixXd J(m, n);
    for (int iter = 0; iter < max_iter; ++iter) {
        // 计算误差向量和雅可比矩阵
        problem(x.data(), fvec.data());
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                double h = 1e-6;
                Eigen::VectorXd x1 = x;
                x1(j) += h;
                Eigen::VectorXd fvec1(m);
                problem(x1.data(), fvec1.data());
                J(i, j) = (fvec1(i) - fvec(i)) / h;
            }
        }
        // 计算增量
        Eigen::MatrixXd JtJ = J.transpose() * J;
        Eigen::VectorXd Jtf = J.transpose() * fvec;
        JtJ.diagonal().array() += lambda;
        Eigen::VectorXd dx = -JtJ.ldlt().solve(Jtf);
        // 更新变量
        Eigen::VectorXd x_new = x + dx;
        // 判断是否收敛
        double err = fvec.squaredNorm();
        if (err < tol) {
            break;
        }
        // 更新lambda
        Eigen::VectorXd fvec_new(m);
        problem(x_new.data(), fvec_new.data());
        double err_new = fvec_new.squaredNorm();
        if (err_new < err) {
            lambda /= 10;
            x = x_new;
            fvec = fvec_new;
        } else {
            lambda *= 10;
        }
    }
    // 更新优化变量
    problem.x = std::vector<double>(x.data(), x.data() + x.size());
}

调用方法如下:

// 设置初始值
vector<double> x0 = {0, 0, 0};
// 设置测量值
std::vector<double> ranges;
// 设置anchor坐标
std::vector<std::vector<double>> anchors;
UWBProblem problem;
problem.ranges = ranges;
problem.anchors = anchors;
problem.x = x0;
// 进行优化
double lambda = 0.1;
double tol = 1e-6;
int max_iter = 1000;
levenbergMarquardt(problem, lambda, tol, max_iter);
// 输出优化结果
std::cout << "x = " << problem.x[0] << ", y = " << problem.x[1] << ", z = " << problem.x[2] << std::endl;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/664346.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity核心1——图片导入与图片设置

一、图片导入概述 ​ Unity 支持的图片格式有很多 BMP&#xff1a;是 Windows 操作系统的标准图像文件格式&#xff0c;特点是几乎不进行压缩&#xff0c;占磁盘空间大 TIF&#xff1a;基本不损失图片信息的图片格式&#xff0c;缺点是体积大 JPG&#xff1a;一般指 JPEG 格…

【Elasticsearch】 之 Translog/FST/FOR/RBM算法

目录 Translog FST/FOR/RBM算法解析 FST FOR&#xff08;Frame of Reference&#xff09;: RBM&#xff08;Roaring Bitmaps&#xff09;-(for filter cache) Translog es是近实时的存储搜索引。近实时&#xff0c;并不能保证被立刻看到。数据被看到的时候数据已经作为一…

工业级以太网RJ45温湿度监控系统解决方案之关键POE供电温湿度传感器

目 录 一、关键词…………………………………………………………………………3 二、 产品概述………………………………………………………………………3 三、 应用范围………………………………………………………………………3 四、 产品特点………………………………

Linux0.11内核源码解析-file_dev.c

目录 功能描述 int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) int file_write(struct m_inode * inode, struct file * filp, char * buf, int count) 功能描述 该文件主要是由两个函数file_read()和file_write()组成&#xff0c;提供…

Nginx网站服务——服务基础

文章目录 一.Nginx服务基础1.关于Nginx的特点2.简述Nginx和Apache的差异3.Nginx 相对于 Apache 的优点4.Apache 相对于 Nginx 的优点5.阻塞与非阻塞6.同步与异步7.nginx的应用场景 二.编译安装nginx服务1.在线安装nginx1.1 yum部署Nginx1.2 扩展源安装完后直接安装Nginx 2.ngin…

MySQL数据库---存储引擎(MyISAM与InnoDB)

目录 前言一、存储引擎概念介绍二、MyISAM三、InnoDB四、配置合适的存储引擎总结 前言 数据库存储引擎是数据库底层软件组织&#xff0c;数据库管理系统&#xff08;DBMS&#xff09;使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧…

Vue中如何进行图像识别与人脸对比

Vue中如何进行图像识别与人脸对比 随着人工智能的发展&#xff0c;图像识别和人脸识别技术已经被广泛应用于各种应用程序中。Vue作为一种流行的前端框架&#xff0c;提供了许多实用工具和库&#xff0c;可以帮助我们在应用程序中进行图像识别和人脸识别。在本文中&#xff0c;…

docker换源(docker镜像源)pull超时(pull镜像超时)/etc/docker/daemon.json

文章目录 pull了n次都超时&#xff0c;也是醉了更换镜像源步骤1. 打开终端并以管理员身份登录到Docker主机。2. 编辑Docker配置文件daemon.json。该文件用于配置Docker守护进程的参数。3. 在daemon.json文件中添加以下内容&#xff0c;将<镜像源地址>替换为您选择的镜像源…

基于matlab仿真具有不同传感器模式的锥形阵列(附源码)

一、前言 此示例说明如何在不同的阵列配置上应用锥形和模型细化。它还演示了如何创建具有不同元素模式的数组。 二、ULA 逐渐变细 本节介绍如何在均匀线性阵列 &#xff08;ULA&#xff09; 的元素上应用泰勒窗口以降低旁瓣电平。 比较锥形阵列和非锥形阵列的响应。请注意锥形U…

外部局域网直接访问WSL2

1. 开启hyper-v 1、首先&#xff0c;进入控制面板—程序—启用或关闭windows功能&#xff0c;勾选hyper-v&#xff0c;确认后重启电脑。2、打开 Windows PowerShell&#xff0c;输入 systeminfo 命令 能够看到出现了很多处理器的信息&#xff0c;最末尾有个 Hyper-V 要求&…

Redis 2023面试5题(一)

一、Redis是单线程还是多线程 在面试中&#xff0c;当被问到Redis是单线程还是多线程这个问题时&#xff0c;可以按照以下思路进行回答&#xff1a; 首先&#xff0c;Redis的核心业务部分是单线程的&#xff0c;即命令处理部分是单线程的。然而&#xff0c;Redis也支持多路复…

Java---第四章(数组基础,冒泡排序,二分查找,多维数组)

Java---第四章 一 数组基本知识数组操作 二 数组实操数组排序二分查找二维数组 一 数组 基本知识 概念&#xff1a; 数组是编程语言中的一种常见的数据结构&#xff0c;能够存储一组相同类型的数据 作用&#xff1a; 存储一组相同类型的数据&#xff0c;方便进行数理统计&am…

springboot3生命周期监听的使用和源码解析

定义SpringApplicationRunListener来监听springApplication的启动 1.通过实现springApplicationRunListener来实现监听。 2.在 META-INF/spring.factories 中配置 org.springframework.boot.SpringApplicationRunListener自己的Listener。 在默认的springboot配置中就有给我…

视觉SLAM十四讲——ch12实践(建图)

视觉SLAM十四讲——ch12的实践操作及避坑 0.实践前小知识介绍1. 实践操作前的准备工作2. 实践过程2.1 单目稠密重建2.2 RGB-D稠密建图2.3 点云地图2.4 从点云重建网格2.5 八叉树地图 3. 遇到的问题及解决办法3.1 cmake ..时&#xff0c;出现opencv版本问题3.2 make -j8时&#…

使用腾讯云服务器从零搭建个人网站

前期准备工作 1.服务器重装系统 选择ubuntu18的系统镜像 2.开放端口 需要开放80&#xff0c;27017&#xff0c;3000&#xff0c;22端口 80端口用于配置nginx服务27017端口用于连接mongondb数据库3000端口是启动项目的端口22端口用于ssh远程连接服务器&#xff0c;一般默认会…

SpringBoot - @Transactional注解详解

简介 Spring中的Transactional注解&#xff0c;基于动态代理的机制&#xff0c;提供了一种透明的事务管理机制&#xff0c;方便快捷的解决在开发中碰到的问题&#xff0c;Transactional 的事务开启 &#xff0c;或者是基于接口的或者是基于类的代理被创建。Spring为了更好的支…

数据库SQL查询(二)之连接查询

本文介绍SQL查询&#xff0c;如何在海量数据中筛选想要数据&#xff1b; 数据库管理系统选择&#xff1a;关系型数据库mysql 数据库管理工具选择&#xff1a;navicat 本文中查询语句和查询案例参考自&#xff1a;https://edu.csdn.net/course/detail/27673?ops_request_mis…

python django vue httprunner 实现接口自动化平台(最终版)

一、项目介绍&#xff1a; 1.1 项目地址 前端地址&#xff1a; GitHub - 18713341733/test_platform_service: django vue 实现接口自动化平台 后端地址&#xff1a; GitHub - 18713341733/test_platform_front: Django vue实现接口自动化平台 1.2 项目介绍 1.2.1 环境…

在 K8S 中部署一个应用 上

本身在 K8S 中部署一个应用是需要写 yaml 文件的&#xff0c;我们这次简单部署&#xff0c;通过拉取网络上的镜像来部署应用&#xff0c;会用图解的方式来分享一下&#xff0c;过程中都发生了什么 简单部署一个程序 我们可以通过 kubectl run 的方式来简单部署一个应用&#…

Python深度学习027:什么是梯度、梯度消失、梯度爆炸以及如何解决

文章目录 1. 梯度的概念2. 梯度更新中存在的问题2.1 梯度消失2.2 梯度爆炸3. 解决办法3.1 梯度消失3.2 梯度爆炸1. 梯度的概念 在机器学习中,梯度是指一个多元函数在某一点处的变化率以及变化的方向。 对于一个参数化的函数,梯度可以告诉我们在一个特定的点处函数值增加最快…