当前位置: 首页 > news >正文

Ceisum无人机巡检直播视频投射

接上次的视频投影,Leader告诉我这个视频投影要用在两个地方,一个是我原先写的轨迹回放那里,另一个在无人机起飞后的地图回显,要实时播放无人机拍摄的视频,还要能转镜头,让我把这个也接一下。
我的天!告诉我的时候人都傻了,这是一个功能嘛?
一个是拿到了全部的轨迹数据进行回显,播放的视频也是完整的资源,视频要求投射在地面上。另一个是接收实时的轨迹数据进行回显,播放的是实时的直播,视频居然还要求跟着镜头一起转。
这是两个完完全全不一样的功能好吧!!!
我拿着标书仔仔细细看过那行“演示无人机画面投影到地图上”,一时间陷入了沉默。
无语归无语,但特么还是要写,跟Leader据理力争这不是改改就能换上的功能,然后争取来两周的研发时间。
两周时间看起来很长,其实也就10天,有时候我写个计算方法都要两天,还不一定是最终版,所以只能拜托接下来运气很好,别让我遇到太多阻碍。
开工吧!

Step 0:
思路:最初都是想要拿原来的方法改改看,哪怕不成功,也绝了这个念想。
首先要解决的是视频投放的问题,原本这个投影方法只能投放视频,且只能投影到地面,我要优化成能投影直播并且不接触地面也能投放。
在这里插入图片描述
为了解决直播投放问题,我尝试修改了源码,发现困难重重,主要是两点,一个是原方法不依赖dom元素,这意味着我不能暴力篡改成直播通道,另一个是封装层级过多,我想在某几个关键步骤看下效果都不能被满足,只能盲写看最终效果。
卡在第一步我是万万没有想到,经过一下午的尝试,最终让我放弃了继续下去的想法。

Step0.1:当然,我也没有急着完全放弃之前的代码,我还记得我czml的无人机轨迹方法已经很完善了,从静态路径加载改为动态路径回显似乎并不难,如果成功的话只需要给视频材质连四根线,接着解决朝向转动的问题就完成了,大大减轻了工作量。

在这里插入图片描述
我将原有轨迹数据做成坐标发射器,通过动态接收点位组成路径,每次接收到数据就更新czml加载的点位集合,成功改成了动态路径回显。
然后我尝试将我之前做好的视椎体视频替换进去,然后切换朝向,发现在dataSource.then中格外难操作矩阵,需要多考虑很多问题,于是暂时搁置该方法,等待重启机会。

旧路已死,新步骤开始从0开始一点点搭建功能。

Step 1:
思路: 选择实体结合时间轴,更为灵活地构建动画,同时代码将更繁琐。
首先写一段简单动画,从一个点到另一个点。
在这里插入图片描述

部分代码:

var startPosition = Cesium.Cartesian3.fromDegrees(-75.0, 40.0, 1000.0);
var endPosition = Cesium.Cartesian3.fromDegrees(-75.0, 42.0, 1000.0);// 设置时间范围
var startTime = Cesium.JulianDate.now();
var endTime = Cesium.JulianDate.addSeconds(startTime,15,new Cesium.JulianDate()
);// 创建一个 SampledPositionProperty 来定义路径
var positionProperty = new Cesium.SampledPositionProperty();// 添加时间和位置样本
positionProperty.addSample(startTime, startPosition);
positionProperty.addSample(endTime, endPosition);// 创建一个 entity 并设置其 position 为定义的路径
var entity = viewer.entities.add({position: positionProperty,point: {pixelSize: 10,color: Cesium.Color.RED,},
});// 使视图跟踪 entity
viewer.trackedEntity = entity;// 设置时钟范围
viewer.clock.startTime = startTime.clone();
viewer.clock.stopTime = endTime.clone();
viewer.clock.currentTime = startTime.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 当到达结束时间时停止
viewer.clock.multiplier = 1; // 时间加速倍率
viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
viewer.clock.shouldAnimate = true;

Step 2:
思路:两个点变多个点,写一个点位生成器,模拟实时接收数据,实现多点位连续飞行
在这里插入图片描述

Step 3:
思路:模拟点位飞行,点换成模型,视椎体跟随模型一起,并封装成class,效果很好,但视角连贯性有待提高
在这里插入图片描述
部分代码:

class PointMover {constructor(viewer) {this.viewer = viewer;this.pointQueue = [];this.isAnimating = false;this.videoEntity = null;// 添加模型this.entity = ...// 添加视椎体this.frustumPrimitive = viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: geo,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5)),},}),appearance: new Cesium.PerInstanceColorAppearance({translucent: true,flat: true,}),asynchronous: false,}));// 初始化视频材质this.videoEntity = viewer.entities.add({name: "uav-tmp-fly-wxsimple",polygon: {hierarchy: new Cesium.CallbackProperty((time) => {// 添加安全检查if (!this.entity || !this.entity.position) {return null;}try {const position = this.entity.position.getValue(time);if (!Cesium.defined(position)) {return null;}// 计算视锥体的远截面四个角点// 返回新的多边形层次结构return new Cesium.PolygonHierarchy([upRightPt,upLeftPt,downLeftPt,downRightPt,]);} catch (error) {console.warn("Error calculating polygon positions:", error);return null;}}, false),perPositionHeight: true,material: videoElement,// 添加其他属性以提高渲染性能shadows: Cesium.ShadowMode.DISABLED,classificationType: Cesium.ClassificationType.BOTH,zIndex: 999,},});// 内部方法:启动动画startAnimation() {if (this.pointQueue.length > 2 && !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick = (clock) => {// 移除旧的视锥体// 添加新的视锥体// ...}// 添加 onTick 事件监听器this.viewer.clock.onTick.addEventListener(onTick);}}// 外部调用方法:更新点位并启动动画updatePositionsAndAnimate(newPosition) {this.pointQueue.push(newPosition);if (!this.isAnimating) {this.startAnimation();}}}
}

Step 4:
思路:加入虚线路径
在这里插入图片描述
部分代码:

class PointMover {constructor(viewer) {this.viewer = viewer;this.pointQueue = [];this.isAnimating = false;this.videoEntity = null;this.pathPoints = []; // 用于记录路径点this.pathPolyline = null; // 用于绘制路径// ...}// 绘制路径的方法createPathPolyline() {this.pathPolyline = this.viewer.entities.add({polyline: {positions: new Cesium.CallbackProperty((time) => {// 取当前记录的路径点return this.pathPoints;}, false),material: new Cesium.PolylineDashMaterialProperty({dashLength: 16.0, // 虚线的长度}),width: 3,},});}// 更新路径并添加新的位置updatePath(newPosition) {this.pathPoints.push(newPosition); // 将新位置加入到路径点// 更新虚线的路径if (this.pathPolyline) {this.pathPolyline.polyline.positions = new Cesium.CallbackProperty((time) => {return this.pathPoints;},false);}}// 内部方法:启动动画startAnimation() {if (this.pointQueue.length > 2 && !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick = (clock) => {// 将当前位置添加到路径数组this.updatePath(currentPosition);//...}}}
}

Step 4延伸:
思路:在Step 4基础上尝试扩展,显隐,销毁 show的配合
在这里插入图片描述
部分代码:

class PointMover {constructor(viewer, show) {this.viewer = viewer;this.pointQueue = [];this.isAnimating = false;this.videoEntity = null;this.pathPoints = [];this.pathPolyline = null; this.isFrustumShow = show; //用于显隐控制// ...if (this.frustumPrimitive) {if (!this.isFrustumShow) {this.frustumPrimitive.show = false;} else {this.frustumPrimitive.show = true;}}if (this.videoEntity) {if (!this.isFrustumShow) {this.videoEntity.show = false;} else {this.videoEntity.show = true;}}}// 内部方法:启动动画startAnimation() {if (this.pointQueue.length > 2 && !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick = (clock) => {// 将当前位置添加到路径数组//...this.frustumPrimitive = this.viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: new Cesium.GeometryInstance({geometry: newGeometry,attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5)),},}),appearance: new Cesium.PerInstanceColorAppearance({translucent: true,flat: true,}),asynchronous: false,}));if (this.frustumPrimitive) {if (!this.isFrustumShow) {this.frustumPrimitive.show = false;} else {this.frustumPrimitive.show = true;}}if (this.videoEntity) {if (!this.isFrustumShow) {this.videoEntity.show = false;} else {this.videoEntity.show = true;}}}}}// ...// 外部调用方法,更新显隐开关updateVisibleAndHidden(val) {this.isFrustumShow = val;}dispose() {if (this.frustumPrimitive) {viewer.scene.primitives.remove(this.frustumPrimitive);this.frustumPrimitive = null;}if (this.videoEntity) {viewer.entities.remove(this.videoEntity);this.videoEntity = null;}if (this.entity) {viewer.entities.remove(this.entity);this.entity = null;}if (this.pathPolyline) {viewer.entities.remove(this.pathPolyline);this.pathPolyline = null;}}
}
if (this.pointMover) {clearInterval(this.intervalId);this.intervalId = null;this.pointMover.dispose();this.pointMover = null;
}
this.pointMover = new PointMover(viewer, this.isFrustumShow);

Step 5:
思路:尝试改变视椎体朝向
在这里插入图片描述
部分代码:

class PointMover {constructor(viewer) {// ...this.currentHeading = 0;this.currentPitch = 0;this.currentRoll = 0;// ...}// 内部方法:启动动画startAnimation() {if (this.pointQueue.length > 2 && !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick = (clock) => {const heading = Cesium.Math.toRadians(this.currentHeading); // 偏航角const pitch = Cesium.Math.toRadians(this.currentPitch); // 俯仰角const roll = Cesium.Math.toRadians(this.currentRoll); // 翻滚角// 创建一个HeadingPitchRoll对象const headingPitchRoll = new Cesium.HeadingPitchRoll(heading,pitch,roll);// 创建一个旋转矩阵const rotationMatrix =Cesium.Transforms.headingPitchRollToFixedFrame(currentPosition,headingPitchRoll,Cesium.Ellipsoid.WGS84);// 从旋转矩阵计算四元数const orientation =Cesium.Quaternion.fromRotationMatrix(rotationMatrix);//...}}}// ...updateHeadingPitchRoll(heading, patch, roll) {this.currentHeading = heading;this.currentPitch = patch;this.currentRoll = roll;}
}

Step 5延伸:
思路:在Step 5基础上尝试,同样矩阵转动视频材质
在这里插入图片描述
发现视频材质无法贴合视椎体,方案终止

Step 6:
思路:换用另一种视椎体构建方法,采用相机视角重置视椎体视角的方法,消除地理位置的影响
在这里插入图片描述
部分代码:

class PointMover {constructor(viewer) {// ...this.videoEntity = viewer.entities.add({name: "uav-tmp-fly-wxsimple",polygon: {hierarchy: new Cesium.CallbackProperty((time) => {if (!this.entity || !this.entity.position) {return null;}try {const position = this.entity.position.getValue(time);if (!Cesium.defined(position)) {return null;}let frustum = this.camera.frustum;let Cartesian3 = Cesium.Cartesian3;let camera = this.camera;// ...return new Cesium.PolygonHierarchy([upRightPt,upLeftPt,downLeftPt,downRightPt,]);} catch (error) {console.warn("Error calculating polygon positions:", error);return null;}}, false),perPositionHeight: true,material: new Cesium.ImageMaterialProperty({image: videoElement, // 这里传入视频元素transparent: true, // 设置透明repeat: new Cesium.Cartesian2(1.0, 1.0), // 控制重复}),shadows: Cesium.ShadowMode.DISABLED,classificationType: Cesium.ClassificationType.BOTH,zIndex: 999,},});}// 内部方法:启动动画startAnimation() {if (this.pointQueue.length > 2 && !this.isAnimating) {// 在 onTick 事件处理函数中更新视锥体位置const onTick = (clock) => {var scene = this.viewer.scene;  this.camera = new Cesium.Camera(scene)//...}}}// ...
}

最终
接入实际项目,无人机航拍镜头实时同步反显,完结撒花°˖✧◝(⁰▿⁰)◜✧˖°
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关文章:

Ceisum无人机巡检直播视频投射

接上次的视频投影,Leader告诉我这个视频投影要用在两个地方,一个是我原先写的轨迹回放那里,另一个在无人机起飞后的地图回显,要实时播放无人机拍摄的视频,还要能转镜头,让我把这个也接一下。 我的天&#x…...

【组件库】使用Vue2+AntV X6+ElementUI 实现拖拽配置自定义vue节点

先来看看实现效果: 【组件库】使用 AntV X6 ElementUI 实现拖拽配置自定义 Vue 节点 在现代前端开发中,流程图和可视化编辑器的需求日益增加。AntV X6 是一个强大的图形化框架,支持丰富的图形操作和自定义功能。结合 ElementUI,…...

Vue.js组件开发-如何实现全选反选

在 Vue.js 中实现全选和反选功能,可以通过结合v-model、计算属性和事件处理来完成。 实现思路 • 数据绑定:为每个复选框绑定一个选中状态。 • 全选控制:通过一个复选框控制所有复选框的选中状态。 • 反选控制:通过一个按钮或…...

2025.1.20——四、[强网杯 2019]Upload1 文件上传|反序列化

题目来源:buuctf [强网杯 2019]Upload 1 目录 一、打开靶机,查看信息 二、解题思路 step 1:登陆进去看情况 step 2:大佬来支援——问题在cookie step 3:测试两个思路 1.目录穿越 2.目录扫描 step 4&#xff…...

php代码审计2 piwigo CMS in_array()函数漏洞

php代码审计2 piwigo CMS in_array()函数漏洞 一、目的 本次学习目的是了解in_array()函数和对项目piwigo中关于in_array()函数存在漏洞的一个审计并利用漏洞获得管理员帐号。 二、in_array函数学习 in_array() 函数搜索数组中是否存在指定的值。 in_array($search,$array…...

docker搭建redis集群(三主三从)

本篇文章不包含理论解释,直接开始集群(三主三从)搭建 环境 centos7 docker 26.1.4 redis latest (7.4.2) 服务器搭建以及环境配置 请查看本系列前几篇博客 默认已搭建好三个虚拟机并安装配置好docker 相关博客&#xf…...

[Datawheel]利用Zigent框架编写智能体-1

1.背景知识 1.1 什么是zigent? Zigent 是一个多智能体框架,旨在简化和优化智能体的开发与部署。Zigent 是由 自塾(Zishu.co) 团队开发的一个开源项目。自塾在 2024 年推出了多个开源项目,其中包括 wow-agent&#xf…...

【计算机视觉】人脸识别

一、简介 人脸识别是将图像或者视频帧中的人脸与数据库中的人脸进行对比,判断输入人脸是否与数据库中的某一张人脸匹配,即判断输入人脸是谁或者判断输入人脸是否是数据库中的某个人。 人脸识别属于1:N的比对,输入人脸身份是1&…...

linux环境变量配置文件区别 /etc/profile和~/.bash_profile

在 Linux 系统中,环境变量可以定义用户会话的行为,而这些变量的加载和配置通常涉及多个文件,如 ~/.bash_profile 和 /etc/profile。这些文件的作用和加载时机各有不同。以下是对它们的详细区别和用途的说明: 文章目录 1. 环境变量…...

mac 配置 python 环境变量

最新 mac 电脑,配置原理暂未研究,欢迎答疑 方案一 获取python的安装路径 which python3 配置环境变量 open ~/.bash_profile 末尾添加: PATH"/Library/Frameworks/Python.framework/Versions/3.13/bin:${PATH}" export PATH …...

终极的复杂,是简单

软件仿真拥有最佳的信号可见性和调试灵活性,能够高效捕获很多显而易见的常见错误,被大多数工程师熟练使用。 空间领域应用的一套数据处理系统(Data Handling System),采用抗辐FPGA作为主处理器,片上资源只包含10752个寄存器,软仿也是个挺花时间的事。 Few ms might take …...

软件开发中的密码学(国密算法)

1.软件行业中的加解密 在软件行业中,加解密技术广泛应用于数据保护、通信安全、身份验证等多个领域。加密(Encryption)是将明文数据转换为密文的过程,而解密(Decryption)则是将密文恢复为明文的过程。以下…...

【豆包MarsCode 蛇年编程大作战】蛇形烟花

项目体验地址:项目体验地址 官方活动地址:活动地址 目录 【豆包MarsCode 蛇年编程大作战】蛇形烟花演示 引言 豆包 MarsCode介绍 项目准备 第一步:安装插件 第二步:点击豆包图标来进行使用豆包 使用豆包 MarsCodeAI助手实…...

Jmeter使用Request URL请求接口

简介 在Jmeter调试接口时,有时不清楚后端服务接口的具体路径,可以使用Request URL和cookie来实现接口请求。以下内容以使用cookie鉴权的接口举例。 步骤 ① 登录网站后获取具体的Request URL和cookie信息 通过浏览器获取到Request URL和cookie&#…...

使用Pytest Fixtures来提升TestCase的可读性、高效性

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理,构建成功的基石 在自动化测试工作之前,你应该知道的10条建议 在自动化测试中,重要的不是工具 在编写单元测试时,你是否发现自己有很多重复代码? 数据库设…...

Arduino大师练成手册 -- 读取DHT11

要在 Arduino 上控制 DHT11 温湿度传感器,你可以按照以下步骤进行: 硬件连接: 将 DHT11 的 VCC 引脚连接到 Arduino 的 5V 引脚。 将 DHT11 的 GND 引脚连接到 Arduino 的 GND 引脚。 将 DHT11 的 DATA 引脚连接到 Arduino 的数字引脚&am…...

【Jave全栈】Java与JavaScript比较

文章目录 前言一、Java1、 历史与背景2、语言特点3、应用场景4、生态系统 二、JavaScript1、历史与背景2、语言特点3、应用场景4、 生态系统 三、相同点四、不同点1、语言类型2、用途3、语法和结构4、性能5、生态系统6、开发模式 前言 Java和JavaScript是两种不同的编程语言&a…...

【高项】6.2 定义活动 ITTO

定义活动是识别和记录为完成项目可交付成果而须采取的具体行动的过程。 作用:将工作包分解为进度活动,作为对项目工作进行进度估算、规划、执行、监督和控制的基础 输入 项目管理计划 ① 进度管理计划:定义进度计划方法、滚动式规划的持续…...

openlava/LSF 用户组管理脚本

背景 在openlava运维中经常需要自动化一些常规操作,比如增加用户组以及组成员、删除用户组成员、删除用户组等。而openlava的配置文件需要手动修改,然后再通过badmin reconfig激活配置。因此开发脚本将手工操作自动化就很有必要。 通过将脚本中的User…...

数据结构与算法之贪心: LeetCode 649. Dota2 参议院 (Ts版)

Dota2 参议院 https://leetcode.cn/problems/dota2-senate/ 描述 Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇) Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...