Cesium 无人机航线规划

本文描述了开发者计划开发一个自主的无人机管理平台,模仿大疆司空2的功能,包括实时数据回传、航线编辑、无人机操控等。核心功能包括基于Cesium的三维空间航线编辑和航点控制,以及与无人机的联动测试进展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  

基于上云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,支持机场信息展示以及相关控制

支持空域多模式切换展示

评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值