我的私有云-IOT定位/追踪系统

news2024/11/16 19:36:11

目录

1. 说明

2 完成后的效果

2.1 实时定位

2.2 轨迹重现 

2.3 设备美照

3. 项目设计

3.1 系统拓扑图​编辑

3.2 技术选型

3.3 消息订阅处理架构图

3.4 frp服务在线监控​编辑

4. 实施

4.1 数据模型 - DeviceLocation

4.2 数据报规格定义

订阅主题

数据报格式 

银尔达后台配置

4.3 脚本:

高德地图GPS定位纠偏脚本

5. 要点:

6. 参考:

- 工具

- Javascript 在线转 Typescript: 

- 码讯定位精度选择(定位技术对比)


1. 说明

      本文介绍一套低成本实现的IOT定位追踪系统方案,实现基于:本地内网服务器-云服务器-IOT终端-手机终端 互联互通基础上的定位追踪应用。

2 完成后的效果

2.1 实时定位

定位实时数据

2.2 轨迹重现 

定位轨迹数据

2.3 设备美照

3. 项目设计

  • 准备一个云服务器,固定带宽2~5M的一般云主机,装frp服务端,负责系统通讯接驳。
  • 本地私有云集群搭建,多核高内存高性能服务器集群,负责提供各种服务,其中之一装Frp客户端,映射各服务端口到云服务器。
  • IOT定位器,在相关后台设置上报消息格式,MQTT服务器路径及订阅主题
  • IOT设备消息路径:定位器-》云服务器-》私有云 MQTT Server-》Nifi-》Kafka-》应用消费服务
  • 使用终端API路径:终端-》云服务器-》私有云 API Server
3.1 系统拓扑图
3.2 技术选型
层次技术/框架/硬件备注
前端Vue3, TypescriptVUE3结合高德地图Api, 实现实时定位与轨迹重现
中间件
  • Nifi - 数据流控制
  • Emqx - MQTT服务
  • Kafka - 消息服务
  • 高德地图2.0
后端FastApi, Kinit使用Kinit做后台管理
数据库Mysql 8, Redis使用mysql里面的地理字段

Geometry存位置数据

定位器硬件银尔达 Air820ugGPS有源天线,供电5V2A,10W,含GPS/BD定位功能,通过其后台设置每分钟上报一次
3.3 消息订阅处理架构图

3.4 frp服务在线监控

4. 实施

4.1 数据模型 - DeviceLocation

参考: FastApi地理坐标数据存取实践_fastapi geoalchemy-CSDN博客

from typing import List, Optional
from datetime import datetime
from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, ForeignKeyConstraint, Index, Integer, String, Table, Text, text
from sqlalchemy.dialects.mysql import TINYINT
from sqlalchemy.orm import Mapped, declarative_base, mapped_column, relationship
from sqlalchemy.orm.base import Mapped
from geoalchemy2 import Geometry, WKBElement
from sqlalchemy.orm import relationship, Mapped, mapped_column
from db.db_base import BaseModel
from .data_types import DeviceType
import uuid
import secrets

metadata = BaseModel.metadata

class DeviceLocation(BaseModel):
    __tablename__ = 'ia_iot_device_location'
    __table_args__ = (
        Index('Index_1', 'iot_device_id'),
        Index('Index_2', 'coordinates')
    )

    id = mapped_column(BigInteger, primary_key=True)
    coordinates: Mapped[WKBElement] = mapped_column(Geometry(geometry_type='POINT', spatial_index=True), nullable=False, comment='地理坐标')
    iot_device_id = mapped_column(BigInteger, server_default=text("'0'"))
    label = mapped_column(String(255, 'utf8mb4_general_ci'))
4.2 数据报规格定义
订阅主题
Topic类型备注
ia001.device.busKafka 主题, 不允许有“/”消息总线
/ia001/report/#Emqx 主题Nifi使用,通配设备上报消息
/ia001/cmd/${IMEI}Emqx 主题下发指令给设备, id - IMEI
数据报格式 
定位{"id":"860048072112954","lng":"113.3558264","lat":"022.5690239"}
银尔达后台配置
主动上报内容格式
基站定位{"id":"${IMEI}","type":"LBS","lng":"${LBSLON}","lat":"${LBSLAT}","csq":"${CSQ}"}
GPS定位{"id":"${IMEI}","type":"GPS","lng":"${GPSLON}","lat":"${GPSLAT}","csq":"${CSQ}"}
4.3 脚本:
高德地图GPS定位纠偏脚本

Javascript 版本: 查看绑定资源 

Typescript 版本: 

/* eslint-disable @typescript-eslint/no-loss-of-precision */

const x_PI: number = (3.14159265358979324 * 3000.0) / 180.0
const PI: number = 3.1415926535897932384626
const a: number = 6378245.0
const ee: number = 0.00669342162296594323

const bd09togcj02 = (bd_lon: number, bd_lat: number): number[] => {
  bd_lon = +bd_lon
  bd_lat = +bd_lat
  const x: number = bd_lon - 0.0065
  const y: number = bd_lat - 0.006
  const z: number = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI)
  const theta: number = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI)
  const gg_lng: number = z * Math.cos(theta)
  const gg_lat: number = z * Math.sin(theta)
  return [gg_lng, gg_lat]
}

const gcj02tobd09 = (lng: number, lat: number): number[] => {
  lat = +lat
  lng = +lng
  const z: number = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI)
  const theta: number = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI)
  const bd_lng: number = z * Math.cos(theta) + 0.0065
  const bd_lat: number = z * Math.sin(theta) + 0.006
  return [bd_lng, bd_lat]
}

const wgs84togcj02 = (lng: number, lat: number): number[] => {
  lat = +lat
  lng = +lng
  if (out_of_china(lng, lat)) {
    return [lng, lat]
  } else {
    const dlat: number = transformlat(lng - 105.0, lat - 35.0)
    const dlng: number = transformlng(lng - 105.0, lat - 35.0)
    const radlat: number = (lat / 180.0) * PI
    let magic: number = Math.sin(radlat)
    magic = 1 - ee * magic * magic
    const sqrtmagic: number = Math.sqrt(magic)
    const dlatAdjusted: number = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
    const dlngAdjusted: number = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
    const mglat: number = lat + dlatAdjusted
    const mglng: number = lng + dlngAdjusted
    return [mglng, mglat]
  }
}

const gcj02towgs84 = (lng: number, lat: number): number[] => {
  lat = +lat
  lng = +lng
  if (out_of_china(lng, lat)) {
    return [lng, lat]
  } else {
    const dlat: number = transformlat(lng - 105.0, lat - 35.0)
    const dlng: number = transformlng(lng - 105.0, lat - 35.0)
    const radlat: number = (lat / 180.0) * PI
    let magic: number = Math.sin(radlat)
    magic = 1 - ee * magic * magic
    const sqrtmagic: number = Math.sqrt(magic)
    const dlatAdjusted: number = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
    const dlngAdjusted: number = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
    const mglat: number = lat + dlatAdjusted
    const mglng: number = lng + dlngAdjusted
    return [lng * 2 - mglng, lat * 2 - mglat]
  }
}

const transformlat = (lng: number, lat: number): number => {
  lat = +lat
  lng = +lng
  let ret: number =
    -100.0 +
    2.0 * lng +
    3.0 * lat +
    0.2 * lat * lat +
    0.1 * lng * lat +
    0.2 * Math.sqrt(Math.abs(lng))
  ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
  ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0
  ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0
  return ret
}

const transformlng = (lng: number, lat: number): number => {
  lat = +lat
  lng = +lng
  let ret: number =
    300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
  ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
  ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0
  ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0
  return ret
}

const out_of_china = (lng: number, lat: number): boolean => {
  lat = +lat
  lng = +lng
  return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
}

export default {
  bd09togcj02,
  gcj02tobd09,
  wgs84togcj02,
  gcj02towgs84
}

5. 要点:

- API输出坐标数据时,需在Pydantic 模型做数据转换


    @field_validator("coordinates", mode="before")
    def parse_coordinates(cls, value: WKBElement):
        return dump_coords(to_shape(value))[0] if value else None

6. 参考:

- 工具
- Javascript 在线转 Typescript: 

Javascript to Typescript converter with ChatGPT | Js2TS.com

- 码讯定位精度选择(定位技术对比)
#UWB蓝牙5.1蓝牙信标Wi-FiRFIDZigbee
精度10-30cm1-5m3-5m 依赖于信标的密度5-15m15cm-1m3-10m
可靠性抗干扰能力强对多路径、障碍物和干扰非常敏感易受遮挡和多径影响对多路径、障碍物和干扰非常敏感不易受影响抗干扰能力弱
覆盖范围50-100m10-20m6-8m40-50m1m60-70m(主要应用一维)
数据通信最高27Mbps最高2Mbps不适用最高1 Gbps不适用20-250kbps
安全范围非常安全可利用中继攻击进行欺骗可利用中继攻击进行欺骗可利用中继攻击进行欺骗可利用中继攻击进行欺骗安全性比较低
定位服务延迟<1ms≤3ms≥3ms≥3ms≥1s30ms
可拓展性基于超过数万个或不限量标签的解决方案几百到一千标签几百到一千标签几百到一千标签不限量标签最大60000个节点

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

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

相关文章

ARM32开发——(二十三)存储器介绍

1. 存储器分类 存储器按其存储介质特性主要分为“易失性存储器”和“非易失性存储器”两大类。 “易失/非易失”是指存储器断电后&#xff0c; 它存储的数据内容是否会丢失的特性。 在计算机中易失性存储器最典型的代表是内存&#xff0c;非易失性存储器的代表则是硬盘。 2.…

互联网全景消息(2)之RabbitMq高阶使用

一、RabbitMQ消息可靠性保障 消息的可靠性投递是使用消息中间件不可避免的问题&#xff0c;不管是Kafka、rocketMQ或者是rabbitMQ&#xff0c;那么在RabbitMQ中如何保障消息的可靠性呢&#xff1f; 首先来看一下rabbitMQ的 架构图&#xff1a; 首先从图里我们可以看到&#xff…

python发现是anaconda的,而不是原来的编译环境

发现有三个python编译器。 可以检查一下环境变量&#xff0c;把原来的python编译器版本上移到anaconda的python编译器之前。这样每次在终端使用python命令就是原来的python编译器版本了

基于Docker搭建Graylog分布式日志采集系统

文章目录 一、简介二、Graylog1、主要特点2、组件3、工作流程介绍4、使用场景 三、Graylog 安装部署1、 安装 docker2、安装docker compose3、 安装graylog4、Graylog控制台 四、springboot集成Graylog 一、简介 Graylog是一个开源的日志管理工具&#xff0c;主要功能包括日志…

c++中的匿名对象及内存管理及模版初阶

c中的匿名对象 A a;//a的生命周期在整个main函数中 a.Sum(1); //匿名对象生命周期只有一行&#xff0c;只有这一行会创建对象,出了这一行就会调析构 A().Sum(1);//只有这一行需要这个对象&#xff0c;其他地方不需要。 return 0; 日期到天数的转换 计算日期到天数转换_牛客…

解读GaussianTalker:利用音频驱动的基于3D高斯点染技术的实时高保真讲话头像合成

单位&#xff1a;首尔大学 项目地址&#xff1a;https://ku-cvlab.github.io/GaussianTalker/ github&#xff1a;https://github.com/KU-CVLAB/gaussiantalker 本文是对GaussianTalker的解读&#xff0c;欢迎大家阅读指正&#xff01; 目录 前言摘要一、背景介绍二 相关工作三…

拼多多Temu半托管和全托管对比

根据东吴证券报告显示&#xff0c;与全托管相比&#xff0c;半托管给予商家更灵活的物流选择&#xff0c;允许商家自行负责仓配物流&#xff1b;与传统3P模式相比&#xff0c;半托管仍保留平台核价、平台负责营销售后客服等特点。 Temu 最开始采用全托管模式&#xff0c;但随着…

【C++ Primer Plus习题】7.6

问题: 解答: #include <iostream> using namespace std;#define SIZE 20int Fill_array(double* arr, int len) {int i 0;for (i 0; i < len; i){cout << "请输入值:";cin >> arr[i];if (cin.fail()){cout << "输入非法数字,结束…

.NET中分布式服务

单体架构 特点&#xff1a; 所有的功能集成在一个项目工程中。所有的功能打在一个安装包。 优点&#xff1a; 项目架构简单。开发效率高。容易打包。 缺点&#xff1a; 全部功能集成在一个工程中&#xff0c;如果要更新&#xff0c; 所有的都要重新发布版本迭代速度逐渐变…

C#笔记4 详细解释事件及其原型、匿名方法和委托的关系

匿名方法 定义 匿名方法允许一个与委托关联的代码被内联的写入使用委托的位置。 语法形式 delegate(参数列表) {代码块 } 前文说过&#xff0c;委托是定义了一个公司&#xff0c;公司专门承接某一类型的任务。 委托的实例化就是公司把任务交给了具体的职员&#xff08;方…

【React】从零开始搭建 react 项目(初始化+路由)

创建 React 项目 创建项目的方式&#xff1a;create-react-app 项目名称 如果没有安装 react 脚手架&#xff0c;请先安装 npm isntall create-react-app -g安装成功后&#xff0c;开始配置项目 配置项目的 icon 和标题 配置 jsconfig.json 目的&#xff1a;用于配置 Java…

Google Earth Engine(GEE)——在 CloudCompare软件 中处理地面激光扫描 (TLS) 数据

背景和目的 本实验的目标是熟悉 3D 点云数据。我们将使用上周在 Boab 法院校园收集的数据。我们使用 Leica BLK360 激光扫描仪收集了多次扫描,今天我们将处理其中的一些扫描。 可以在此处以 .las 格式下载扫描数据(请注意,每个文件约为 1GB):这个是链接 https://www.dro…

批量进行Mysql数据处理的一项工作记录以及保存一个nginx变量大全

一、批量进行Mysql数据处理的一项工作记录 在使用SQL执行一起数据批量处理的时候遇到执行数速度非常慢。表temp_users是一个包含百万级的用户ID表&#xff0c;表user_list是一个亿级的表&#xff0c;因为跨库&#xff0c;这里使用的是federated引擎创建的结构表。根据要实现的目…

FPGA实现SDI视频H265压缩网络推流输出,基于VCU架构,支持12G-SDI 4K60帧,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我这里已有的视频图像编解码方案本博已有的 SDI 编解码方案 3、详细设计方案设计框图FPGA开发板视频输入SDI硬件均衡器LMH1219UHD-SDI GT SDI视频解串SMPTE UHD-SDI RX SUBSYSTEM SDI视频解码Video Frame Buffer WriteZynq UltraS…

大模型时代下,软件检测行业将如何发展?

大模型时代&#xff0c;软件测试面临着前所未有的机遇和挑战&#xff0c;各类产品测试领域将如何应对技术发展和时代变化&#xff1f; 2024年8月28日晚八点&#xff0c;安畅检测首席专家李龙与腾讯Tech Lead茹炳晟、中电金信质量团队负责人王壮做客人民邮电出版社创办的IT专业…

springboot集成guava布隆过滤器

1.创建springboot项目&#xff0c;引入maven依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency>2.创建guava布隆过滤器 Component public class Gua…

DexclassLoader读取dex在Android14上遇到问题

报错如下&#xff1a; 在Android 14&#xff08;API 级别 34&#xff09;及以后版本中&#xff0c;DexClassLoader 被进一步限制&#xff0c;只能用于加载只读文件中的代码。这意味着你不能再使用 DexClassLoader 来加载从应用的内部存储空间中读取的文件。 我想通过JNI来修改…

Elasticsearch 8.13.4 LocalDateTime类型转换问题

框架背景 springboot 3.3.1elasticseach8.13.4spring-data-elasticsearch5.3.1(其实只要用了springboot3.3.1 上下两个的版本都在里面绑死了) 问题描述 使用spring-data-elasticsearch操作es&#xff0c;当字段增加映射注解,其实如果是日期类型&#xff0c;你不加默认也给你…

计算机网络概述(协议层次与服务模型)

目录 1.协议层次 2.服务模型 1.协议层次 层次化方式实现复杂网络功能&#xff1a; 将网络复杂的功能分成明确的层次&#xff0c;每一层实现了其中一个或一组功能&#xff0c;功能中有其上层可以使用的功能&#xff1a;服务本层协议实体相互交互执行本层的协议动作&#xff0…

C++(Qt)-GIS开发-QGraphicsView显示在线瓦片地图

C(Qt)-GIS开发-QGraphicsView显示在线瓦片地图 文章目录 C(Qt)-GIS开发-QGraphicsView显示在线瓦片地图1、概述2、实现效果3、主要代码4、源码地址 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;GIS开发 &#x1f448; 1、概述 支持加载显示在线瓦片…