多机器人路径规划算法可视化

本文主要参考源代码here

这个是EECBS的源码,可以根据其中的single agent进行改进,最后用了两种形式将我改进的算法实现可视化。注意:源码中的地图原点位于左上角(0,0),可视化的时候需要注意这点,最后生成的path以txt的格式展现,其中的excel是算法的相关各个部分处理时间,可以选用不同的启发式DG、WDG等实现不同的效果。

我的地图也是课题组自己生成的某个地图库,可以看以下上述链接,其中有各种各样的源码地图,最后保存为txt格式

一、matlab

% 载入数据文件
a = load('swapped.txt');
[row, col] = size(a);
b = a;
b(end+1, end+1) = 0; % 扩展数组,增加一个零元素
figure;

% 设置栅格颜色,不改变背景栅格的颜色
colormap([0 0 0; 1 1 1]); % 黑色为障碍物,白色为路径

% 绘制栅格图
pcolor(-0.5:size(a, 2)-0.5, size(a, 1)-0.5:-1:-0.5, b); % 给坐标赋值,+0 起点就是 0
set(gca, 'xtick', 0:10:300, 'ytick', 0:10:190);
axis image xy
axis ij

% 去除网格线
grid off;
shading flat; % 去掉栅格线,平滑颜色过渡

hold on;

% 读取路径文件
filename = 'pathss-agent4.txt';

% 尝试读取整个文件内容
try
    fileContent = fileread(filename);
catch
    error('无法读取文件 %s', filename);
end

% 按行分割文件内容
lines = splitlines(fileContent);

% 初始化存储路径点的 cell 数组
numAgents = length(lines);
paths = cell(numAgents, 1);

% 解析每一行的数据
for i = 1:numAgents
    % 提取路径数据部分
    line = lines{i};
    
    % 使用正则表达式提取坐标
    tokens = regexp(line, '\((\d+),(\d+)\)', 'tokens');
    
    % 预分配坐标矩阵
    numPoints = length(tokens);
    points = zeros(numPoints, 2);
    
    % 将提取的数据转化为矩阵
    for j = 1:numPoints
        points(j, 1) = str2double(tokens{j}{1}); % y 坐标
        points(j, 2) = str2double(tokens{j}{2}); % x 坐标
    end
    
    % 存储路径
    paths{i} = points;
end

% 生成随机颜色
colors = rand(numAgents, 3);

% 绘制所有机器人的路径
for i = 1:numAgents
    path = paths{i};
    % 绘制每个机器人的路径,使用随机颜色
    plot(path(:,2), path(:,1), 'LineWidth', 1.5, 'Color', colors(i,:));
end

hold off;

% 生成第二个图,显示路径颜色和机器人编号对应关系
figure;

% 每行显示的机器人数量
numPerRow = 25; % 每行显示25个机器人,您可以调整这个值

% 计算总的行数
numRows = ceil(numAgents / numPerRow);

% 创建一个矩阵显示机器人编号和路径颜色的对应关系
for i = 1:numAgents
    % 计算当前机器人的行列位置
    rowIdx = ceil(i / numPerRow);
    colIdx = mod(i - 1, numPerRow) + 1;

    % 绘制每个机器人的小矩形,显示路径颜色
    % 将矩形的长宽缩小为 0.5
    rectangle('Position', [1.5*(colIdx-1), numRows-rowIdx, 0.5, 0.5], 'FaceColor', colors(i,:), 'EdgeColor', 'none');
    hold on;
    
    % 在小矩形旁边绘制空白矩形并显示机器人编号
    % 空白矩形也缩小为 0.5
    rectangle('Position', [1.5*(colIdx-1) + 0.5, numRows-rowIdx, 0.5, 0.5], 'FaceColor', [1 1 1], 'EdgeColor', 'none');
    
    % 显示机器人编号,字体大小稍微调整以适应缩小后的矩形
    text(1.5*(colIdx-1) + 0.75, numRows-rowIdx + 0.25, sprintf('Agent %d', i-1), ...
        'HorizontalAlignment', 'center', 'VerticalAlignment', 'middle', 'FontSize', 6, 'FontName', 'New Nomas');
end

% 设置图像的坐标轴
axis([0 numPerRow*1.5 0 numRows]);
set(gca, 'ytick', [], 'xtick', [], 'xlim', [0 numPerRow*1.5], 'ylim', [0 numRows]);

% 添加标题和标签
title('机器人路径颜色与编号的关系');
xlabel('机器人编号');
ylabel('行号');

% 添加颜色条,帮助识别颜色和机器人路径的关系
colorbar;
hold off;

% 生成第三个图,显示起点和终点位置
figure;

% 重新绘制栅格图
pcolor(-0.5:size(a, 2)-0.5, size(a, 1)-0.5:-1:-0.5, b); % 给坐标赋值,+0 起点就是 0
colormap([0 0 0; 1 1 1]); 
set(gca, 'xtick', 0:10:300, 'ytick', 0:10:190);
axis image xy
axis ij

% 去除网格线
grid off;
shading flat; % 去掉栅格线,平滑颜色过渡

hold on;

% 绘制起点和终点标记
for i = 1:numAgents
    path = paths{i};
    
    % 检查路径是否为空
    if ~isempty(path)
        % 绘制起点,小圆点
        plot(path(1,2), path(1,1), 'o', 'MarkerSize', 6, 'MarkerEdgeColor', colors(i,:), 'MarkerFaceColor', colors(i,:));
        
        % 绘制终点,小圆点
        plot(path(end,2), path(end,1), 'o', 'MarkerSize', 6, 'MarkerEdgeColor', colors(i,:), 'MarkerFaceColor', colors(i,:));
    end
end

% 添加标题
title('机器人起点与终点位置');
xlabel('X坐标');
ylabel('Y坐标');
hold off;

二、C#

sing System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace WinForms3
{
    public partial class Form1 : Form
    {
        private List<List<char>> mapData = new List<List<char>>(); // 地图数据
        private Dictionary<int, List<Point>> agentPaths = new Dictionary<int, List<Point>>(); // 机器人路径
        private int currentStep = 0; // 当前时间步
        private int totalSteps = 0; // 最大时间步数
        private System.Windows.Forms.Timer timer; // 定时器
        private Bitmap mapBitmap; // 缓存的地图位图
        private float scaleFactor = 1.0f; // 缩放因子
        private int cellSize = 20; // 单元格大小(默认大小)

        public Form1()
        {
            InitializeComponent();

            // 初始化定时器
            timer = new System.Windows.Forms.Timer();
            timer.Interval = 500; // 每500ms更新一次
            timer.Tick += Timer_Tick;
        }

        private void btnLoadMap_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "Text Files (*.txt)|*.txt";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string[] lines = File.ReadAllLines(openFileDialog.FileName);
                    mapData = lines.Select(line => line.ToList()).ToList();

                    // 绘制静态地图到位图
                    CreateMapBitmap();

                    // 动态计算缩放因子
                    UpdateScaleFactor();

                    pnlDrawing.Invalidate(); // 重绘
                }
            }
        }

        private void btnLoadPaths_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "Text Files (*.txt)|*.txt";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string[] lines = File.ReadAllLines(openFileDialog.FileName);
                    ParsePaths(lines);

                    // 初始化播放
                    currentStep = 0;
                    totalSteps = agentPaths.First().Value.Count;
                    timer.Start();
                }
            }
        }

        private void ParsePaths(string[] lines)
        {
            agentPaths.Clear();
            foreach (var line in lines)
            {
                var parts = line.Split(':');
                if (parts.Length == 2)
                {
                    int agentId = int.Parse(parts[0].Split()[1]);
                    var coordinates = parts[1].Trim().Split(')');
                    List<Point> path = new List<Point>();

                    foreach (var coord in coordinates)
                    {
                        if (coord.Contains('('))
                        {
                            var point = coord.Trim().Trim('(', ')').Split(',');
                            int y = int.Parse(point[0]); // 第一个是 Y 坐标
                            int x = int.Parse(point[1]); // 第二个是 X 坐标
                            path.Add(new Point(x, y)); // 转换为 (X, Y) 格式
                        }
                    }

                    agentPaths[agentId] = path;
                }
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            currentStep++;
            if (currentStep >= totalSteps)
            {
                timer.Stop();
            }

            pnlDrawing.Invalidate(); // 刷新绘图
        }

        private void pnlDrawing_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.Clear(Color.White);
            g.ScaleTransform(scaleFactor, scaleFactor);

            // 绘制静态地图
            if (mapBitmap != null)
            {
                g.DrawImage(mapBitmap, 0, 0);
            }

            // 绘制动态机器人路径
            if (agentPaths.Count > 0)
            {
                foreach (var agent in agentPaths)
                {
                    int agentId = agent.Key;
                    if (currentStep < agent.Value.Count)
                    {
                        Point position = agent.Value[currentStep];

                        // 坐标系变换:绘图 Y = 地图高度 - 路径 Y - 1
                        int x = position.X;
                        int y = mapData.Count - position.Y - 1;

                        if (x < 0 || x >= mapData[0].Count || y < 0 || y >= mapData.Count)
                        {
                            Console.WriteLine($"Agent {agentId} has out-of-bound path: ({x}, {y})");
                            continue;
                        }

                        Brush brush = new SolidBrush(Color.FromArgb((agentId * 50) % 255, (agentId * 80) % 255, (agentId * 110) % 255));
                        g.FillEllipse(brush, x * cellSize, y * cellSize, cellSize, cellSize);
                    }
                }
            }
        }

        private void CreateMapBitmap()
        {
            int mapWidth = mapData[0].Count * cellSize;
            int mapHeight = mapData.Count * cellSize;

            mapBitmap = new Bitmap(mapWidth, mapHeight);
            using (Graphics g = Graphics.FromImage(mapBitmap))
            {
                for (int row = 0; row < mapData.Count; row++)
                {
                    for (int col = 0; col < mapData[row].Count; col++)
                    {
                        int drawY = mapData.Count - row - 1; // Y 坐标翻转
                        if (mapData[row][col] == '@')
                        {
                            g.FillRectangle(Brushes.Black, col * cellSize, drawY * cellSize, cellSize, cellSize);
                        }
                        else
                        {
                            g.FillRectangle(Brushes.White, col * cellSize, drawY * cellSize, cellSize, cellSize);
                            g.DrawRectangle(Pens.Gray, col * cellSize, drawY * cellSize, cellSize, cellSize);
                        }
                    }
                }
            }
        }

        private void UpdateScaleFactor()
        {
            if (mapData.Count == 0 || mapData[0].Count == 0)
                return;

            float scaleX = pnlDrawing.Width / (float)(mapData[0].Count * cellSize);
            float scaleY = pnlDrawing.Height / (float)(mapData.Count * cellSize);
            scaleFactor = Math.Min(scaleX, scaleY);
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            UpdateScaleFactor();
            pnlDrawing.Invalidate(); // 窗口大小调整后重绘
        }
    }
}

最后生成的界面效果如下

另外,放上500台机器人仿真调度的结果,C#界面做的太小了,就不放了

500台机器人的路径规划

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值