跪拜 Guibai
← All articles
Frontend · JavaScript · Data Visualization

Cesium Dual-Frustum Gimbal Preview Replaces Guesswork with Visual Aiming for Drone Routes

By 尘世中一位迷途小书童 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Any team building a drone ground station or 3D map tool that needs to preview a camera's view will hit the same frustum-orientation mismatch, event-capture conflict, and async flicker bugs documented here. The single-state, single-render-loop architecture is directly portable to other Cesium projects that synchronize multiple views.

Summary

Configuring a drone gimbal at a waypoint means answering three questions: altitude, pointing direction, and zoom. Three numeric inputs give no feedback on what the camera actually sees, so operators fly, check the photo, and iterate. A Cesium-based tool replaces that loop with a visual editor: a green frustum shows the current zoomed field of view, a yellow frustum shows the wide-angle reference, and a hawk-eye preview window lets users drag to aim. Changing the hawk-eye view updates a central state object, which redraws both frustums and a height pole on the main map in the same animation frame.

The implementation runs as a single HTML file with two Cesium Viewers. A unidirectional data flow — user input mutates state, a batched requestAnimationFrame redraws everything — prevents the drift and mismatch that plagued earlier attempts. Key fixes include forcing synchronous geometry creation to stop frustum flicker during drag, swapping HeadingPitchRoll parameters and applying a -90° X-axis rotation so the frustum points at the ground instead of the sky, and overlaying a transparent div to capture pointer events that Cesium's ScreenSpaceEventHandler would otherwise swallow.

Zoom uses multiplication rather than addition to match perceptual scaling, and the hawk-eye camera's aspectRatio is deliberately left untouched to avoid stretching on the non-16:9 preview panel. Ground distance from a ray-ellipsoid intersection is clamped at 20 km so near-horizontal views don't display absurd 30 km readings. The tool omits full route editing, persistence, and per-drone gimbal limits, keeping it a focused starting point for integrating visual gimbal aiming into a larger ground-station application.

Takeaways
Two Cesium Viewers share one state object; a batched requestAnimationFrame redraws the main map frustums, hawk-eye camera, and HUD together so the views never drift apart.
FrustumGeometry orientation differs from Camera.setView: HeadingPitchRoll parameters must be swapped (roll in second position, pitch in third) and followed by a -90° X-axis quaternion rotation, or the frustum points skyward.
Setting Primitive asynchronous to false prevents frustum flicker during drag, because Cesium otherwise offloads geometry creation to a worker and the rebuilt cone lags behind the pointer.
A transparent overlay div captures pointer events for hawk-eye dragging; binding to Cesium's canvas fails because ScreenSpaceEventHandler intercepts mouse events.
Dragging updates only one axis per frame — yaw or pitch, whichever delta is larger — so diagonal mouse movement doesn't spin both angles at once and make precise aiming impossible.
Zoom is applied with multiplication and division, not addition, because perceived zoom is exponential: 1X→2X and 100X→101X are not equivalent steps.
The hawk-eye camera's aspectRatio must not be overridden; letting it follow the panel's actual dimensions (e.g., 420×300) avoids image stretching.
Ground distance from a ray-ellipsoid intersection is clamped at 20 km; near-horizontal pitch angles produce meaningless 30 km+ readings that should display as '--'.
A yellow wide-angle reference frustum (fixed 28° FOV) sits inside a green current-zoom frustum, giving operators a constant sense of how much the zoom has narrowed the frame.
Conclusions

The core insight is not the 3D math but the interaction design: replacing three numeric inputs with a draggable preview and dual frustums shifts the user's task from mental trigonometry to direct manipulation, which is what DJI's ground station already proved works.

Unidirectional data flow — a single state object mutated by user input and read by all render functions — is the architectural fix that eliminates view-synchronization bugs, and it's a pattern that applies to any multi-view Cesium application.

Many of the documented bugs (frustum pointing skyward, async flicker, event hijacking) stem from Cesium's internal conventions diverging from developer intuition, making this a useful reference for anyone building custom Cesium primitives beyond the standard entity API.

Concepts & terms
View Frustum
A truncated pyramid representing the 3D volume a camera can see. In Cesium, FrustumGeometry draws this as a semi-transparent cone from the camera position, showing exactly what ground area falls within the frame.
HeadingPitchRoll (HPR)
Cesium's orientation triplet: heading is compass direction, pitch is up/down tilt, roll is rotation around the forward axis. FrustumGeometry expects them in a different order than Camera.setView, requiring a parameter swap and an extra -90° X-axis rotation.
Unidirectional Data Flow
A pattern where all user input modifies a single state object, and all views read from that same state during a batched render pass. It prevents the drift that occurs when two viewers independently compute their own camera transforms.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗