SMT 贴片机上位机项目
SMT 贴片机上位机项目场景,针对面试问题进行代码层面的分析与整理,聚焦核心代码框架及设计思路:
一、上位机软件架构设计(对应问题 1)
1. 分层架构代码框架
// 1. 硬件抽象层(Hardware Abstraction Layer)
// 运动控制卡封装
public interface IMotionController
{
bool Initialize();
void MoveTo(int axis, double position, double speed); // 单轴运动
void SyncMove(List<Tuple<int, double>> axisPositions, double speed); // 多轴同步运动
double GetCurrentPosition(int axis);
event Action<string> ErrorOccurred; // 错误事件
}
public class GTSMotionController : IMotionController
{
// 封装固高GTS控制卡DLL调用
[DllImport("GTS.dll")]
private static extern int GTS_Init();
public bool Initialize()
{
return GTS_Init() == 0; // 调用底层初始化函数
}
public void MoveTo(int axis, double position, double speed)
{
// 转换物理位置为脉冲(基于脉冲当量计算)
int pulses = (int)(position / _pulseEquivalent);
GTS_Move(axis, pulses, (int)speed); // 调用运动函数
}
// 其他实现...
}
// 视觉库封装
public interface IVisionProcessor
{
(double x, double y, double angle) LocateMark(string imagePath); // 定位Mark点
void SetCameraParam(int exposure, int gain);
}
public class HalconVisionProcessor : IVisionProcessor
{
// 封装Halcon函数
public (double x, double y, double angle) LocateMark(string imagePath)
{
HOperatorSet.ReadImage(out HObject image, imagePath);
// 调用Halcon定位算子(阈值分割、模板匹配等)
HOperatorSet.FindShapeModel(...);
return (x, y, angle); // 返回定位结果
}
}
// 2. 业务逻辑层(Business Logic Layer)
public class MountingService
{
private readonly IMotionController _motionController;
private readonly IVisionProcessor _visionProcessor;
private readonly IRecipeRepository _recipeRepo;
// 依赖注入(解耦硬件与业务逻辑)
public MountingService(IMotionController motion, IVisionProcessor vision, IRecipeRepository repo)
{
_motionController = motion;
_visionProcessor = vision;
_recipeRepo = repo;
}
// 核心业务:执行贴装流程
public async Task ExecuteMounting(string recipeId)
{
var recipe = await _recipeRepo.GetRecipeAsync(recipeId); // 获取贴装配方
await CalibrateVision(); // 视觉校准(九点标定)
foreach (var component in recipe.Components)
{
// 1. 移动到取料位置
await _motionController.MoveToAsync(0, component.PickX, component.PickY);
// 2. 视觉定位元件
var (x, y, angle) = _visionProcessor.LocateMark(component.ImagePath);
// 3. 补偿位置后贴装
await _motionController.MoveToAsync(0, component.PlaceX + x, component.PlaceY + y);
// 4. 控制真空阀贴装
await _ioService.SetOutputAsync("Vacuum", false);
}
}
}
// 3. 表示层(MVVM模式)
public class MountingViewModel : BindableBase // Prism的BindableBase实现INotifyPropertyChanged
{
private readonly MountingService _mountingService;
private string _currentStatus;
public string CurrentStatus
{
get => _currentStatus;
set => SetProperty(ref _currentStatus, value);
}
public DelegateCommand<string> StartMountingCommand { get; }
public MountingViewModel(MountingService service)
{
_mountingService = service;
StartMountingCommand = new DelegateCommand<string>(async (recipeId) =>
{
CurrentStatus = "开始贴装...";
await _mountingService.ExecuteMounting(recipeId); // 调用业务层
CurrentStatus = "贴装完成";
});
}
}
2. 采用 MVVM / 分层架构的好处
-
解耦性 :硬件抽象层隔离了运动控制卡、视觉库的具体实现,更换硬件时只需修改对应实现类(如从固高卡换成雷塞卡,只需新增
LeisaiMotionController实现IMotionController)。 -
可维护性 :业务逻辑集中在服务类,避免与 UI 代码混杂,例如贴装流程的修改只需调整
MountingService,不影响 UI。 -
可测试性 :通过接口注入,可使用模拟对象(Moq)进行单元测试(如用
Mock<IMotionController>模拟运动控制,无需真实硬件)。 -
多团队协作:UI 团队专注于 XAML 和 ViewModel,控制团队专注于硬件抽象和业务逻辑,并行开发。
二、与硬件的交互方式(对应问题 2)
-
运动控制卡交互 :通过DLL 导入(P/Invoke) 调用底层驱动
-
固高 GTS 控制卡提供 C 语言 DLL,通过
[DllImport]声明导入,封装成 C# 接口(如IMotionController),避免直接在业务层调用非托管代码。 -
关键代码见上文
GTSMotionController,核心是脉冲换算 (物理位置→脉冲数)和异步封装 (将同步 DLL 调用转为Task,避免阻塞 UI)。
-
-
视觉库交互 :通过Halcon .NET 封装库 (如
halcondotnet.dll)-
直接引用 Halcon 的 C# 类库,封装
IVisionProcessor接口,隐藏复杂的视觉算法细节,业务层只需调用LocateMark等方法。 -
示例:定位 Mark 点时,先通过相机 SDK 获取图像,再传入视觉处理器进行处理,返回坐标补偿值。
-
三、上位机与 PLC 的分工与通信(对应问题 3)
1. 分工
-
上位机:负责高精度运动控制(贴装头定位)、视觉定位、配方管理、生产调度、数据统计。
-
PLC:负责辅助机构控制(传送带启停、顶针升降、安全门检测)、I/O 信号采集(传感器状态)、紧急停止处理。
2. 通信实现(TCP/IP 协议)
public class PlcCommunicator
{
private TcpClient _client;
private NetworkStream _stream;
public async Task Connect(string ip, int port)
{
_client = new TcpClient();
await _client.ConnectAsync(ip, port);
_stream = _client.GetStream();
}
// 发送指令:例如控制传送带启动
public async Task SendCommand(PlcCommand command)
{
byte[] data = PlcProtocol.Encode(command); // 按自定义协议编码(如地址+指令+校验)
await _stream.WriteAsync(data, 0, data.Length);
}
// 接收PLC状态:例如顶针到位信号
public async Task<PlcStatus> ReceiveStatus()
{
byte[] buffer = new byte[1024];
int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length);
return PlcProtocol.Decode<PlcStatus>(buffer, bytesRead);
}
}
// 业务层调用
public async Task StartConveyor()
{
await _plcCommunicator.SendCommand(new PlcCommand { Address = 0x01, Command = "ConveyorStart" });
// 等待PLC反馈启动成功
var status = await _plcCommunicator.ReceiveStatus();
if (!status.ConveyorRunning)
throw new Exception("传送带启动失败");
}
四、多线程设计(对应问题 4)
采用线程池 + Task 调度,按功能划分线程:
-
UI 线程:负责界面渲染(WPF 主线程),仅处理 UI 更新,不执行耗时操作。
-
运动控制线程
:通过Task.Run
在后台线程执行运动指令,确保实时性(避免被 UI 阻塞)。
// 运动服务中的异步封装 public async Task MoveToAsync(int axis, double position, double speed) { // 在线程池线程中执行同步运动函数 await Task.Run(() => _motionController.MoveTo(axis, position, speed)); } -
视觉处理线程
:独立Task
处理图像识别(CPU 密集型),避免占用运动控制线程。
public async Task<(double x, double y)> LocateAsync(string imagePath) { return await Task.Run(() => _visionProcessor.LocateMark(imagePath)); } -
通信线程 :专用
Task循环接收 PLC / 设备数据(await _stream.ReadAsync),通过ConcurrentQueue缓存数据,避免阻塞。 -
日志线程:单线程消费日志队列,异步写入数据库 / 文件(避免高频日志 IO 阻塞业务)。
线程安全保障:
-
UI 更新:通过
Dispatcher(WPF)或BeginInvoke切换到 UI 线程。 -
共享数据:使用
ConcurrentDictionary、lock或SemaphoreSlim保护临界资源(如当前设备状态)。
五、数据设计与数据库(对应问题 5)
1. 数据库选型:SQL Server(生产数据)+ SQLite(本地配方)
2. 核心表结构
-- 配方表(存储贴装参数)
CREATE TABLE Recipes (
RecipeId INT PRIMARY KEY,
ProductName NVARCHAR(100),
CreatedTime DATETIME,
LastModifiedTime DATETIME
);
-- 元件表(每个配方包含多个元件)
CREATE TABLE Components (
ComponentId INT PRIMARY KEY,
RecipeId INT FOREIGN KEY REFERENCES Recipes(RecipeId),
PartNumber NVARCHAR(50), -- 元件型号
PickX FLOAT, -- 取料X坐标
PickY FLOAT, -- 取料Y坐标
PlaceX FLOAT, -- 贴装X坐标
PlaceY FLOAT, -- 贴装Y坐标
VisionTemplatePath NVARCHAR(200) -- 视觉模板路径
);
-- 生产记录表
CREATE TABLE ProductionLogs (
LogId INT PRIMARY KEY,
RecipeId INT,
StartTime DATETIME,
EndTime DATETIME,
TotalCount INT, -- 总贴装数
GoodCount INT, -- 良品数
ErrorInfo NVARCHAR(MAX)
);
3. 数据访问层实现(使用 EF Core)
public class RecipeRepository : IRecipeRepository
{
private readonly AppDbContext _dbContext;
public async Task<Recipe> GetRecipeAsync(string recipeId)
{
return await _dbContext.Recipes
.Include(r => r.Components)
.FirstOrDefaultAsync(r => r.RecipeId == recipeId);
}
public async Task SaveProductionLogAsync(ProductionLog log)
{
_dbContext.ProductionLogs.Add(log);
await _dbContext.SaveChangesAsync();
}
}
六、棘手问题及解决方案(对应问题 6)
问题:高速贴装时视觉定位延迟导致贴装偏移
-
现象:当贴装速度超过 20,000 CPH 时,视觉定位结果滞后于运动控制,导致元件贴装位置偏移。
-
分析:视觉处理(模板匹配)耗时约 50ms,而运动控制周期仅 30ms,两者不同步。
-
解决方案:
-
预缓存定位结果
:在贴装前提前对下一元件进行视觉定位,缓存结果(使用ConcurrentQueue)
// 预加载线程 private async Task PreloadVisionResults(Recipe recipe) { foreach (var comp in recipe.Components) { var result = await _visionProcessor.LocateMark(comp.ImagePath); _visionResultQueue.Enqueue(result); // 缓存到队列 } } -
运动与视觉并行处理
:使用Task.WhenAll
同时执行当前元件贴装和下一元件视觉定位。
// 并行处理 var currentMountTask = _motionController.MoveToAsync(placeX, placeY); var nextVisionTask = _visionProcessor.LocateMark(nextComp.ImagePath); await Task.WhenAll(currentMountTask, nextVisionTask); // 时完成 -
优化视觉算法 :通过 Halcon 的
CreateShapeModel创建高效模板,减少匹配区域(ROI),将处理时间压缩至 20ms 以内。
-
-
结果:贴装速度提升至 25,000 CPH 时仍保持定位精度(±0.02mm)。
以上代码框架和设计思路覆盖了 SMT 贴片机上位机的核心技术点,可直接用于面试中对项目细节的阐述,突出架构设计、多线程、硬件交互等关键能力