| | |
| | | import java.util.ArrayList; |
| | | import zhuye.Shouye; |
| | | import zhuye.MapRenderer; |
| | | import gecaoji.Device; |
| | | import gecaoji.Gecaoji; |
| | | import gecaoji.lujingdraw; |
| | | import publicway.buttonset; |
| | |
| | | return; |
| | | } |
| | | |
| | | // 解析路径坐标 |
| | | pathPoints = lujingdraw.parsePlannedPath(plannedPath); |
| | | if (pathPoints == null || pathPoints.size() < 2) { |
| | | // 1. 解析路径坐标(原始路径) |
| | | List<Point2D.Double> rawPathPoints = lujingdraw.parsePlannedPath(plannedPath); |
| | | if (rawPathPoints == null || rawPathPoints.size() < 2) { |
| | | JOptionPane.showMessageDialog(null, "路径坐标解析失败,无法进行导航预览", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | // 2. 尝试重新生成完整路径段(包含围边和作业路径) |
| | | // 这样可以确保导航预览时,割草机先沿着内缩边界走一圈,再走割草路径 |
| | | List<lujing.Lunjingguihua.PathSegment> segments = null; |
| | | String boundaryCoords = dikuai.getBoundaryCoordinates(); |
| | | String mowingWidth = dikuai.getMowingBladeWidth(); // 注意:这里应该用割草宽度,而不是割刀宽度,通常是一样的 |
| | | // 如果没有割草宽度,尝试从Device获取 |
| | | if (mowingWidth == null || mowingWidth.trim().isEmpty() || "-1".equals(mowingWidth.trim())) { |
| | | Device device = Device.getActiveDevice(); |
| | | if (device != null) { |
| | | mowingWidth = device.getMowingWidth(); |
| | | } |
| | | } |
| | | // 如果还是没有,使用默认值 |
| | | if (mowingWidth == null || mowingWidth.trim().isEmpty() || "-1".equals(mowingWidth.trim())) { |
| | | mowingWidth = "0.34"; |
| | | } |
| | | |
| | | String safetyDistance = dikuai.getMowingSafetyDistance(); |
| | | String obstaclesCoords = dikuai.getObstacleCoordinates(); |
| | | String mowingPattern = dikuai.getMowingPattern(); |
| | | |
| | | if (boundaryCoords != null && !boundaryCoords.trim().isEmpty() && !"-1".equals(boundaryCoords.trim())) { |
| | | try { |
| | | // 解析割草模式 |
| | | String mode = "parallel"; // 默认平行模式 |
| | | if (mowingPattern != null && !mowingPattern.trim().isEmpty()) { |
| | | String pattern = mowingPattern.trim().toLowerCase(); |
| | | if ("1".equals(pattern) || "spiral".equals(pattern) || "螺旋式".equals(pattern) || "螺旋".equals(pattern)) { |
| | | mode = "spiral"; |
| | | } else if ("parallel".equals(pattern) || "平行线".equals(pattern) || "平行".equals(pattern)) { |
| | | mode = "parallel"; |
| | | } |
| | | } |
| | | |
| | | // 调用路径规划生成完整路径段 |
| | | segments = lujing.Lunjingguihua.generatePathSegments( |
| | | boundaryCoords, |
| | | obstaclesCoords != null ? obstaclesCoords : "", |
| | | mowingWidth, |
| | | safetyDistance, |
| | | mode |
| | | ); |
| | | } catch (Exception e) { |
| | | // 如果重新生成失败,segments 为 null |
| | | System.err.println("导航预览重新生成路径失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | // 3. 构建最终导航路径点列表 |
| | | pathPoints = new ArrayList<>(); |
| | | if (segments != null && !segments.isEmpty()) { |
| | | // 如果成功生成了路径段,使用路径段构建点列表 |
| | | // 这样包含了围边路径和作业路径,以及它们之间的连接 |
| | | lujing.Lunjingguihua.PathSegment firstSeg = segments.get(0); |
| | | pathPoints.add(new Point2D.Double(firstSeg.start.x, firstSeg.start.y)); |
| | | |
| | | for (lujing.Lunjingguihua.PathSegment seg : segments) { |
| | | // 添加终点(起点已经在上一次循环或初始化时添加了) |
| | | // 注意:这里假设路径段是连续的,或者我们只关心端点 |
| | | // 如果段之间不连续(有空走),generatePathSegments 应该已经生成了连接段(isMowing=false) |
| | | pathPoints.add(new Point2D.Double(seg.end.x, seg.end.y)); |
| | | } |
| | | } else { |
| | | // 如果生成失败,回退到使用原始解析的路径点 |
| | | // 这通常只包含作业路径,可能没有围边 |
| | | pathPoints = rawPathPoints; |
| | | } |
| | | |
| | | if (pathPoints == null || pathPoints.size() < 2) { |
| | | JOptionPane.showMessageDialog(null, "无法构建有效的导航路径", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | this.currentDikuai = dikuai; |
| | | this.currentPathIndex = 0; |
| | |
| | | return; |
| | | } |
| | | |
| | | Point2D.Double targetPoint = pathPoints.get(currentPathIndex + 1); |
| | | |
| | | // 计算到目标点的距离 |
| | | double dx = targetPoint.x - currentPos.x; |
| | | double dy = targetPoint.y - currentPos.y; |
| | | double distance = Math.hypot(dx, dy); |
| | | |
| | | // 计算每帧移动的距离(米) |
| | | double moveDistance = currentSpeed * (TIMER_INTERVAL_MS / 1000.0); |
| | | |
| | | if (distance <= moveDistance) { |
| | | // 到达目标点,移动到下一个点 |
| | | mower.setPosition(targetPoint.x, targetPoint.y); |
| | | navigationTrack.add(new Point2D.Double(targetPoint.x, targetPoint.y)); |
| | | mapRenderer.addNavigationPreviewTrackPoint(targetPoint); |
| | | // 到达阈值:当距离小于这个值时,认为已到达目标点(0.05米) |
| | | double arrivalThreshold = 0.05; |
| | | |
| | | // 循环处理,直到移动距离用完或到达路径终点 |
| | | double remainingDistance = moveDistance; |
| | | |
| | | while (remainingDistance > 0.001 && currentPathIndex < pathPoints.size() - 1) { |
| | | Point2D.Double targetPoint = pathPoints.get(currentPathIndex + 1); |
| | | |
| | | currentPathIndex++; |
| | | // 计算到目标点的距离 |
| | | double dx = targetPoint.x - currentPos.x; |
| | | double dy = targetPoint.y - currentPos.y; |
| | | double distance = Math.hypot(dx, dy); |
| | | |
| | | // 如果还有下一个点,计算方向 |
| | | if (currentPathIndex < pathPoints.size() - 1) { |
| | | Point2D.Double nextPoint = pathPoints.get(currentPathIndex + 1); |
| | | double heading = calculateHeading(targetPoint, nextPoint); |
| | | setMowerHeading(heading); |
| | | // 如果距离小于到达阈值,认为已到达目标点,移动到下一个点 |
| | | if (distance <= arrivalThreshold) { |
| | | // 到达目标点,移动到精确位置 |
| | | mower.setPosition(targetPoint.x, targetPoint.y); |
| | | navigationTrack.add(new Point2D.Double(targetPoint.x, targetPoint.y)); |
| | | mapRenderer.addNavigationPreviewTrackPoint(targetPoint); |
| | | |
| | | currentPos = new Point2D.Double(targetPoint.x, targetPoint.y); |
| | | currentPathIndex++; |
| | | |
| | | // 如果还有下一个点,更新方向 |
| | | if (currentPathIndex < pathPoints.size() - 1) { |
| | | Point2D.Double nextPoint = pathPoints.get(currentPathIndex + 1); |
| | | double heading = calculateHeading(targetPoint, nextPoint); |
| | | setMowerHeading(heading); |
| | | } |
| | | |
| | | // 继续处理剩余的移动距离 |
| | | continue; |
| | | } |
| | | } else { |
| | | |
| | | // 向目标点移动 |
| | | // 先更新方向,确保车头朝向目标点 |
| | | // 计算本次实际移动的距离(不超过剩余距离和到目标点的距离) |
| | | double actualMoveDistance = Math.min(remainingDistance, distance); |
| | | |
| | | // 更新方向,确保车头朝向目标点 |
| | | double heading = calculateHeading(currentPos, targetPoint); |
| | | setMowerHeading(heading); |
| | | |
| | | double ratio = moveDistance / distance; |
| | | // 计算新位置 |
| | | double ratio = actualMoveDistance / distance; |
| | | double newX = currentPos.x + dx * ratio; |
| | | double newY = currentPos.y + dy * ratio; |
| | | |
| | | mower.setPosition(newX, newY); |
| | | navigationTrack.add(new Point2D.Double(newX, newY)); |
| | | mapRenderer.addNavigationPreviewTrackPoint(new Point2D.Double(newX, newY)); |
| | | |
| | | // 更新当前位置和剩余距离 |
| | | currentPos = new Point2D.Double(newX, newY); |
| | | remainingDistance -= actualMoveDistance; |
| | | |
| | | // 如果已经到达目标点附近(距离小于阈值),在下次循环中会处理 |
| | | // 继续循环处理剩余的移动距离 |
| | | } |
| | | |
| | | // 更新速度显示到地图渲染器 |
| | |
| | | } |
| | | |
| | | |
| | | |