基于上云api的无人机平台代码及录屏:大疆机场及无人机上云-CSDN博客
====================================================================
以下介绍基于msdk的无人机平台:
鉴于大疆司空平台和大疆无人机app高度绑定,导致很多东西没办法定制化。
从去年的时候就打算仿大疆开发一套完整的平台,包括无人机app以及仿司空2的管理平台,集航线规划、任务派发、实时图像、无人机管理等功能的平台。
当前阶段主要实现了:
1、无人机实时数据(包括视频、音频、拍照数据、位置信息以及无人机姿态)回传至web端,web端实时预览
2、web端实时编辑航线,编辑好的航线直接派发到无人机,无人机按照编辑航线进行作业;
3、......
后续我们还做了喊话、图像自动识别动功能
......
下面是基于Cesium开发的航线编辑功能,后面会对这个这个模块的实现进行记录,供大家参考。
当前我做的是三维空间的航线编辑,还有种方式是快速编辑(此处不做说明)。
简单操作说明:
航点编辑支持两种方式:键盘和鼠标点击。
W -- 向前
S -- 向后
A -- 向左
D -- 向右
并且相机姿态编辑,也支持键盘和鼠标操作:
⬆ -- 镜头抬升
⬇ -- 镜头降低
⬅ -- 镜头左转
➡ -- 镜头右转
具体操作大家可以参考司空2的平台,后续我会将此功能放到线上,大家也可以体验。
后续我写完了,统一放出核心代码。当前仅作记录。
// 根据距离计算位置
computeByDistance(offset) {
// 根据相机的heading 计算前后左右的朝向
let heading = this.startMoveHeading;
const center = Cesium.Matrix4.multiplyByPoint(this.localMtx_inverse, this.localCenter, new Cesium.Cartesian3());
const x = (offset.a.val || 0) - (offset.d.val || 0);
const y = (offset.w.val || 0) - (offset.s.val || 0);
const directionX = Cesium.Cartesian3.multiplyByScalar(new Cesium.Cartesian3(1, 0, 0), -1 * x, new Cesium.Cartesian3());
const directionY = Cesium.Cartesian3.multiplyByScalar(new Cesium.Cartesian3(0, 1, 0), y, new Cesium.Cartesian3());
let newc = Cesium.Cartesian3.add(center, directionX, new Cesium.Cartesian3());
newc = Cesium.Cartesian3.add(newc, directionY, new Cesium.Cartesian3());
const rotationZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(-1 * heading || 0), new Cesium.Matrix3());
newc = Cesium.Matrix3.multiplyByVector(rotationZ, newc.clone(), new Cesium.Cartesian3());
const res = Cesium.Matrix4.multiplyByPoint(this.localMtx, newc.clone(), new Cesium.Cartesian3());
return res;
}
computeHeadingPitch(offset) {
const heading = (offset.ArrowRight.val || 0) - (offset.ArrowLeft.val || 0);
const pitch = (offset.ArrowUp.val || 0) - (offset.ArrowDown.val || 0);
return { heading, pitch }
}
--------------------------------------------- 2024-04-17 更新 ---------------------------------------------
这两天没写什么,主要新增了个方向罗盘
新增了相机范围显示功能,并且同步了相机窗口和主窗口的联动
当前在相机窗口进行鼠标按下拖动时,主窗口的锥体会同步进行运动,以下是核心代码:
// viewer2 绑定鼠标事件,可通过鼠标控制镜头
class ViewerClickHandler {
constructor(viewer, opt) {
this.viewer = viewer;
this.opt = opt || {};
this.startPX = undefined;
this.handler = undefined;
const dom = window.document.getElementById(this.viewer.container.id);
this.width = dom.offsetWidth;
this.height = dom.offsetHeight;
// 禁止viewer的所有操作
this.viewer.scene.screenSpaceCameraController.enableInputs = false;
this.state = 'no'; // change end start
this.bindHandler()
}
bindHandler() {
if (!this.handler) this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((evt) => {
// 初始化进入时,鼠标提示操作
const px = evt.position;
if (!this.startPX) {
this.startPX = px;
}
if (this.opt.start) this.opt.start();
this.state = 'start';
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
this.handler.setInputAction((evt) => {
// 初始化进入时,鼠标提示操作
const px = evt.endPosition;
if (!this.startPX) return;
const heading = (px.x - this.startPX.x) * 30 / this.width;
const pitch = - (px.y - this.startPX.y) * 30 / this.height;
if (this.opt.change) this.opt.change(heading, pitch);
this.state = 'change';
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.handler.setInputAction((evt) => {
// 初始化进入时,鼠标提示操作
if (this.startPX) {
this.startPX = undefined;
}
if (this.opt.end) this.opt.end();
this.state = 'end';
}, Cesium.ScreenSpaceEventType.LEFT_UP);
}
destroy() {
if (this.handler) this.handler.destroy();
this.handler = undefined;
this.state = 'no';
}
}
// frustum和camera同步
const combineCamera = {
isactivate: false,
frustum: undefined,
viewer: undefined,
viewer2: undefined,
activate(frustum, viewer, viewer2) {
if (!this.isactivate) {
this.isactivate = true;
this.frustum = frustum;
this.viewer = viewer;
this.viewer2 = viewer2;
this.bindListener();
}
},
disable() {
if (this.isactivate) {
this.isactivate = false;
this.unbindListener();
if (this.viewerHandler) {
this.viewerHandler.destroy();
this.viewerHandler = undefined;
}
}
},
removeCallback: undefined,
removeCallback2: undefined,
viewerHandler: undefined,
bindListener() {
const camera2 = this.viewer2.camera;
if (!this.viewerHandler) {
let initHeading, initPitch;
this.viewerHandler = new ViewerClickHandler(this.viewer2, {
start: () => {
// 添加初始hp 防止每次都从0度方向开始
initHeading = this.frustum.heading || 0;
initPitch = this.frustum.pitch || 0;
},
change: (heading, pitch) => {
// 修改viewer1中的椎体角度
console.log(heading, this.frustum.heading + (heading || 0));
this.frustum.update('', {
heading: initHeading + heading,
pitch: initPitch + pitch
})
// 修改当前视角
camera2.flyTo({
destination: this.frustum.position,
orientation: {
heading: Cesium.Math.toRadians(initHeading + heading || 0),
pitch: Cesium.Math.toRadians(initPitch + pitch || 0),
roll: 0
},
duration: 0
});
},
end: () => {
initHeading = undefined;
initPitch = undefined;
}
})
}
this.removeCallback = this.viewer.scene.postRender.addEventListener(() => {
if (this.viewerHandler && this.viewerHandler.state == 'change') return; // 操作视角时 不再通过此处来进行椎体同步
// frustum ---> camera
if(!this.frustum) return ;
const heading = this.frustum?.heading;
const pitch = this.frustum?.pitch;
camera2.flyTo({
destination: this.frustum.position,
orientation: {
heading: Cesium.Math.toRadians(heading || 0),
pitch: Cesium.Math.toRadians(pitch || 0),
roll: 0
},
duration: 0
});
}, false)
},
unbindListener() {
if (this.removeCallback) {
this.removeCallback();
this.removeCallback = undefined;
}
}
}
------------------------------------------------- 2024-07-02 日更新-------------------------------------------------
------------------------------------------------ 2024-08-07 ---------------------------------------------------
新增虚拟座舱,支持web端直接控制无人机飞行,支持视频和地图联动
接入大疆机场2,支持机场信息展示以及相关控制
支持空域多模式切换展示