三维渲染
BicMap 通过内置的 URDFPlugin 支持在地图上叠加 URDF 格式的机器人三维模型,并提供三维空间标注(3D Marker)能力,适用于数字孪生、机器人可视化等场景。
源码参考
- 核心类:
src/bicMap/core/urdf/— URDFPlugin- 示例:
src/examples/indoor/load3D/index.vue- 示例:
src/examples/indoor/load3dControl/index.vue- 示例:
src/examples/indoor/load3dMarker/index.vue
URDFPlugin — URDF 机器人模型
URDFPlugin 基于 Three.js 实现,在透明 Canvas 上渲染机器人三维模型,并通过屏幕坐标投影与 MapLibre 地图实时同步位置和相机视角。
创建实例
js
import URDFPlugin from '@x-humanoid-cloud/bic-map/urdf'
const urdfPlugin = new URDFPlugin(
container, // HTMLElement — 承载 Three.js 渲染的 DOM 容器
sceneOptions, // null | { background: color } — 传 null 为透明背景
cameraOptions,// { fov, position: { x, y, z } }
controlsOptions, // { controlsFlag: boolean } — false 禁用内置 OrbitControls
pixelRatio // number — 设备像素比,默认 1
)cameraOptions
| 参数 | 类型 | 说明 |
|---|---|---|
fov | number | 相机视角(Field of View),推荐 15–30 |
position.x | number | 相机 X 轴位置 |
position.y | number | 相机 Y 轴位置 |
position.z | number | 相机 Z 轴位置 |
加载 URDF 模型
js
const result = await urdfPlugin.urdfLoad(urdfUrl, packages)
// result.robot — Three.js Object3D,即 URDF 根节点| 参数 | 类型 | 说明 |
|---|---|---|
urdfUrl | string | URDF 文件路径 |
packages | Object | package:// 路径映射,纯 primitive 模型可传 {} |
实例方法
js
// 更新球坐标相机(与地图视角同步)
// azimuth: 方位角(弧度),elevation: 仰角(弧度),distance: 距离(米)
urdfPlugin.updateCameraControl(azimuth, elevation, distance)
// 设置相机是否自动注视机器人身体中心
urdfPlugin.setLookAtRobot(boolean)
// 添加方向光
urdfPlugin.addDirectionalLight(color, intensity, x, y, z)
// 添加环境光
urdfPlugin.addAmbientLight(color, intensity)
// 容器尺寸变化时调用,刷新渲染器
urdfPlugin.onResize()
// 销毁实例,释放 Three.js 资源
urdfPlugin.destroy()与地图视角同步
MapLibre pitch(俯视角度)需转换为 Three.js 球坐标仰角(elevation):
js
// pitch=0(垂直俯视) → elevation=π/2;pitch=90(水平) → elevation=0
function mapPitchToElevation(pitchDeg) {
return (90 - pitchDeg) * Math.PI / 180
}
// 在 map.on('move') 中实时同步
map.on('move', () => {
const el = mapPitchToElevation(map.getPitch())
const az = Math.PI / 2 + map.getBearing() * Math.PI / 180
urdfPlugin.updateCameraControl(az, el, CAM_DISTANCE)
})GPS 坐标 → 屏幕位置
将机器人的经纬度投影到像素坐标,动态更新 Three.js 容器位置,实现模型随地图平移/缩放跟随:
js
function syncRobotScreenPos(lngLat) {
const px = map.project(lngLat) // { x, y } 屏幕像素坐标
urdfLayer.style.left = `${px.x}px`
urdfLayer.style.top = `${px.y}px`
// 容器设置 transform: translate(-50%, -50%) 使中心对齐到坐标点
}
map.on('move', () => syncRobotScreenPos(robotLngLat))完整使用示例
vue
<template>
<div style="position: relative; width: 100%; height: 100vh;">
<div id="mapContainer" style="position: absolute; inset: 0;" />
<!-- Three.js 覆盖层,pointer-events: none 透传鼠标事件 -->
<div
id="urdfLayer"
style="position: absolute; width: 240px; height: 320px;
transform: translate(-50%, -50%); pointer-events: none;"
/>
</div>
</template>
<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
import bicMap from '@x-humanoid-cloud/bic-map'
import URDFPlugin from '@x-humanoid-cloud/bic-map/urdf'
const CAM_DISTANCE = 3
const PITCH_3D = 60
let map, urdfPlugin
function mapPitchToElevation(p) { return (90 - p) * Math.PI / 180 }
onMounted(async () => {
await bicMap.init()
map = bicMap.createMap({ container: 'mapContainer', zoom: 18, pitch: PITCH_3D })
map.on('load', async () => {
// 加载 SLAM 底图
await bicMap.loadSlamMap(map, { /* SLAM 参数 */ })
// 创建 URDFPlugin
const container = document.getElementById('urdfLayer')
urdfPlugin = new URDFPlugin(
container,
null, // 透明背景
{ fov: 20, position: { x: 0, y: 2, z: 3 } },
{ controlsFlag: false }, // 由地图相机驱动
window.devicePixelRatio
)
urdfPlugin.addDirectionalLight(0xffffff, 1.5, 5, 10, 5)
urdfPlugin.addAmbientLight(0xffffff, 0.8)
const { robot } = await urdfPlugin.urdfLoad('/assets/robot.urdf')
robot.rotation.x = -Math.PI / 2 // ROS z-up → Three.js y-up
robot.scale.set(0.2, 0.2, 0.2)
// 与地图同步
map.on('move', () => {
const el = mapPitchToElevation(map.getPitch())
const az = Math.PI / 2 + map.getBearing() * Math.PI / 180
urdfPlugin.updateCameraControl(az, el, CAM_DISTANCE)
const px = map.project([116.4074, 39.9042])
const layer = document.getElementById('urdfLayer')
layer.style.left = `${px.x}px`
layer.style.top = `${px.y}px`
})
})
})
onBeforeUnmount(() => {
map?.off('move')
map?.remove()
urdfPlugin?.destroy()
})
</script>三维空间数据渲染
在室内 SLAM 地图上叠加三维空间标注(语义区域、设备位置、信息面板等),通过 MapLibre fill-extrusion 图层实现带高度的三维要素渲染。
源码参考:
src/examples/indoor/load3dMarker/index.vue、src/examples/indoor/load3dMarker/SpatialInfoPanel.vue
添加三维要素图层
js
// 在 map.on('load') 中添加 fill-extrusion 图层
map.addSource('spatial-data', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [[[lng1,lat1],[lng2,lat2],[lng3,lat3],[lng4,lat4],[lng1,lat1]]]
},
properties: {
height: 3.0, // 三维高度(米)
base: 0, // 底部高度
color: '#3b82f6', // 填充颜色
label: '会议室 A'
}
}
]
}
})
map.addLayer({
id: 'spatial-extrusion',
type: 'fill-extrusion',
source: 'spatial-data',
paint: {
'fill-extrusion-color': ['get', 'color'],
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'base'],
'fill-extrusion-opacity': 0.75
}
})相机倾斜展示三维效果
js
// 加载完成后开启 3D 视角
map.on('load', () => {
map.easeTo({ pitch: 55, bearing: -15, duration: 800 })
})
