AGV调度系统开发实战用C#和SQL Server手把手搭建简易多车调度Demo在智能制造和物流自动化领域AGV自动导引车和AMR自主移动机器人正成为工厂和仓库中不可或缺的搬运工。但要让这些智能小车高效协同工作背后离不开强大的调度系统。本文将带你从零开始用C#和SQL Server构建一个简易但功能完整的AGV调度系统Demo。1. 系统架构设计与环境准备一个典型的AGV调度系统通常包含以下几个核心模块通信模块负责与AGV小车进行实时数据交换任务管理模块处理任务创建、分配和状态跟踪交通管制模块防止多车碰撞和死锁数据库模块持久化存储任务和车辆状态信息1.1 开发环境配置首先确保你的开发环境已安装以下组件# 必需软件 - Visual Studio 2019/2022 (社区版即可) - .NET 6.0 SDK - SQL Server 2019 Express - SQL Server Management Studio (SSMS)对于这个Demo我们将使用以下技术栈技术组件用途替代方案C#/.NET 6后端逻辑开发Java, PythonSQL Server数据存储MySQL, PostgreSQLSocket编程AGV通信模拟gRPC, REST APIWinForms简易UI展示WPF, ASP.NET提示虽然实际工业环境中常用WPF或Web界面但为简化Demo我们使用WinForms快速实现可视化。2. 数据库设计与实现数据库是调度系统的记忆中枢需要精心设计以支持高效查询和状态跟踪。2.1 数据表结构创建以下三个核心表-- AGV车辆表 CREATE TABLE AGVVehicles ( VehicleID INT PRIMARY KEY, VehicleName NVARCHAR(50), CurrentX INT, CurrentY INT, BatteryLevel INT, Status NVARCHAR(20), -- IDLE, MOVING, CHARGING, ERROR LastHeartbeat DATETIME ); -- 任务表 CREATE TABLE Tasks ( TaskID INT PRIMARY KEY IDENTITY, StartX INT, StartY INT, TargetX INT, TargetY INT, Priority INT DEFAULT 1, Status NVARCHAR(20), -- PENDING, ASSIGNED, COMPLETED, FAILED AssignedVehicleID INT NULL FOREIGN KEY REFERENCES AGVVehicles(VehicleID), CreatedTime DATETIME DEFAULT GETDATE(), CompletedTime DATETIME NULL ); -- 地图障碍表 CREATE TABLE MapObstacles ( ObstacleID INT PRIMARY KEY IDENTITY, X INT, Y INT, Type NVARCHAR(20) -- STATIC, TEMPORARY );2.2 数据库访问层使用Dapper作为轻量级ORM工具public class DatabaseService { private readonly string _connectionString; public DatabaseService(string connectionString) { _connectionString connectionString; } public IEnumerableAGVVehicle GetAllVehicles() { using var connection new SqlConnection(_connectionString); return connection.QueryAGVVehicle(SELECT * FROM AGVVehicles); } public int CreateTask(Task task) { using var connection new SqlConnection(_connectionString); return connection.ExecuteScalarint( INSERT INTO Tasks (StartX, StartY, TargetX, TargetY, Priority, Status) VALUES (StartX, StartY, TargetX, TargetY, Priority, PENDING); SELECT CAST(SCOPE_IDENTITY() as int), task); } // 其他数据库操作方法... }3. AGV通信模拟与任务调度3.1 Socket通信基础我们用Socket模拟AGV与调度系统的通信public class AGVSimulator { private readonly Socket _socket; public AGVSimulator(string ip, int port) { _socket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port)); } public void SendStatusUpdate(int vehicleId, int x, int y, int battery, string status) { var message $STATUS|{vehicleId}|{x}|{y}|{battery}|{status}; var bytes Encoding.ASCII.GetBytes(message); _socket.Send(bytes); } public void ReceiveCommand() { var buffer new byte[1024]; var bytesRead _socket.Receive(buffer); var command Encoding.ASCII.GetString(buffer, 0, bytesRead); ProcessCommand(command); } private void ProcessCommand(string command) { var parts command.Split(|); switch(parts[0]) { case MOVE: // 处理移动指令 break; case CHARGE: // 处理充电指令 break; // 其他命令处理... } } }3.2 简单调度算法实现基于最短路径和车辆可用性进行任务分配public class TaskScheduler { private readonly DatabaseService _dbService; public TaskScheduler(DatabaseService dbService) { _dbService dbService; } public void AssignTasks() { var pendingTasks _dbService.GetPendingTasks(); var availableVehicles _dbService.GetAvailableVehicles(); foreach(var task in pendingTasks) { var bestVehicle FindBestVehicleForTask(task, availableVehicles); if(bestVehicle ! null) { _dbService.AssignTaskToVehicle(task.TaskID, bestVehicle.VehicleID); SendMoveCommand(bestVehicle, task); availableVehicles.Remove(bestVehicle); } } } private AGVVehicle FindBestVehicleForTask(Task task, ListAGVVehicle vehicles) { AGVVehicle bestVehicle null; double minDistance double.MaxValue; foreach(var vehicle in vehicles) { var distance CalculateDistance(vehicle.CurrentX, vehicle.CurrentY, task.StartX, task.StartY); if(distance minDistance) { minDistance distance; bestVehicle vehicle; } } return bestVehicle; } private double CalculateDistance(int x1, int y1, int x2, int y2) { return Math.Sqrt(Math.Pow(x2 - x1, 2) Math.Pow(y2 - y1, 2)); } }4. 交通管制与死锁预防多AGV协同工作时交通管制是确保安全运行的关键。4.1 简单交通管制实现public class TrafficController { private readonly object _lock new object(); private readonly Dictionary(int, int), int _occupiedCells new Dictionary(int, int), int(); public bool RequestPath(int vehicleId, List(int x, int y) path) { lock(_lock) { foreach(var cell in path) { if(_occupiedCells.ContainsKey(cell) _occupiedCells[cell] ! vehicleId) { return false; // 路径被占用 } } // 锁定路径 foreach(var cell in path) { _occupiedCells[cell] vehicleId; } return true; } } public void ReleasePath(int vehicleId, List(int x, int y) path) { lock(_lock) { foreach(var cell in path) { if(_occupiedCells.TryGetValue(cell, out var occupyingVehicle) occupyingVehicle vehicleId) { _occupiedCells.Remove(cell); } } } } }4.2 死锁检测与恢复实现一个简单的死锁检测算法public class DeadlockDetector { public bool CheckForDeadlock(ListAGVVehicle vehicles) { // 构建等待图 var graph new Dictionaryint, Listint(); foreach(var vehicle in vehicles) { if(vehicle.WaitingForCells.Any()) { foreach(var cell in vehicle.WaitingForCells) { var occupyingVehicle GetVehicleOccupyingCell(cell); if(occupyingVehicle ! null) { if(!graph.ContainsKey(vehicle.VehicleID)) graph[vehicle.VehicleID] new Listint(); graph[vehicle.VehicleID].Add(occupyingVehicle.VehicleID); } } } } // 检测图中是否有环 return HasCycle(graph); } private bool HasCycle(Dictionaryint, Listint graph) { var visited new HashSetint(); var recursionStack new HashSetint(); foreach(var node in graph.Keys) { if(DetectCycle(node, graph, visited, recursionStack)) return true; } return false; } private bool DetectCycle(int node, Dictionaryint, Listint graph, HashSetint visited, HashSetint recursionStack) { if(recursionStack.Contains(node)) return true; if(visited.Contains(node)) return false; visited.Add(node); recursionStack.Add(node); if(graph.ContainsKey(node)) { foreach(var neighbor in graph[node]) { if(DetectCycle(neighbor, graph, visited, recursionStack)) return true; } } recursionStack.Remove(node); return false; } }5. 系统集成与可视化5.1 WinForms可视化界面创建一个简单的监控界面public class MainForm : Form { private readonly DatabaseService _dbService; private readonly Timer _refreshTimer; private PictureBox _mapBox; public MainForm(DatabaseService dbService) { _dbService dbService; // 初始化UI组件 InitializeComponents(); // 设置定时刷新 _refreshTimer new Timer { Interval 1000 }; _refreshTimer.Tick RefreshData; _refreshTimer.Start(); } private void InitializeComponents() { _mapBox new PictureBox { Dock DockStyle.Fill, BackColor Color.White }; this.Controls.Add(_mapBox); this.Size new Size(800, 600); this.Text AGV调度系统监控; } private void RefreshData(object sender, EventArgs e) { var vehicles _dbService.GetAllVehicles(); var tasks _dbService.GetAllTasks(); using(var g _mapBox.CreateGraphics()) { g.Clear(Color.White); // 绘制地图网格 for(int x 0; x 20; x) { for(int y 0; y 15; y) { g.DrawRectangle(Pens.LightGray, x * 40, y * 40, 40, 40); } } // 绘制AGV车辆 foreach(var vehicle in vehicles) { var brush vehicle.Status MOVING ? Brushes.Green : vehicle.Status IDLE ? Brushes.Blue : Brushes.Red; g.FillEllipse(brush, vehicle.CurrentX * 40 10, vehicle.CurrentY * 40 10, 20, 20); g.DrawString(vehicle.VehicleID.ToString(), this.Font, Brushes.White, vehicle.CurrentX * 40 15, vehicle.CurrentY * 40 15); } // 绘制任务 foreach(var task in tasks.Where(t t.Status ! COMPLETED)) { g.DrawRectangle(Pens.Red, task.StartX * 40 15, task.StartY * 40 15, 10, 10); g.DrawRectangle(Pens.Green, task.TargetX * 40 15, task.TargetY * 40 15, 10, 10); g.DrawLine(Pens.Blue, task.StartX * 40 20, task.StartY * 40 20, task.TargetX * 40 20, task.TargetY * 40 20); } } } }5.2 系统集成与测试创建主程序入口public class Program { public static void Main() { var dbService new DatabaseService(YourConnectionString); var scheduler new TaskScheduler(dbService); var trafficController new TrafficController(); // 启动Socket服务器 var server new AGVServer(8080); server.Start(); // 启动UI Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm(dbService)); } }6. 性能优化与扩展思路6.1 性能优化技巧数据库优化为常用查询字段添加索引使用存储过程处理复杂操作考虑引入缓存层减少数据库访问调度算法改进实现基于优先级的任务队列考虑电池电量因素在调度决策中引入预测性调度基于历史数据预测任务到达通信优化使用二进制协议替代文本协议减少带宽实现心跳机制检测离线AGV考虑使用UDP协议实现广播通信6.2 系统扩展方向多楼层支持扩展地图模型支持Z轴坐标动态路径规划集成A*或Dijkstra算法实现最优路径充电调度自动调度低电量AGV前往充电站故障恢复实现任务重新分配和路径重规划Web监控界面改用ASP.NET Core实现远程监控注意在实际工业环境中还需要考虑网络稳定性、异常处理、安全认证等更多因素。