Leaflet室内导航实战:如何用PathFinding.js避开障碍物规划最优路线

news2026/3/13 17:23:08
Leaflet室内导航实战用PathFinding.js构建高精度避障路径规划系统你是否曾在一个大型购物中心里迷失方向或者在医院复杂的科室走廊中来回打转对于开发者而言构建一个能精准应对这些室内复杂环境的导航系统远比处理开阔的室外道路更具挑战。室内空间充满了动态的障碍物——可能是临时摆放的展柜、维修中的区域或是固定的墙体与立柱。传统的“两点之间直线最短”思维在这里完全失效我们需要的是能够像人一样思考、绕开障碍、寻找最优通路的智能算法。本文将带你深入Leaflet与PathFinding.js的结合实战从零构建一个适用于商场、医院、机场、仓库等场景的室内导航核心引擎。我们不仅会详细拆解A*寻路算法的集成步骤更会探讨如何将抽象的网格坐标与真实的地理位置进行高精度转换处理复杂的多边形障碍物并最终呈现一条清晰、流畅且可交互的导航路径。无论你是希望为现有项目添加室内导航功能的前端开发者还是对地理信息系统GIS和路径规划算法充满兴趣的技术爱好者这篇实战指南都将提供一套可直接落地的解决方案。1. 环境搭建与核心概念解析在开始编码之前我们需要厘清几个关键概念。Leaflet是一个轻量级、开源的JavaScript库用于在Web上创建交互式地图。它擅长处理瓦片图层、标记、弹窗等地理信息的可视化。然而Leaflet本身并不提供路径规划算法尤其是在处理非道路的、自定义的障碍物环境时显得力不从心。这时PathFinding.js登场了。它是一个纯JavaScript实现的寻路算法库封装了A*、Dijkstra、最佳优先搜索Best-First Search等多种经典算法。其核心思想是将地图抽象为一个二维网格Grid每个网格单元Cell被标记为“可通过”或“不可通过”障碍物。寻路算法就在这个网格世界中寻找从起点到终点的最短可行路径。那么如何将Leaflet的经纬度世界与PathFinding.js的网格世界连接起来这是整个项目的技术枢纽。我们需要建立一个映射函数实现两种坐标系的相互转换。首先通过npm或yarn安装必要的依赖npm install leaflet pathfinding # 或者 yarn add leaflet pathfinding接下来创建一个基础的HTML文件并引入相关资源。注意PathFinding.js本身不包含可视化样式我们需要引入其官方提供的可视化CSS可选用于调试网格。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title室内导航演示/title link relstylesheet hrefhttps://unpkg.com/leaflet1.9.4/dist/leaflet.css / link relstylesheet hrefhttps://cdn.jsdelivr.net/npm/pathfinding0.4.18/visual/css/style.css / style #map { height: 600px; } /style /head body div idmap/div script srchttps://unpkg.com/leaflet1.9.4/dist/leaflet.js/script script srchttps://cdn.jsdelivr.net/npm/pathfinding0.4.18/pathfinding-browser.min.js/script script src./app.js/script !-- 我们的主逻辑文件 -- /body /html提示在生产环境中建议将CDN链接替换为本地已安装的node_modules中的文件路径或使用构建工具进行打包以获得更好的稳定性和加载速度。2. 构建网格地图与障碍物系统一切就从初始化一张Leaflet地图开始。为了模拟室内环境我们通常不使用真实的地理底图如OpenStreetMap而是使用一张自定义的室内平面图作为图层。这可以是一张PNG或JPEG图片通过L.imageOverlay方法加载。// app.js // 1. 初始化地图视口中心可以设为(0,0)因为我们使用自定义坐标 var map L.map(map).setView([0, 0], 18); // 2. 定义室内平面图的边界左上角经纬度右下角经纬度 var imageBounds [[-100, -100], [100, 100]]; L.imageOverlay(path/to/your/floor-plan.jpg, imageBounds).addTo(map); // 设置地图的最大边界防止用户拖出平面图范围 map.setMaxBounds(imageBounds);现在我们进入核心环节创建PathFinding.js的网格并设置障碍物。网格的大小gridSize决定了寻路的精度和性能。网格越密路径越精细但计算量也越大。对于大多数室内场景一个60x60或100x100的网格通常已足够。// 3. 定义网格参数 const GRID_WIDTH 60; const GRID_HEIGHT 60; // 初始化一个所有格子均可通行的网格 const grid new PF.Grid(GRID_WIDTH, GRID_HEIGHT); // 4. 定义障碍物这里用多边形区域表示 // 每个障碍物是一个由多个[lng, lat]点组成的数组 const obstacles [ [ // 障碍物1一个矩形区域 [-30, -20], [-10, -20], [-10, -40], [-30, -40] ], [ // 障碍物2一个三角形区域 [20, 10], [40, 10], [30, 30] ], // ... 可以添加更多障碍物 ]; // 5. 将障碍物区域绘制到Leaflet地图上可视化 obstacles.forEach(coords { L.polygon(coords, {color: red, fillOpacity: 0.5}).addTo(map) .bindPopup(障碍区域); }); // 6. 关键步骤将障碍物映射到网格 // 我们需要一个函数将经纬度坐标转换为网格的行列索引 function latLngToGrid(lat, lng) { // 假设我们的地图边界是 imageBounds: [[-100, -100], [100, 100]] // 即经度(lng)和纬度(lat)范围都是[-100, 100] const bounds imageBounds; const minLng bounds[0][1]; // -100 const maxLng bounds[1][1]; // 100 const minLat bounds[0][0]; // -100 const maxLat bounds[1][0]; // 100 // 将经纬度线性映射到[0, GRID_WIDTH-1]和[0, GRID_HEIGHT-1]的整数 // 注意Lat对应Y轴行Lng对应X轴列 const x Math.floor(((lng - minLng) / (maxLng - minLng)) * (GRID_WIDTH - 1)); const y Math.floor(((lat - minLat) / (maxLat - minLat)) * (GRID_HEIGHT - 1)); // 确保索引在网格范围内 return [ Math.max(0, Math.min(GRID_WIDTH - 1, x)), Math.max(0, Math.min(GRID_HEIGHT - 1, y)) ]; }有了转换函数我们就可以遍历每个障碍物多边形的所有顶点并将这些顶点所在的网格单元标记为“不可通过”。这里有一个简化处理我们只标记了顶点所在的格子。对于更精确的障碍物表示你需要判断多边形的每条边穿过了哪些网格单元并将其全部标记。可以使用射线扫描法或网格填充算法来实现但这会显著增加计算复杂度。对于大多数应用标记顶点加上一个小的安全缓冲区如将顶点周围一圈格子也标记是性价比很高的做法。// 7. 标记障碍网格简化版标记顶点所在格 obstacles.forEach(polygon { polygon.forEach(point { const [lng, lat] point; // 注意我们的坐标数组是[lng, lat] const [gridX, gridY] latLngToGrid(lat, lng); grid.setWalkableAt(gridX, gridY, false); // 可选标记顶点周围一圈格子增加障碍物“厚度” for (let dx -1; dx 1; dx) { for (let dy -1; dy 1; dy) { const nx gridX dx; const ny gridY dy; if (nx 0 nx GRID_WIDTH ny 0 ny GRID_HEIGHT) { grid.setWalkableAt(nx, ny, false); } } } }); });3. 实现A*寻路与路径可视化网格和障碍物准备就绪后我们就可以初始化寻路器并计算路径了。PathFinding.js支持多种算法A*算法在大多数情况下是性能与效果的最佳平衡。// 8. 初始化A*寻路器 // 可配置参数是否允许对角线移动启发函数权重等 const finder new PF.AStarFinder({ allowDiagonal: true, // 允许斜向移动 dontCrossCorners: true, // 但不穿过障碍物的角落更真实 heuristic: PF.Heuristic.euclidean // 使用欧几里得距离作为启发函数 }); // 9. 定义起点和终点经纬度坐标 const startLatLng [ -60, -60 ]; // [lat, lng] const endLatLng [ 50, 50 ]; // 在地图上添加标记 const startMarker L.marker(startLatLng).addTo(map).bindPopup(起点); const endMarker L.marker(endLatLng).addTo(map).bindPopup(终点); // 10. 坐标转换并寻路 const startGrid latLngToGrid(startLatLng[0], startLatLng[1]); const endGrid latLngToGrid(endLatLng[0], endLatLng[1]); console.log(起点网格: (${startGrid[0]}, ${startGrid[1]})); console.log(终点网格: (${endGrid[0]}, ${endGrid[1]})); // 执行寻路算法 const pathGrid finder.findPath(startGrid[0], startGrid[1], endGrid[0], endGrid[1], grid.clone()); // 11. 将网格路径转换回经纬度坐标 function gridToLatLng(gridX, gridY) { const bounds imageBounds; const minLng bounds[0][1]; const maxLng bounds[1][1]; const minLat bounds[0][0]; const maxLat bounds[1][0]; const lng minLng (gridX / (GRID_WIDTH - 1)) * (maxLng - minLng); const lat minLat (gridY / (GRID_HEIGHT - 1)) * (maxLat - minLat); return [lat, lng]; // 返回Leaflet标准的[lat, lng]数组 } const pathLatLng pathGrid.map(point gridToLatLng(point[0], point[1])); // 12. 在Leaflet地图上绘制路径 if (pathGrid pathGrid.length 0) { const pathPolyline L.polyline(pathLatLng, { color: #3388ff, weight: 5, opacity: 0.7, lineJoin: round }).addTo(map); // 添加一个平滑的动画箭头指示方向 const movingMarker L.Marker.movingMarker(pathLatLng, [10000], { autostart: true, icon: L.divIcon({className: moving-arrow, html: ➤}) }).addTo(map); } else { console.warn(未找到可行路径起点或终点可能在障碍物内或障碍物完全阻断了通路。); }注意grid.clone()非常重要。PathFinding.js的findPath方法会修改传入的网格对象例如可能会标记已访问的节点。如果你计划多次寻路例如用户动态改变终点必须每次都使用原始网格的克隆体否则后续寻路可能会因为网格状态被污染而失败。为了让路径更直观我们可以添加一些交互功能。例如允许用户点击地图设置新的起点或终点并实时重新计算路径。// 13. 交互式路径规划 let currentStart startLatLng; let currentEnd endLatLng; // 点击地图设置新起点 map.on(click, function(e) { const clickedLatLng e.latlng; // 简单示例交替设置起点和终点 if (!L.Marker) return; if (startMarker) map.removeLayer(startMarker); if (endMarker) map.removeLayer(endMarker); if (pathPolyline) map.removeLayer(pathPolyline); if (movingMarker) map.removeLayer(movingMarker); // 假设这次点击设为新终点原终点变为新起点 currentStart currentEnd; currentEnd [clickedLatLng.lat, clickedLatLng.lng]; // 更新标记 startMarker L.marker(currentStart).addTo(map).bindPopup(起点); endMarker L.marker(currentEnd).addTo(map).bindPopup(终点); // 重新计算并绘制路径 const newStartGrid latLngToGrid(currentStart[0], currentStart[1]); const newEndGrid latLngToGrid(currentEnd[0], currentEnd[1]); const newPathGrid finder.findPath(newStartGrid[0], newStartGrid[1], newEndGrid[0], newEndGrid[1], grid.clone()); if (newPathGrid newPathGrid.length 0) { const newPathLatLng newPathGrid.map(p gridToLatLng(p[0], p[1])); pathPolyline L.polyline(newPathLatLng, {color: #3388ff, weight: 5}).addTo(map); // 可以在这里重新创建动画箭头... } });4. 性能优化与高级技巧当室内地图面积很大或网格精度要求很高时一个巨大的网格如500x500可能会带来性能问题。此外A*算法在寻找长距离路径时开放列表OpenList的维护成本会变高。以下是一些优化策略4.1 分层路径规划Hierarchical Pathfinding不要在整个地图上使用一个高精度网格。可以创建两个层级的网格粗网格层用于快速规划大致的路径方向。精网格层在粗网格路径的每个路段内使用高精度网格进行局部精细规划。// 伪代码示例 const coarseGrid new PF.Grid(20, 20); // 粗网格 const fineGrid new PF.Grid(100, 100); // 精网格 // 1. 在粗网格上规划全局路径 const coarsePath finder.findPath(startGridCoarse, endGridCoarse, coarseGrid); // 2. 对粗路径的每一段在对应的精网格区域进行精细规划 for (let i 0; i coarsePath.length - 1; i) { const segmentStart coarsePath[i]; const segmentEnd coarsePath[i 1]; // 计算segmentStart和segmentEnd对应的精网格区域边界 // 在该边界内初始化一个局部的fineGrid // 进行精细寻路并将结果拼接起来 }4.2 使用更高效的启发函数和数据结构A*算法的性能很大程度上取决于启发函数h(n)的选择。对于允许对角线移动的网格**切比雪夫距离Chebyshev distance或欧几里得距离Euclidean distance**通常比曼哈顿距离更合适。PathFinding.js内置了多种启发函数。此外可以使用**二叉堆Binary Heap**来优化开放列表的优先级队列操作。幸运的是PathFinding.js的默认实现已经考虑了性能优化。4.3 动态障碍物与路径重规划在真实场景中障碍物可能移动或临时出现如清洁车、临时围栏。我们需要支持动态更新网格并重新规划路径。// 假设我们检测到一个新的动态障碍物区域 const dynamicObstacle [[10, 10], [15, 10], [15, 15], [10, 15]]; // 1. 更新网格 dynamicObstacle.forEach(point { const [gridX, gridY] latLngToGrid(point[1], point[0]); // 注意坐标顺序 grid.setWalkableAt(gridX, gridY, false); }); // 2. 立即重新规划当前路径 const newPath finder.findPath( latLngToGrid(currentStart[0], currentStart[1]), latLngToGrid(currentEnd[0], currentEnd[1]), grid.clone() // 使用更新后的网格 ); // 3. 平滑路径输出 // A*在网格上寻路产生的路径通常是“锯齿状”的因为移动被限制在网格的八个方向上。 // 可以使用路径平滑算法如拉直拐角或应用贝塞尔曲线。 function smoothPath(rawPathLatLng) { // 简单的拐角拉直算法移除共线的中间点 const smoothed []; for (let i 0; i rawPathLatLng.length; i) { if (i 0 || i rawPathLatLng.length - 1) { smoothed.push(rawPathLatLng[i]); continue; } const prev rawPathLatLng[i - 1]; const curr rawPathLatLng[i]; const next rawPathLatLng[i 1]; // 计算向量判断三点是否近似共线 const vec1 [curr[0] - prev[0], curr[1] - prev[1]]; const vec2 [next[0] - curr[0], next[1] - curr[1]]; // 计算叉积的模如果很小则说明共线 const cross Math.abs(vec1[0] * vec2[1] - vec1[1] * vec2[0]); if (cross 1e-9) { // 阈值可根据实际情况调整 smoothed.push(curr); // 不共线保留该点 } // 否则跳过这个中间点 } return smoothed; }4.4 不同算法的选择对比PathFinding.js提供了多种算法适用于不同场景算法特点适用场景A*最常用启发式搜索通常能快速找到最短路径绝大多数静态环境寻路Dijkstra保证找到最短路径但比A*慢因为它探索所有方向当启发函数难以定义或权重复杂时Best-First Search速度最快但不保证路径最短可能找到次优解对实时性要求极高对路径长度不敏感Breadth-First Search (BFS)层层扩展保证找到路径如果存在无权图或需要最少转弯次数的场景Jump Point Search (JPS)A*的优化在均匀网格上跳过大量节点速度极快大型、开放的网格地图选择算法时可以在初始化时指定const finderAStar new PF.AStarFinder(); const finderDijkstra new PF.DijkstraFinder(); const finderBFS new PF.BreadthFirstFinder(); // 注意JPS需要额外的初始化参数 const finderJPS new PF.JumpPointFinder({allowDiagonal: true});5. 实战案例商场导航系统集成让我们将这些技术整合到一个更贴近实际的案例中一个商场室内导航系统。该系统需要处理多层楼、电梯/楼梯连接点、以及店铺兴趣点POI。5.1 处理多层结构对于多层建筑我们需要为每个楼层创建独立的网格和地图。在用户切换楼层时动态加载对应楼层的网格数据、障碍物和POI。// 楼层数据示例 const floorData { F1: { grid: null, // 延迟初始化 obstacles: [...], pois: [...], imageBounds: [...], mapImage: floor1.jpg }, F2: {...}, // ... }; let currentFloor F1; // 切换楼层函数 function switchFloor(floorId) { if (!floorData[floorId]) return; currentFloor floorId; const floor floorData[floorId]; // 清除当前地图上的所有图层保留底图 map.eachLayer(layer { if (layer instanceof L.ImageOverlay) return; // 保留底图 map.removeLayer(layer); }); // 加载新楼层的底图 L.imageOverlay(floor.mapImage, floor.imageBounds).addTo(map); map.setMaxBounds(floor.imageBounds); // 初始化或加载该楼层的网格 if (!floor.grid) { floor.grid createGridForFloor(floor); } // 绘制该楼层的障碍物和POI renderFloorObstacles(floor.obstacles); renderFloorPois(floor.pois); // 如果存在跨楼层的路径需要在这里处理连接点如电梯 }5.2 集成兴趣点与路径指引路径规划不仅要避开障碍还要能引导用户到具体的店铺门口。我们可以将每个店铺的入口坐标作为一个POI并作为路径的终点。// POI数据结构 const poiList [ { id: store_001, name: 星巴克, category: 餐饮, entrance: [35, -25], floor: F1 }, { id: store_002, name: 优衣库, category: 服装, entrance: [-15, 40], floor: F1 }, { id: elevator_1, name: 中央电梯, category: 设施, location: [0, 0], floor: F1, connectsTo: [F2] }, // ... ]; // 当用户点击或搜索POI时 function navigateToPoi(poiId) { const poi poiList.find(p p.id poiId); if (!poi) return; // 如果POI在不同楼层先规划到楼层连接点电梯/楼梯的路径 if (poi.floor ! currentFloor) { // 1. 找到当前楼层通往目标楼层的连接点 const connector findConnector(currentFloor, poi.floor); // 2. 规划到连接点的路径 planPath(currentUserLocation, connector.location); // 3. 提示用户“请乘坐电梯至X楼” showInstruction(请前往${connector.name}并到达${poi.floor}); // 4. 用户切换楼层后自动规划从连接点到POI的路径 // 这通常需要与楼层切换函数联动 } else { // 同楼层直接规划路径 planPath(currentUserLocation, poi.entrance); // 在路径终点添加一个弹出信息框 L.popup() .setLatLng(poi.entrance) .setContent(strong目的地${poi.name}/strongbr您已到达) .openOn(map); } }5.3 路径指引与语音提示一条静态的折线还不够我们需要提供分段的转向提示。// 生成转向指令 function generateTurnInstructions(pathLatLngs) { const instructions []; for (let i 1; i pathLatLngs.length - 1; i) { const prev pathLatLngs[i - 1]; const curr pathLatLngs[i]; const next pathLatLngs[i 1]; const bearingIn calculateBearing(prev, curr); // 进入当前点的方向 const bearingOut calculateBearing(curr, next); // 离开当前点的方向 const angleDiff ((bearingOut - bearingIn 540) % 360) - 180; // 归一化到[-180, 180] let instruction; if (Math.abs(angleDiff) 10) { instruction 直行; } else if (angleDiff 10 angleDiff 45) { instruction 稍向右转; } else if (angleDiff 45 angleDiff 135) { instruction 右转; } else if (angleDiff 135) { instruction 掉头; } else if (angleDiff -10 angleDiff -45) { instruction 稍向左转; } else if (angleDiff -45 angleDiff -135) { instruction 左转; } else { instruction 直行; } // 估算到达下一个关键点的大致距离基于网格单元 const distance calculateDistance(curr, next); instructions.push({ point: curr, text: ${instruction}前行约${Math.round(distance)}米, distance: distance }); } return instructions; } // 在地图上以标记形式显示关键转向点 instructions.forEach((inst, idx) { L.marker(inst.point, { icon: L.divIcon({ className: turn-icon, html: div classturn-number${idx 1}/div }) }).addTo(map) .bindPopup(strong第${idx 1}步/strongbr${inst.text}); });在实际部署中我遇到过网格精度与性能的权衡问题。一个200x200的网格在手机浏览器上计算一条复杂路径可能会有轻微卡顿尤其是在低端设备上。我的经验是对于大多数室内场景将网格精度控制在100x100以内并结合分层寻路的思想既能保证路径的合理性和平滑度又能确保交互的流畅性。另外将PathFinding.js的寻路计算放入Web Worker中避免阻塞UI线程也是一个提升用户体验的立竿见影的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2408294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…