| | |
| | | import zhuye.Shouye; |
| | | import zhuye.MapRenderer; |
| | | import gecaoji.Gecaoji; |
| | | import gecaoji.Device; |
| | | 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; |
| | | } |
| | | |
| | | // 3. 构建最终导航路径点列表 |
| | | // 直接使用解析出来的路径点 |
| | | pathPoints = rawPathPoints; |
| | | |
| | | if (pathPoints == null || pathPoints.size() < 2) { |
| | | JOptionPane.showMessageDialog(null, "无法构建有效的导航路径", "错误", JOptionPane.ERROR_MESSAGE); |
| | | return; |
| | | } |
| | | |
| | | this.currentDikuai = dikuai; |
| | | this.currentPathIndex = 0; |
| | | this.currentSpeed = DEFAULT_SPEED; |
| | | // 将当前速度(米/秒)转换为km/h写入设备yaw属性 |
| | | try { |
| | | double kmh = this.currentSpeed * 3.6; |
| | | Device.getActiveDevice().setYaw(String.valueOf(kmh)); |
| | | } catch (Exception ignore) { } |
| | | this.navigationTrack.clear(); |
| | | |
| | | // 获取首页和地图渲染器 |
| | |
| | | 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; |
| | | |
| | | // 如果已经到达目标点附近(距离小于阈值),在下次循环中会处理 |
| | | // 继续循环处理剩余的移动距离 |
| | | } |
| | | |
| | | // 更新速度显示到地图渲染器 |
| | |
| | | |
| | | /** |
| | | * 计算两点之间的方向角(度) |
| | | * 图标默认朝上,向右旋转90度车头朝右 |
| | | * atan2返回的角度:向右是0度,向上是90度 |
| | | * 需要转换为图标旋转角度:向右需要90度,向上需要0度 |
| | | * 车辆图标的车头默认是在屏幕正下方(270度) |
| | | * atan2返回的角度:向右是0度,向上是90度,向左是180度,向下是-90度(270度) |
| | | * 需要转换为图标旋转角度,使车头朝向行驶方向 |
| | | */ |
| | | private double calculateHeading(Point2D.Double from, Point2D.Double to) { |
| | | double dx = to.x - from.x; |
| | |
| | | atan2Angle += 360; |
| | | } |
| | | |
| | | // 图标默认朝上(0度),向右旋转90度车头朝右 |
| | | // 所以:运动方向向右(0度)→ 需要旋转90度 |
| | | // 运动方向向上(90度)→ 需要旋转0度 |
| | | // 运动方向向左(180度)→ 需要旋转270度 |
| | | // 运动方向向下(270度)→ 需要旋转180度 |
| | | // 公式:heading = (90 - atan2Angle + 360) % 360 |
| | | double heading = (90.0 - atan2Angle + 360.0) % 360.0; |
| | | // 图标默认朝下(270度),需要计算旋转角度使车头朝向行驶方向 |
| | | // 如果运动方向向上(90度)→ 需要旋转180度(270 - 90 = 180) |
| | | // 如果运动方向向右(0度)→ 需要旋转270度(270 - 0 = 270) |
| | | // 如果运动方向向左(180度)→ 需要旋转90度(270 - 180 = 90) |
| | | // 如果运动方向向下(270度)→ 需要旋转0度(270 - 270 = 0) |
| | | // 公式:heading = (270 - atan2Angle + 360) % 360 |
| | | double heading = (270.0 - atan2Angle + 360.0) % 360.0; |
| | | |
| | | return heading; |
| | | } |
| | |
| | | */ |
| | | private void speedUp() { |
| | | currentSpeed += SPEED_MULTIPLIER; |
| | | // 同步到设备yaw属性(km/h) |
| | | try { |
| | | double kmh = currentSpeed * 3.6; |
| | | Device.getActiveDevice().setYaw(String.valueOf(kmh)); |
| | | } catch (Exception ignore) { } |
| | | updateSpeedDisplay(); |
| | | } |
| | | |
| | |
| | | currentSpeed = 0.1; |
| | | } |
| | | } |
| | | // 同步到设备yaw属性(km/h) |
| | | try { |
| | | double kmh = currentSpeed * 3.6; |
| | | Device.getActiveDevice().setYaw(String.valueOf(kmh)); |
| | | } catch (Exception ignore) { } |
| | | updateSpeedDisplay(); |
| | | } |
| | | |
| | |
| | | * 更新速度显示 |
| | | */ |
| | | private void updateSpeedDisplay() { |
| | | // 可以在地图上显示当前速度 |
| | | // 这里暂时不实现,如果需要可以在MapRenderer中添加速度显示 |
| | | // 导航预览下在割草机图标上方实时显示速度(固定像素间距) |
| | | if (mapRenderer != null) { |
| | | mapRenderer.setNavigationPreviewSpeed(currentSpeed); |
| | | mapRenderer.repaint(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | mapRenderer.setNavigationPreviewSpeed(0.0); // 清除速度显示 |
| | | mapRenderer.repaint(); |
| | | } |
| | | |
| | | // 退出时将设备yaw属性重置为0 |
| | | try { |
| | | Device.getActiveDevice().setYaw("0"); |
| | | } catch (Exception ignore) { } |
| | | |
| | | // 恢复地块管理页面 |
| | | // 在清空currentDikuai之前保存地块编号,使用final变量以便在lambda中使用 |
| | |
| | | } |
| | | |
| | | |
| | | |