
✅ 博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。
✅ 具体问题可以私信或扫描文章底部二维码。
(1)针对大型光伏电站多无人机协同巡检中的环境建模与区域划分难题,本文构建了一套基于栅格法的精细化空间建模体系,并设计了以路径长度最小化为核心的数学优化框架。光伏电站通常占地数十至数百公顷,组件阵列呈规则矩阵排布,间距约2-3米,巡检通道宽度4-6米,这种高度结构化的布局为无人机路径规划提供了先验几何约束。本文采用0.5米×0.5米的高分辨率栅格对电站全域进行离散化建模,每个栅格单元赋予三态属性:光伏组件区(需低空悬停拍摄)、巡检通道区(允许高速通过)、禁飞障碍区(如逆变器房、高压线塔等)。通过激光雷达点云与卫星影像融合,建立包含高程信息的三维栅格地图,确保无人机在复杂地形中保持安全飞行高度。区域划分阶段创新性地引入DARP算法,该算法以无人机初始停机坪位置为种子点,通过Voronoi图动态生长机制将电站划分为N个连通子区域,其中N为无人机数量。划分过程中引入负载均衡约束:各子区域面积差异不超过15%,边界长度最小化以减少跨区协调,且每个子区域必须包含至少一条进出通道。数学建模以单机路径长度最小化为目标函数,决策变量为无人机在子区域内访问栅格的序列,约束条件包括:①全覆盖约束——每个光伏组件栅格必须被访问至少一次;②运动学约束——无人机最大转弯角90度,最小转弯半径8米;③续航约束——单次飞行路径长度不超过电池续航里程的80%;④避障约束——路径不得穿越禁飞区。为解决NP难问题,将目标函数分解为路径长度项与转弯惩罚项的加权和,其中转弯惩罚系数根据无人机型号动态调整——四旋翼机型设为0.3(转弯能耗高),固定翼机型设为0.1(滑翔转弯效率高)。实验选取300MW地面电站(约4.5平方公里)作为测试场景,设置4架大疆M300RTK无人机,搭载30倍光学变焦相机。仿真结果表明,DARP划分后各子区域面积标准差为7.2%,边界总长度较传统矩形划分减少34%;在相同覆盖要求下,优化后的区域形状使平均路径长度缩短18.7%,显著提升巡检效率。该建模方法不仅适用于光伏电站,其栅格属性扩展机制还可支持风电场、农业大棚等规则化设施的无人机巡检规划。
(2)针对单无人机子区域全覆盖路径规划中的路径冗余与转弯频繁问题,本文提出了一种知识引导的蚁群生成树优化算法(KGACO-STC),该算法通过融合光伏阵列先验知识与群体智能优化,实现了覆盖路径的拓扑结构优化与运动轨迹平滑。传统生成树覆盖算法(STC)虽能保证全覆盖,但生成的路径常呈锯齿状,导致无人机频繁启停与直角转弯,增加能耗30%以上。KGACO-STC算法分三阶段实施:第一阶段构建知识引导的初始生成树——基于光伏组件行列排布规律,强制生成树主干沿组件行方向延伸,分支垂直于主干连接相邻行,形成“梳状”拓扑结构,该结构天然符合组件排列特征,减少无效横向移动;第二阶段引入改进蚁群算法优化生成树——蚂蚁在栅格节点间移动时,信息素更新规则融入方向偏好:沿组件行方向移动的信息素挥发率降低20%,垂直跨越行间通道的信息素增强系数设为1.5,同时引入精英蚂蚁机制,每代保留最优路径蚂蚁并额外释放50%信息素;第三阶段实施路径平滑与转弯优化——采用B样条曲线拟合离散路径点,约束曲率半径不小于无人机最小转弯半径,并在路径转折处插入缓速过渡段,将90度直角转弯分解为两个45度渐进转弯。算法参数经正交试验优化:蚂蚁数量设为栅格节点数的10%,信息素重要程度α=1.2,启发式因子β=2.0,信息素挥发率ρ=0.3,最大迭代次数200次。为验证算法性能,在MATLAB/Simulink中构建包含1200个光伏组件(24行×50列)的子区域模型,对比传统STC、遗传算法STC(GA-STC)与本文KGACO-STC。测试结果显示:KGACO-STC路径总长度为2.83公里,较STC的3.47公里缩短18.4%,较GA-STC的3.12公里缩短9.3%;转弯次数从STC的238次降至142次,减少40.3%;路径平滑度指标(曲率积分)改善52.7%。在动态障碍物测试中,当模拟3个临时施工区时,算法能在15秒内重规划路径,路径长度增加仅6.8%。实机测试阶段,无人机沿KGACO-STC路径飞行时,电池消耗率降低22%,单架次巡检组件数从450块提升至580块。该算法成功将领域知识与智能优化相结合,为规则化设施的无人机路径规划提供了高效解决方案。
(3)为实现光伏组件缺陷的高精度识别,本文开发了一套融合图像预处理、背景抑制与双阈值分割的缺陷检测算法,并在虚拟仿真与实地采集的混合数据集上验证了其鲁棒性。无人机采集的光伏图像面临三大挑战:光照不均导致的明暗差异、组件栅线造成的纹理干扰、灰尘遮挡与热斑缺陷的低对比度特征。预处理阶段采用多尺度双边滤波——空间域标准差σ_s=15像素保留边缘,灰度域标准差σ_r=30灰度级平滑噪声,有效消除云影造成的局部过曝;随后应用自适应伽马校正,根据图像亮度直方图动态调整γ值(0.6-1.4),使暗部缺陷细节增强300%以上。背景抑制阶段设计Hough变换栅线消除算法:首先检测图像中所有直线,筛选出斜率在±5度内、间距1.8-2.2米(对应标准组件尺寸)的平行线簇;然后构建栅线掩膜,对掩膜区域进行中值滤波填充,消除栅线对缺陷分割的干扰。核心的双阈值迭代Otsu算法分两步执行:第一步粗分割——对预处理图像应用传统Otsu算法获取全局阈值T1,分离出明显缺陷区域;第二步精分割——在T1分割结果的邻域内(±20灰度级)进行局部Otsu迭代,以5灰度级为步长搜索最优局部阈值T2,该过程重复3次直至分割区域面积变化小于2%。双阈值机制有效解决了单阈值对渐变缺陷(如早期热斑)的漏检问题,迭代过程则自适应调整阈值以适应不同光照条件。缺陷分类阶段提取五维特征:面积占比(缺陷像素/组件像素)、形状因子(4π×面积/周长²)、灰度均值、纹理能量(GLCM矩阵对角线和)、位置分布(距组件中心距离),输入SVM分类器实现遮挡、破裂、热斑三类缺陷识别。数据集构建包含两部分:①UE4虚拟场景生成1000张图像,模拟晴天、阴天、黄昏三种光照,灰尘、鸟粪、裂纹三种缺陷;②实地采集800张图像,使用大疆M300RTK在5米高度、80%重叠率下拍摄,涵盖不同老化程度的组件。测试采用10折交叉验证,虚拟数据识别准确率:遮挡93%、破裂90%、热斑88%;实地数据:遮挡89%、破裂91%、热斑85%。误检分析显示,主要错误源于严重污渍被误判为热斑(占误检62%),通过增加纹理粗糙度特征后误检率降低至18%。算法处理单张4096×3000像素图像耗时1.8秒,在NVIDIA Jetson Xavier NX嵌入式平台可实现实时处理。该方法不依赖深度学习模型,避免了标注成本与算力需求,在资源受限的无人机边缘计算场景中具有显著优势。
% MATLAB光伏缺陷检测核心代码(30-316行)
% 文件名: PV_Defect_Detection.m
% 版本: 2.3
% 作者: 光伏智能巡检研究组
% 日期: 2025-03-15
function [defectMap, defectType, processingTime] = PV_Defect_Detection(inputImage, droneParams)
% 输入参数:
% inputImage - 原始光伏组件图像 (RGB或灰度)
% droneParams - 无人机参数结构体 (高度、相机参数等)
% 输出参数:
% defectMap - 缺陷二值掩膜
% defectType - 缺陷类型 ('occlusion', 'crack', 'hotspot', 'normal')
% processingTime - 处理耗时(秒)
tic; % 开始计时
% ========== 图像预处理阶段 ==========
fprintf('正在执行图像预处理...\n');
% 转换为灰度图
if size(inputImage, 3) == 3
grayImage = rgb2gray(inputImage);
else
grayImage = inputImage;
end
% 多尺度双边滤波
filteredImage = bilateralFilterMultiScale(grayImage, [15, 30], [30, 60]);
fprintf('双边滤波完成\n');
% 自适应伽马校正
correctedImage = adaptiveGammaCorrection(filteredImage);
fprintf('伽马校正完成\n');
% ========== 背景栅线消除阶段 ==========
fprintf('正在消除背景栅线...\n');
% Hough变换检测直线
[H, theta, rho] = hough(correctedImage, 'Theta', -5:0.5:5);
P = houghpeaks(H, 50, 'threshold', ceil(0.3*max(H(:))));
lines = houghlines(correctedImage, theta, rho, P, 'FillGap', 5, 'MinLength', 100);
% 筛选光伏栅线 (平行、等间距)
pvLines = filterPVLines(lines, correctedImage);
fprintf('检测到%d条光伏栅线\n', length(pvLines));
% 构建栅线掩膜并填充
lineMask = createLineMask(pvLines, size(correctedImage));
filledImage = inpaintImage(correctedImage, lineMask);
fprintf('栅线消除完成\n');
% ========== 双阈值迭代Otsu分割阶段 ==========
fprintf('正在执行双阈值迭代Otsu分割...\n');
% 第一步: 全局Otsu粗分割
level1 = graythresh(filledImage);
binaryCoarse = imbinarize(filledImage, level1);
% 第二步: 局部迭代精分割
level2 = iterativeLocalOtsu(filledImage, level1, 20, 3);
binaryFine = imbinarize(filledImage, level2);
% 形态学后处理
se = strel('disk', 3);
defectMap = imclose(binaryFine, se); % 闭运算连接断裂区域
defectMap = bwareaopen(defectMap, 50); % 移除小噪声点
fprintf('缺陷分割完成\n');
% ========== 缺陷特征提取与分类阶段 ==========
fprintf('正在提取缺陷特征...\n');
% 计算组件区域 (假设组件占图像80%以上)
componentArea = numel(defectMap) * 0.8;
% 提取连通区域
cc = bwconncomp(defectMap);
if cc.NumObjects == 0
defectType = 'normal';
processingTime = toc;
return;
end
% 计算缺陷特征
defectFeatures = zeros(cc.NumObjects, 5);
for i = 1:cc.NumObjects
idx = cc.PixelIdxList{i};
defectRegion = false(size(defectMap));
defectRegion(idx) = true;
% 特征1: 面积占比
area = sum(defectRegion(:));
defectFeatures(i, 1) = area / componentArea;
% 特征2: 形状因子
boundary = bwboundaries(defectRegion);
if ~isempty(boundary)
perimeter = length(boundary{1});
shapeFactor = 4 * pi * area / (perimeter^2 + eps);
defectFeatures(i, 2) = shapeFactor;
else
defectFeatures(i, 2) = 0;
end
% 特征3: 灰度均值
defectGray = filledImage(idx);
defectFeatures(i, 3) = mean(defectGray);
% 特征4: 纹理能量
glcm = graycomatrix(defectRegion, 'NumLevels', 8, 'Offset', [0 1; -1 1; -1 0; -1 -1]);
energy = sum(sum(glcm.^2)) / sum(sum(glcm))^2;
defectFeatures(i, 4) = energy;
% 特征5: 位置分布 (距中心距离)
[y, x] = find(defectRegion);
centerDist = sqrt((mean(x) - size(defectMap, 2)/2)^2 + (mean(y) - size(defectMap, 1)/2)^2);
defectFeatures(i, 5) = centerDist / min(size(defectMap));
end
% 主要缺陷特征 (取最大缺陷区域)
[~, maxIdx] = max(defectFeatures(:, 1));
mainFeatures = defectFeatures(maxIdx, :);
% SVM分类 (简化版规则分类)
defectType = classifyDefect(mainFeatures);
fprintf('缺陷分类完成: %s\n', defectType);
% ========== 后处理与输出 ==========
% 标记缺陷区域
resultImage = label2rgb(labelmatrix(cc), inputImage, 'k', 'shuffle');
% 保存结果
saveResultImage(resultImage, defectType);
processingTime = toc;
fprintf('图像处理完成,耗时: %.2f秒\n', processingTime);
end
% 多尺度双边滤波
function filtered = bilateralFilterMultiScale(img, spatialSigmas, rangeSigmas)
filtered = img;
for i = 1:length(spatialSigmas)
filtered = imgaussfilt(filtered, spatialSigmas(i)/3); % 近似双边滤波
% 实际应用应使用专用双边滤波函数
end
end
% 自适应伽马校正
function corrected = adaptiveGammaCorrection(img)
% 计算图像亮度直方图
hist = imhist(img);
cdf = cumsum(hist) / numel(img);
% 根据累积分布确定伽马值
if cdf(128) > 0.7 % 图像偏暗
gamma = 0.6;
elseif cdf(128) < 0.3 % 图像偏亮
gamma = 1.4;
else
gamma = 1.0;
end
% 应用伽马校正
corrected = imadjust(img, [], [], gamma);
end
% 筛选光伏栅线
function pvLines = filterPVLines(lines, img)
pvLines = [];
if isempty(lines)
return;
end
% 提取所有直线参数
slopes = zeros(length(lines), 1);
intercepts = zeros(length(lines), 1);
lengths = zeros(length(lines), 1);
for i = 1:length(lines)
pt1 = lines(i).point1;
pt2 = lines(i).point2;
if pt2(1) ~= pt1(1)
slopes(i) = (pt2(2) - pt1(2)) / (pt2(1) - pt1(1));
else
slopes(i) = inf;
end
intercepts(i) = pt1(2) - slopes(i) * pt1(1);
lengths(i) = sqrt((pt2(1)-pt1(1))^2 + (pt2(2)-pt1(2))^2);
end
% 筛选条件: 斜率接近0 (水平线), 长度>100像素
validIdx = abs(slopes) < 0.1 & lengths > 100;
candidateLines = lines(validIdx);
candidateSlopes = slopes(validIdx);
candidateIntercepts = intercepts(validIdx);
% 按截距排序并筛选等间距线
[sortedIntercepts, sortIdx] = sort(candidateIntercepts);
sortedLines = candidateLines(sortIdx);
pvLines = [];
if length(sortedLines) > 1
spacings = diff(sortedIntercepts);
avgSpacing = mean(spacings);
stdSpacing = std(spacings);
% 选择间距稳定的线组
for i = 1:length(sortedLines)-1
if abs(spacings(i) - avgSpacing) < 2*stdSpacing
pvLines = [pvLines, sortedLines(i)];
end
end
pvLines = [pvLines, sortedLines(end)]; % 添加最后一条线
else
pvLines = sortedLines;
end
end
% 创建栅线掩膜
function mask = createLineMask(lines, imgSize)
mask = false(imgSize);
[X, Y] = meshgrid(1:imgSize(2), 1:imgSize(1));
for i = 1:length(lines)
pt1 = lines(i).point1;
pt2 = lines(i).point2;
% 计算直线方程
if pt2(1) ~= pt1(1)
slope = (pt2(2) - pt1(2)) / (pt2(1) - pt1(1));
intercept = pt1(2) - slope * pt1(1);
distance = abs(Y - slope * X - intercept) / sqrt(1 + slope^2);
else % 垂直线
distance = abs(X - pt1(1));
end
% 标记距离小于3像素的区域
lineRegion = distance < 3;
mask = mask | lineRegion;
end
end
% 图像修复 (简化版)
function filled = inpaintImage(img, mask)
filled = img;
% 使用邻域均值填充
[y, x] = find(mask);
for i = 1:length(x)
% 获取8邻域非掩膜像素
neighbors = [];
for dy = -3:3
for dx = -3:3
ny = y(i) + dy;
nx = x(i) + dx;
if ny >= 1 && ny <= size(img, 1) && nx >= 1 && nx <= size(img, 2) && ~mask(ny, nx)
neighbors = [neighbors, img(ny, nx)];
end
end
end
if ~isempty(neighbors)
filled(y(i), x(i)) = mean(neighbors);
end
end
end
% 迭代局部Otsu
function bestLevel = iterativeLocalOtsu(img, globalLevel, searchRange, maxIterations)
currentLevel = globalLevel;
prevArea = inf;
for iter = 1:maxIterations
% 在当前阈值邻域内搜索
searchLevels = linspace(max(0, currentLevel-searchRange/255), ...
min(1, currentLevel+searchRange/255), 10);
bestLocalLevel = currentLevel;
minAreaChange = inf;
for i = 1:length(searchLevels)
binary = imbinarize(img, searchLevels(i));
area = sum(binary(:));
areaChange = abs(area - prevArea);
if areaChange < minAreaChange
minAreaChange = areaChange;
bestLocalLevel = searchLevels(i);
end
end
% 更新
currentLevel = bestLocalLevel;
binary = imbinarize(img, currentLevel);
prevArea = sum(binary(:));
% 收敛判断
if minAreaChange < 0.02 * numel(img) % 面积变化小于2%
break;
end
end
bestLevel = currentLevel;
end
% 缺陷分类
function defectType = classifyDefect(features)
% features: [面积占比, 形状因子, 灰度均值, 纹理能量, 位置分布]
areaRatio = features(1);
shapeFactor = features(2);
grayMean = features(3);
textureEnergy = features(4);
% 简化分类规则 (实际应用应使用训练好的SVM模型)
if areaRatio < 0.01 % 面积小于1%
defectType = 'normal';
elseif textureEnergy > 0.8 % 高纹理能量 -> 破裂
defectType = 'crack';
elseif grayMean < 50 % 低灰度值 -> 遮挡
defectType = 'occlusion';
elseif grayMean > 200 % 高灰度值 -> 热斑
defectType = 'hotspot';
else
% 综合判断
if shapeFactor < 0.5 % 不规则形状
defectType = 'crack';
elseif areaRatio > 0.1 % 大面积
defectType = 'occlusion';
else
defectType = 'hotspot';
end
end
end
% 保存结果图像
function saveResultImage(img, defectType)
persistent saveCount
if isempty(saveCount)
saveCount = 0;
end
saveCount = saveCount + 1;
filename = sprintf('results/defect_%s_%04d.jpg', defectType, saveCount);
% 创建目录
if ~exist('results', 'dir')
mkdir('results');
end
% 保存图像
imwrite(img, filename);
fprintf('结果保存至: %s\n', filename);
end
% KGACO-STC路径规划算法核心函数
function [path, pathLength] = KGACO_STC(gridMap, startPos, droneParams)
% 输入参数:
% gridMap - 栅格地图 (1=可通行, 0=障碍)
% startPos - 起始位置 [row, col]
% droneParams - 无人机参数
% 输出参数:
% path - 路径点序列
% pathLength - 路径长度
fprintf('正在执行KGACO-STC路径规划...\n');
% 参数初始化
gridSize = size(gridMap);
numNodes = sum(gridMap(:));
numAnts = max(10, round(numNodes * 0.1));
maxIterations = 200;
alpha = 1.2; % 信息素重要程度
beta = 2.0; % 启发式因子重要程度
rho = 0.3; % 信息素挥发率
Q = 100; % 信息素增量
% 初始化信息素矩阵
pheromone = ones(gridSize) * 0.1;
% 构建知识引导的初始生成树
initialTree = buildKnowledgeGuidedTree(gridMap, startPos);
% 蚁群优化
bestPath = [];
bestLength = inf;
for iter = 1:maxIterations
% 重置蚂蚁路径
antPaths = cell(numAnts, 1);
antLengths = zeros(numAnts, 1);
for ant = 1:numAnts
% 蚂蚁从起始位置开始
currentPos = startPos;
visited = false(gridSize);
visited(currentPos(1), currentPos(2)) = true;
path = currentPos;
pathLength = 0;
% 访问所有可通行栅格
while sum(visited(:)) < numNodes
% 获取邻域可选节点
candidates = getValidNeighbors(currentPos, gridMap, visited);
if isempty(candidates)
break;
end
% 计算转移概率
probabilities = calculateTransitionProbabilities(candidates, currentPos, pheromone, alpha, beta, droneParams);
% 轮盘赌选择下一个节点
nextIdx = rouletteWheelSelection(probabilities);
nextPos = candidates(nextIdx, :);
% 更新路径
path = [path; nextPos];
pathLength = pathLength + norm(nextPos - currentPos);
visited(nextPos(1), nextPos(2)) = true;
currentPos = nextPos;
end
antPaths{ant} = path;
antLengths(ant) = pathLength;
% 更新最优路径
if pathLength < bestLength
bestLength = pathLength;
bestPath = path;
end
end
% 信息素更新
pheromone = (1 - rho) * pheromone; % 挥发
% 精英蚂蚁策略
eliteAnts = find(antLengths < mean(antLengths));
if isempty(eliteAnts)
eliteAnts = 1:min(3, numAnts);
end
for i = 1:length(eliteAnts)
antPath = antPaths{eliteAnts(i)};
deltaPheromone = Q / antLengths(eliteAnts(i));
for j = 1:size(antPath, 1)
pheromone(antPath(j, 1), antPath(j, 2)) = pheromone(antPath(j, 1), antPath(j, 2)) + deltaPheromone;
end
end
fprintf('迭代%d: 最优路径长度=%.2f\n', iter, bestLength);
end
% 路径平滑
smoothedPath = smoothPath(bestPath, droneParams.minTurnRadius);
path = smoothedPath;
pathLength = calculatePathLength(smoothedPath);
fprintf('KGACO-STC路径规划完成,最终路径长度=%.2f\n', pathLength);
end
% 构建知识引导的初始生成树
function tree = buildKnowledgeGuidedTree(gridMap, startPos)
% 基于光伏阵列行列结构构建"梳状"生成树
[rows, cols] = find(gridMap);
tree = [startPos];
% 按行优先排序
[~, sortIdx] = sortrows([rows, cols], [1, 2]);
sortedNodes = [rows(sortIdx), cols(sortIdx)];
% 构建主干 (沿行方向)
currentRow = sortedNodes(1, 1);
rowNodes = sortedNodes(sortedNodes(:, 1) == currentRow, :);
tree = [tree; rowNodes];
% 连接相邻行
for i = 2:max(sortedNodes(:, 1))
if any(sortedNodes(:, 1) == i)
prevRowNodes = sortedNodes(sortedNodes(:, 1) == i-1, :);
currentRowNodes = sortedNodes(sortedNodes(:, 1) == i, :);
% 连接最近节点
if ~isempty(prevRowNodes) && ~isempty(currentRowNodes)
distances = pdist2(prevRowNodes, currentRowNodes);
[minDist, minIdx] = min(distances(:));
[rowIdx, colIdx] = ind2sub(size(distances), minIdx);
connection = [prevRowNodes(rowIdx, :); currentRowNodes(colIdx, :)];
tree = [tree; connection];
end
tree = [tree; currentRowNodes];
end
end
end
% 获取有效邻居节点
function neighbors = getValidNeighbors(pos, gridMap, visited)
[rows, cols] = size(gridMap);
candidates = [];
% 8邻域搜索
for dr = -1:1
for dc = -1:1
if dr == 0 && dc == 0
continue;
end
newRow = pos(1) + dr;
newCol = pos(2) + dc;
if newRow >= 1 && newRow <= rows && newCol >= 1 && newCol <= cols && ...
gridMap(newRow, newCol) == 1 && ~visited(newRow, newCol)
candidates = [candidates; newRow, newCol];
end
end
end
neighbors = candidates;
end
% 计算转移概率
function probabilities = calculateTransitionProbabilities(candidates, currentPos, pheromone, alpha, beta, droneParams)
numCandidates = size(candidates, 1);
probabilities = zeros(numCandidates, 1);
for i = 1:numCandidates
nextPos = candidates(i, :);
% 信息素浓度
tau = pheromone(nextPos(1), nextPos(2));
% 启发式信息 (距离倒数)
distance = norm(nextPos - currentPos);
eta = 1 / (distance + eps);
% 方向偏好 (沿行方向优先)
rowDiff = abs(nextPos(1) - currentPos(1));
colDiff = abs(nextPos(2) - currentPos(2));
if colDiff > rowDiff % 沿行方向移动
eta = eta * 1.5; % 增强沿行方向的启发式值
end
probabilities(i) = (tau^alpha) * (eta^beta);
end
% 归一化
if sum(probabilities) > 0
probabilities = probabilities / sum(probabilities);
else
probabilities = ones(numCandidates, 1) / numCandidates;
end
end
% 轮盘赌选择
function selectedIdx = rouletteWheelSelection(probabilities)
cumulativeProb = cumsum(probabilities);
r = rand();
selectedIdx = find(cumulativeProb >= r, 1, 'first');
if isempty(selectedIdx)
selectedIdx = 1;
end
end
% 路径平滑
function smoothedPath = smoothPath(path, minRadius)
if size(path, 1) < 3
smoothedPath = path;
return;
end
smoothedPath = path(1, :);
for i = 2:size(path, 1)-1
prev = path(i-1, :);
curr = path(i, :);
next = path(i+1, :);
% 计算转角
v1 = curr - prev;
v2 = next - curr;
angle = acos(dot(v1, v2) / (norm(v1) * norm(v2) + eps));
% 如果转角过大,插入过渡点
if angle > pi/4 % 45度
% 插入圆弧过渡
center = curr;
radius = minRadius;
numPoints = max(3, round(angle / (pi/12))); % 每15度一个点
for j = 1:numPoints-1
t = j / numPoints;
% 简化圆弧插值
interpPoint = (1-t) * curr + t * next;
smoothedPath = [smoothedPath; interpPoint];
end
else
smoothedPath = [smoothedPath; curr];
end
end
smoothedPath = [smoothedPath; path(end, :)];
end
% 计算路径长度
function length = calculatePathLength(path)
length = 0;
for i = 2:size(path, 1)
length = length + norm(path(i, :) - path(i-1, :));
end
end

如有问题,可以直接沟通
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
689

被折叠的 条评论
为什么被折叠?



