物体运动
在 Cesium 三维GIS项目中,物体运动在仿真模拟,城镇/海洋数字孪生,无人机全景模拟,应急救援等业务场景中比较常见的项目需求。
业务场景
在cesium实际开发中,根据业务需求,一般分为两种情况,以无人机为例:一种是根据无人机的实时飞行同步在cesium场景中,通常还有无人机视频回传,关键帧识别等功能;一种是历史轨迹回放,即无人机飞过的路径再次在cesium场景中显示。
实时点位驱动
这里需要拿到无人机飞行时的接口,或者通过无人机回传时视频进行识别来获取无人机的飞行姿态和位置信息。实现步骤如下:
- 需要通过长链接
websocket实时获取到无人机的飞行姿态,位置信息,监控视频等; - 通过
pinia/vuex建立一个飞行姿态数据缓冲区,用来实时存储websocket回传的数据,根据回传的数据计算出无人机的位置和控制模型朝向的四元数, - 使用
requestAnimationFrame动画帧来驱动无人机不断地更新位置和四元数,以达到无人机实时点位驱动。 伪代码如下:
js
// 1、模拟websocket飞行姿态,位置信息,提交到pinia中
const submitFlightAttitude = (dt) => {
const temp = flightParams.speed / 60 / 60 / 60 / 110
flightParams.longitude += temp * Math.cos(flightParams.heading)
flightParams.latitude -= temp * Math.sin(flightParams.heading)
const { longitude, latitude, height, heading, pitch, roll } = flightParams
if (height <= 120 && height >= 5) {
flightParams.height += temp * Math.sin(pitch) * 110 * 1000 * 10
} else {
if (height > 120) flightParams.height = 120
if (height < 5) flightParams.height = 5
}
const position = cesium.Cartesian3.fromDegrees(longitude, latitude, height)
const hpr = new cesium.HeadingPitchRoll(heading, pitch, roll)
const orientation = cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
)
droneStore.setDroneFilghtParams({ position, orientation })
}
// 2、这里未做缓冲区处理,直接存储当前的无人机信息
import { reactive } from 'vue'
const droneFlight = reactive({
position: null,
orientation: null
})
export const useDroneFilght = () => {
const setDroneFilghtParams = (val) => {
droneFlight.position = val.position
droneFlight.orientation = val.orientation
}
return {
droneFlight,
setDroneFilghtParams
};
}
export const droneFilghtStoreHook = () => {
return useDroneFilght();
}
// 3、动画帧驱动无人机位置和姿态更新
const render = () => {
model.position = droneStore.droneFlight.position
model.orientation = droneStore.droneFlight.orientation
requestAnimationFrameId = requestAnimationFrame(render)
}
时序轨迹回放
首先轨迹回放意味着飞行的数据已经存储在数据库中了,包括具体的飞行时间,飞行路径的关键点等,在cesium中有很多方式可以实现,包括callbackProperty,SampledPositionProperty、requestAnimationFrame等来实现。这里使用SampledPositionProperty以时间进行插值来实现。实现步骤如下:
- 打开cesium初始时timeline部件,并使用css样式进行隐藏,以免影响到整体页面的美观。
- 以飞行时间来初始化开始时刻和结束时刻,并将该时间段赋值给时钟,或者以实际开始时刻和结束时刻进行赋值,设置时间倍率,来控制演示时间,最后将时钟聚焦到该飞行时间。
- 通过
SampledPositionProperty使用时间进行计算出无人机飞行的路径。 - 时间计算出的路径,使用
VelocityOrientationProperty自动计算出飞行姿态 伪代码如下:
js
// 1、初始化时间轴
timeline: true, // 时间轴控件(底部时间进度条)
viewer.timeline.container.style.display = "none";
// 2、时间初始化赋值
const startTime = cesium.JulianDate.fromDate(new Date());
const stopTime = cesium.JulianDate.addSeconds(startTime, TOTAL_FLIGHT_SECONDS, new cesium.JulianDate());
viewer.clock.startTime = startTime.clone();
viewer.clock.stopTime = stopTime.clone();
viewer.clock.multiplier = 1;
viewer.clock.clockRange = cesium.ClockRange.LOOP_STOP;
viewer.timeline.zoomTo(startTime, stopTime);
// 3、时间插值计算
const interval = TOTAL_FLIGHT_SECONDS / (path.length - 1);
let positionProperty = new cesium.SampledPositionProperty();
for (let i = 0; i < path.length; i++) {
const time = cesium.JulianDate.addSeconds(startTime, i * interval, new cesium.JulianDate());
const position = cesium.Cartesian3.fromDegrees(path[i].longitude, path[i].latitude, path[i].height);
positionProperty.addSample(time, position);
}
positionProperty.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: cesium.HermitePolynomialApproximation
});
return positionProperty;
}
// 4、模型加载,自动计算飞行姿态
model = viewer.entities.add({
name: 'drone',
availability: new cesium.TimeIntervalCollection([
new cesium.TimeInterval({
start: startTime,
stop: stopTime,
}),
]),
orientation: new cesium.VelocityOrientationProperty(position),
position: position,
model: {
uri: url,
minimumPixelSize: 36,
maximumScale: 128,
runAnimations: true,
},
})
总结:
本文主要介绍了 Cesium 三维场景中物体运动的两种实现方式。实时点位驱动方式通过 WebSocket 实时获取无人机姿态与位置数据,结合状态管理和动画帧实时更新模型位置与四元数,实现实时飞行态势展示。时序轨迹回放基于历史飞行数据,利用 Cesium 时间轴与插值属性生成平滑飞行路径,并自动匹配运动姿态,完成无人机历史轨迹的仿真回放。
实现效果:
