feat(lms): add ROS connection status workspace view
This commit is contained in:
@@ -480,7 +480,7 @@ const SENSOR_POINT_CLOUD_MAX_AGE_MS = 3_000;
|
||||
const ENABLE_CAMERA_PREVIEW = false;
|
||||
const CUSTOM_WORLD = !REAL_RUNTIME && viewerMapKey === 'nav2-custom';
|
||||
const activeWorldModel = viewerMapKey === 'nav2-turtlebot3-house' ? 'turtlebot3_house' : 'turtlebot3_world';
|
||||
const MKMINI_WORLD_SPACING_SCALE = USE_MKMINI_MODEL && activeWorldModel === 'turtlebot3_world' ? 1.6 : 1;
|
||||
const MKMINI_WORLD_SINGLE_PILLAR = USE_MKMINI_MODEL && activeWorldModel === 'turtlebot3_world';
|
||||
const MKMINI_RENDER_LENGTH = 0.84;
|
||||
const MKMINI_RENDER_WIDTH = 0.61;
|
||||
const MKMINI_RENDER_HEIGHT = 0.6575;
|
||||
@@ -1066,14 +1066,10 @@ function applySdfPose(obj, pose) {
|
||||
obj.rotation.set(pose.roll, pose.pitch, pose.yaw, 'XYZ');
|
||||
}
|
||||
function applyWorldSdfPose(obj, pose) {
|
||||
applySdfPose(obj, {
|
||||
...pose,
|
||||
x: pose.x * MKMINI_WORLD_SPACING_SCALE,
|
||||
y: pose.y * MKMINI_WORLD_SPACING_SCALE,
|
||||
});
|
||||
applySdfPose(obj, pose);
|
||||
}
|
||||
function isMkMiniWorldPillar(visual) {
|
||||
return MKMINI_WORLD_SPACING_SCALE > 1
|
||||
return MKMINI_WORLD_SINGLE_PILLAR
|
||||
&& /^(one|two|three)_/.test(visual.getAttribute('name') || '');
|
||||
}
|
||||
function shouldRenderWorldVisual(visual) {
|
||||
@@ -1141,9 +1137,6 @@ async function addMesh(parent, visual, geometry) {
|
||||
if (!uri) return;
|
||||
const url = gazeboUriToPath(uri);
|
||||
const scale = parseScale(getDirectText(meshEl, 'scale', '1 1 1'));
|
||||
if (MKMINI_WORLD_SPACING_SCALE > 1 && uri.endsWith('/wall.dae')) {
|
||||
scale.multiplyScalar(MKMINI_WORLD_SPACING_SCALE);
|
||||
}
|
||||
const material = materialFromVisual(visual);
|
||||
|
||||
if (/\.stl$/i.test(url)) {
|
||||
@@ -1942,7 +1935,19 @@ function updatePointCloud(payload) {
|
||||
// loaded navigation map after localization starts.
|
||||
if (frameId !== 'map' && hasGlobalMapPointCloud) return;
|
||||
updatePointGeometry(pointCloudGeometry, payload?.points);
|
||||
if (frameId === 'map') hasGlobalMapPointCloud = true;
|
||||
if (frameId === 'map') {
|
||||
hasGlobalMapPointCloud = true;
|
||||
// The saved 3D cloud is now available. The occupancy grid was only a
|
||||
// startup fallback while the exported cloud was loading.
|
||||
if (navigationMapMesh && SHOW_POINT_CLOUD) {
|
||||
navigationMapRoot.remove(navigationMapMesh);
|
||||
navigationMapMesh.geometry.dispose();
|
||||
navigationMapMesh.material.dispose();
|
||||
navigationMapMesh = null;
|
||||
navigationMapTexture?.dispose();
|
||||
navigationMapTexture = null;
|
||||
}
|
||||
}
|
||||
const transform = payload?.transform;
|
||||
if (!transform || !Number.isFinite(Number(transform.x)) || !Number.isFinite(Number(transform.y))) {
|
||||
// A cloud already authored in `map` does not need a transform. Hide
|
||||
@@ -2216,7 +2221,10 @@ function drawMiniMap(state) {
|
||||
|
||||
function updateMiniMapMetadata(map) {
|
||||
if (!map?.width || !map?.height) return;
|
||||
if (navigationMapLocked) return;
|
||||
// MAP1 and its metadata are sent as separate WebSocket frames. Accept the
|
||||
// first metadata frame even when its binary payload arrived first; only a
|
||||
// fully rendered saved navigation map is locked against later local maps.
|
||||
if (navigationMapLocked && navigationMapMesh) return;
|
||||
latestMiniMapGrid = {
|
||||
...(latestMiniMapGrid || {}),
|
||||
...map,
|
||||
@@ -2314,9 +2322,10 @@ function updateRemoteNavigationRoomTransform() {
|
||||
|
||||
function renderNavigationOccupancyMap() {
|
||||
const grid = latestMiniMapGrid;
|
||||
// A 3D SLAM session uses RTAB-Map's accumulated point cloud as the scene
|
||||
// itself. Keep the occupancy grid for the minimap only, not as a floor plane.
|
||||
if (SHOW_POINT_CLOUD || NAV2_VIEW_MODE !== 'navigation' || !grid?.data?.length || !grid.width || !grid.height || !grid.resolution) return;
|
||||
if (NAV2_VIEW_MODE !== 'navigation' || !grid?.data?.length || !grid.width || !grid.height || !grid.resolution || !grid.origin) return false;
|
||||
// The occupancy grid is a short-lived fallback for a saved 3D map. It
|
||||
// keeps the scene usable if the point-cloud export is still starting.
|
||||
if (SHOW_POINT_CLOUD && hasGlobalMapPointCloud) return false;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = grid.width;
|
||||
@@ -2379,6 +2388,7 @@ function renderNavigationOccupancyMap() {
|
||||
camera.lookAt(center);
|
||||
navigationMapFocused = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyMapBinary(buffer) {
|
||||
@@ -2410,6 +2420,9 @@ function applyMapBinary(buffer) {
|
||||
data,
|
||||
};
|
||||
miniMapBitmap = null;
|
||||
// Do not lock a selected navigation map before its separate metadata frame
|
||||
// supplies resolution and origin. Otherwise Three.js receives valid /map
|
||||
// data but cannot construct a plane from it.
|
||||
renderNavigationOccupancyMap();
|
||||
drawMiniMap(latestSlamState);
|
||||
downloadMapButton.disabled = false;
|
||||
|
||||
Reference in New Issue
Block a user