826220679@qq.com
6 小时以前 d58d5e1baf9390d4119f48c4af3fbcf308da10a3
进一步优化了异形无障碍物路径规划算法
已修改4个文件
71 ■■■■ 文件已修改
Obstacledge.properties 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
dikuai.properties 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
set.properties 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/lujing/YixinglujingNoObstacle.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Obstacledge.properties
@@ -1,5 +1,5 @@
# 割草机地块障碍物配置文件
# 生成时间:2025-12-27T22:24:22.132171500
# 生成时间:2025-12-27T22:43:36.141786300
# 坐标系:WGS84(度分格式)
# ============ 地块基准站配置 ============
dikuai.properties
@@ -1,5 +1,5 @@
#Dikuai Properties
#Sat Dec 27 22:24:22 GMT+08:00 2025
#Sat Dec 27 22:43:36 GMT+08:00 2025
LAND1.intelligentSceneAnalysis=-1
LAND1.mowingSafetyDistance=0.53
LAND1.landArea=577.12
@@ -11,8 +11,8 @@
LAND1.returnPathRawCoordinates=-1
LAND1.boundaryOriginalXY=-1
LAND1.mowingWidth=1.00
LAND1.plannedPath=73.87,49.87;50.78,50.04;41.97,22.44;49.61,19.99;51.85,34.26;66.29,36.70;66.29,21.40;78.09,24.75;73.87,49.87;73.96,49.37;50.62,49.54;50.30,48.54;74.12,48.37;74.29,47.37;49.98,47.54;49.66,46.55;74.46,46.37;74.63,45.37;49.34,45.55;49.02,44.55;74.80,44.37;74.96,43.37;48.71,43.55;48.39,42.55;75.13,42.36;75.30,41.36;48.07,41.56;47.75,40.56;75.47,40.36;75.64,39.36;47.43,39.56;47.11,38.56;75.80,38.36;75.97,37.36;46.79,37.57;46.48,36.57;64.75,36.44;66.29,36.43;76.14,36.36;76.31,35.36;66.29,35.43;59.07,35.48;46.16,35.57;45.84,34.57;53.38,34.52;66.29,34.43;76.48,34.35;76.64,33.35;66.29,33.43;51.73,33.53;45.52,33.57;45.20,32.58;51.57,32.53;66.29,32.43;76.81,32.35;76.98,31.35;66.29,31.43;51.42,31.53;44.88,31.58;44.56,30.58;51.26,30.53;66.29,30.43;77.15,30.35;77.32,29.35;66.29,29.43;51.11,29.53;44.25,29.58;43.93,28.59;50.95,28.54;66.29,28.43;77.48,28.35;77.65,27.35;66.29,27.43;50.79,27.54;43.61,27.59;43.29,26.59;50.64,26.54;66.29,26.43;77.82,26.34;77.99,25.34;66.29,25.43;50.48,25.54;42.97,25.59;42.65,24.59;50.32,24.54;66.29,24.43;76.69,24.35;73.25,23.38;66.29,23.43;50.17,23.54;42.33,23.60;42.02,22.60;50.01,22.54;66.29,22.43;69.81,22.40;66.38,21.43;66.29,21.43;49.85,21.54;44.66,21.58;47.85,20.56;49.70,20.54
LAND1.updateTime=2025-12-27 22\:24\:22
LAND1.plannedPath=73.87,49.87;50.78,50.04;41.97,22.44;49.61,19.99;51.85,34.26;66.29,36.70;66.29,21.40;78.09,24.75;73.87,49.87;73.96,49.37;50.62,49.54;50.30,48.54;74.12,48.37;74.29,47.37;49.98,47.54;49.66,46.55;74.46,46.37;74.63,45.37;49.34,45.55;49.02,44.55;74.80,44.37;74.96,43.37;48.71,43.55;48.39,42.55;75.13,42.36;75.30,41.36;48.07,41.56;47.75,40.56;75.47,40.36;75.64,39.36;47.43,39.56;47.11,38.56;75.80,38.36;75.97,37.36;46.79,37.57;66.29,36.43;76.14,36.36;76.31,35.36;66.29,35.43;66.29,34.43;76.48,34.35;76.64,33.35;66.29,33.43;66.29,32.43;76.81,32.35;76.98,31.35;66.29,31.43;66.29,30.43;77.15,30.35;77.32,29.35;66.29,29.43;66.29,28.43;77.48,28.35;77.65,27.35;66.29,27.43;66.29,26.43;77.82,26.34;77.99,25.34;66.29,25.43;66.29,24.43;76.69,24.35;73.25,23.38;66.29,23.43;66.29,22.43;69.81,22.40;66.38,21.43;66.29,21.43;47.85,20.56;49.70,20.54;46.48,36.57;64.75,36.44;59.07,35.48;46.16,35.57;45.84,34.57;53.38,34.52;51.73,33.53;45.52,33.57;45.20,32.58;51.57,32.53;51.42,31.53;44.88,31.58;44.56,30.58;51.26,30.53;51.11,29.53;44.25,29.58;43.93,28.59;50.95,28.54;50.79,27.54;43.61,27.59;43.29,26.59;50.64,26.54;50.48,25.54;42.97,25.59;42.65,24.59;50.32,24.54;50.17,23.54;42.33,23.60;42.02,22.60;50.01,22.54;49.85,21.54;44.66,21.58
LAND1.updateTime=2025-12-27 22\:43\:36
LAND1.baseStationCoordinates=3949.89151752,N,11616.79267501,E
LAND1.boundaryPointInterval=-1
LAND1.createTime=2025-12-23 17\:08\:09
set.properties
@@ -1,5 +1,5 @@
#Mower Configuration Properties - Updated
#Sat Dec 27 22:24:35 GMT+08:00 2025
#Sat Dec 27 22:43:28 GMT+08:00 2025
appVersion=-1
simCardNumber=-1
currentWorkLandNumber=LAND1
@@ -7,13 +7,13 @@
boundaryLengthVisible=false
idleTrailDurationSeconds=60
handheldMarkerId=1872
viewCenterX=-60.00
viewCenterY=-34.94
viewCenterX=-66.17
viewCenterY=-34.20
manualBoundaryDrawingMode=false
mowerId=6258
serialPortName=COM15
serialAutoConnect=true
mapScale=12.39
mapScale=10.32
measurementModeEnabled=false
firmwareVersion=-1
cuttingWidth=200
src/lujing/YixinglujingNoObstacle.java
@@ -71,6 +71,20 @@
    }
    private static List<PathSegment> generateGlobalScanPath(List<Point> polygon, double width, double angle, Point currentPos) {
        // 先尝试将凹陷处视为两个独立区域,分两次扫描,避免跨区直线连接
        List<PathSegment> all = new ArrayList<>();
        // 第一次扫描:优先处理左侧区域(groupIndex=0)
        List<PathSegment> leftScan = generateScanPathForSide(polygon, width, angle, currentPos, 0);
        all.addAll(leftScan);
        Point posAfterLeft = leftScan.isEmpty() ? currentPos : leftScan.get(leftScan.size() - 1).end;
        // 第二次扫描:处理右侧区域(groupIndex=1),从左侧结束点沿边界到右侧首段
        List<PathSegment> rightScan = generateScanPathForSide(polygon, width, angle, posAfterLeft, 1);
        all.addAll(rightScan);
        return all;
    }
    // 仅扫描指定侧(同一条扫描线的第 groupIndex 段),用于将“耳朵”视为独立区域
    private static List<PathSegment> generateScanPathForSide(List<Point> polygon, double width, double angle, Point currentPos, int sideIndex) {
        List<PathSegment> segments = new ArrayList<>();
        List<Point> rotatedPoly = new ArrayList<>();
        for (Point p : polygon) rotatedPoly.add(rotatePoint(p, -angle));
@@ -82,37 +96,46 @@
        }
        boolean leftToRight = true;
        // 步长 y 从最小到最大扫描
        boolean firstSegmentConnected = false;
        for (double y = minY + width/2; y <= maxY - width/2; y += width) {
            List<Double> xIntersections = getXIntersections(rotatedPoly, y);
            if (xIntersections.size() < 2) continue;
            Collections.sort(xIntersections);
            // 处理凹多边形:每两个点组成一个有效作业段
            // 构建本行的作业段(左到右)和组索引
            List<PathSegment> lineSegmentsInRow = new ArrayList<>();
            for (int i = 0; i < xIntersections.size() - 1; i += 2) {
            List<Integer> groupIndices = new ArrayList<>();
            for (int i = 0, g = 0; i < xIntersections.size() - 1; i += 2, g++) {
                Point pS = rotatePoint(new Point(xIntersections.get(i), y), angle);
                Point pE = rotatePoint(new Point(xIntersections.get(i + 1), y), angle);
                lineSegmentsInRow.add(new PathSegment(pS, pE, true));
                groupIndices.add(g);
            }
            // 根据当前S型方向排序作业段
            if (!leftToRight) {
                Collections.reverse(lineSegmentsInRow);
                Collections.reverse(groupIndices);
                for (PathSegment s : lineSegmentsInRow) {
                    Point temp = s.start; s.start = s.end; s.end = temp;
                }
            }
            // 将作业段连接到总路径
            for (PathSegment s : lineSegmentsInRow) {
                if (Math.hypot(currentPos.x - s.start.x, currentPos.y - s.start.y) > 0.01) {
                    // 如果间距大于1cm,添加空走路径
                    addSafeConnection(segments, currentPos, s.start, polygon);
                }
                segments.add(s);
                currentPos = s.end;
            int idxInRow = groupIndices.indexOf(sideIndex);
            if (idxInRow == -1) {
                // 本行不包含该侧的作业段,跳过
                leftToRight = !leftToRight;
                continue;
            }
            PathSegment s = lineSegmentsInRow.get(idxInRow);
            // 首次连接或跨区连接均强制沿边界,避免穿越凹陷区
            if (Math.hypot(currentPos.x - s.start.x, currentPos.y - s.start.y) > 0.01) {
                addBoundaryConnection(segments, currentPos, s.start, polygon);
                firstSegmentConnected = true;
            }
            segments.add(s);
            currentPos = s.end;
            leftToRight = !leftToRight;
        }
        return segments;
@@ -245,6 +268,14 @@
        }
    }
    // 强制沿边界绕行的连接(不做直线安全判断),用来在同一扫描行的多个作业段之间跳转
    private static void addBoundaryConnection(List<PathSegment> segments, Point start, Point end, List<Point> polygon) {
        List<Point> path = getBoundaryPath(start, end, polygon);
        for (int i = 0; i < path.size() - 1; i++) {
            segments.add(new PathSegment(path.get(i), path.get(i+1), false));
        }
    }
    private static boolean isSegmentSafe(Point p1, Point p2, List<Point> polygon) {
        Point mid = new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
        if (!isPointInPolygon(mid, polygon)) return false;