ARM工控机Linux下从CAD图纸到运动加工(一):Qt Demo的CAD导入
EtherCAT总线型运动控制器-ZMC600MZMC600M系列高性能多轴运动控制器是一款EtherCAT总线立式运动控制器控制器本身最多支持6-32轴的复杂的控制需求。ZMC600M是基于“ARM架构纯国产Linux运动控制实时内核MotionRT750 EtherCAT”的总线型运动控制器打破平台壁垒实现了嵌入式系统与运动控制和实时内核技术的无缝衔接。ZMC600M硬件功能特性● 内置Linux系统搭载强实时内核MotionRT750● 可选6-32轴运动控制EtherCAT总线最快通讯周期可达250μs● 24路高速IO输入2路高速锁存3路单端编码器接口● 16路高速IO输出4路PWM/PSO4路单端脉冲轴接口● 2路AD2路DA0-10V12bit● 支持EtherNET、CAN、RS232、RS485通讯● 1路MPG手轮接口● 支持掉电存储● 特殊型号支持5V/24V推挽输出功能● 支持多文件多任务编程可支持PC程序和控制器内置程序同时工作。更多关于ZMC600M详情点击→基于Linux的强实时EtherCAT运动控制器ZMC600M。ZMC600M介绍视频请点击→基于Linux的强实时EtherCAT总线型运动控制器ZMC600M基于linux平台的ZMotionCadEx和Qt/C把CAD文件导入、DXF验证、图形预览、轴参数配置和运动控制流程串成一个可演示、可参考复用的小型验证DEMO。一、痛点及工程价值在真实的CAD/CAM或运动控制项目里问题通常不是单点出现的而是出现在一条很长的链路中CAD文件CAD库导入几何数据解析图形预览坐标和参数转换控制器连接运动执行任何一层出现问题最后用户看到的可能是“这个图纸跑不了”“导入以后没图形”“图形看起来对但设备动作不对”“控制器能连上但加工效果异常”所以这个demo的目标不是做一个简单的文件打开工具而是把“CAD文件→图形预览→参数配置→控制器执行”这条链路串起来方便开发、联调、演示和现场排障。二、CAD图形的校验优势这个Demo主要用来验证从图纸到设备执行之间的关键环节。对开发来说它可以验证CAD导入逻辑和图形数据处理是否正确。对排障来说它可以帮助判断问题到底出在文件、导入库、参数还是设备层。对演示来说它可以比较完整地展示从CAD文件到运动控制执行的过程。三、Demo已实现的功能模块当前版本已实现的核心功能如下支持DXF/PLT/AI/EPS等CAD文件导入。基于正运动ZMotionCadEx库解析CAD文件。支持导入后的图形实时预览。支持X/Y双轴脉冲当量、最大速度、最大加速度、回零速度配置。支持控制器IP连接。支持回零、开始加工、暂停、停止等运动控制操作。支持系统日志输出方便查看导入、连接和运动控制过程。支持加工用红线覆盖显示加工进度。额外加入了一个基于dxflib的DXF验证器用来排查导图失败问题。四、Demo的实现流程CAD导入核心逻辑代码如下CadData CadImporter::importFile(const QString filePath, double unit, double precision) { CadData result; result.success false; // option 0强制将实体转换为线段便于后续预览和运动控制 int option 0; // 原始 precision 0.1 时部分 SPLINE 曲线转换效果不稳定 // 调整到 0.01提高曲线离散和导入成功率 double adjustedPrecision 0.01; Struct_ZCad_VectGroup* vectGroup ZMotionCad3_ImportVectGraph( filePath.toUtf8().constData(), unit, option, adjustedPrecision, 0 ); if (!vectGroup) { result.errorMessage 导入失败ZMotionCad3_ImportVectGraph 返回空指针; return result; } double minX 1e9; double minY 1e9; double maxX -1e9; double maxY -1e9; int layerIndex 0; Struct_ZCad_VectGroup* currentGroup vectGroup; while (currentGroup) { layerIndex; Struct_ZCad_Vect* vect currentGroup-m_pVect; while (vect) { CadEntity entity; entity.layer layerIndex; entity.color currentGroup-m_ColorPen; entity.isClosed vect-IfCloseLine; // 优先读取转换后的线段数据 Struct_ZCad_Seg* firstSeg vect-m_pLineSeg; // 如果转换后的线段为空则 fallback 到原始线数据 if (!firstSeg) { firstSeg vect-m_pLine; } if (firstSeg) { Struct_ZCad_Seg* seg firstSeg; int segCount 0; do { QPointF point(seg-x1, seg-y1); entity.points.append(point); minX qMin(minX, seg-x1); minY qMin(minY, seg-y1); maxX qMax(maxX, seg-x1); maxY qMax(maxY, seg-y1); seg seg-m_pnext; segCount; // 防止链表异常导致死循环 if (segCount 10000) { break; } } while (seg seg ! firstSeg); result.entities.append(entity); } Struct_ZCad_Vect* nextVect (Struct_ZCad_Vect*)vect-m_pnext; if (nextVect currentGroup-m_pVect) { break; } vect nextVect; } Struct_ZCad_VectGroup* nextGroup (Struct_ZCad_VectGroup*)currentGroup-m_pnext; if (nextGroup vectGroup) { break; } currentGroup nextGroup; } if (!result.entities.isEmpty()) { result.boundingRect QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); } ZMotionCad3_DeleteChain((Struct_ZCad_Item*)vectGroup); result.success true; return result; }这里的关键点有两个1.导入时使用option0强制将CAD实体转换为线段方便后续预览和运动控制。②读取数据时不只检查m_pLineSeg还有fallback检查m_pLine避免库已经返回了原始线数据但业务代码没有读取到。这也是后面解决SPLINE样条曲线导入为空问题的关键。CAD文件不是简单“打开一下”而是要经过导入库解析、图层遍历、矢量对象读取、坐标提取最后才能进入图形预览和运动控制流程。五、DXF验证流程在实际调试中经常会遇到一种情况CAD软件里能正常打开的DXF文件导入程序后实体数却是0。这时候不能直接判断文件坏了也不能直接判断CAD库有问题。更合理的做法是先把问题拆开文件里到底有没有几何实体实体类型是什么是LINE、CIRCLE、ARC、LWPOLYLINE还是SPLINE是文件格式问题还是导入参数问题是CAD库没有解析出来还是业务代码没有读取到所以这个demo里额外加了一个基于dxflib的第三方验证器。它不参与正式加工流程只负责独立检查DXF文件内容。这样可以在正运动CAD库之外提供一个对照结果。验证器可以帮助确认文件是否能打开。是否包含有效几何实体。各类实体数量是多少。图形边界范围是否合理。文件是否可能是格式或实体类型问题。1.脚本里的关键检查逻辑。DXF_FILE$1 if [ ! -f $DXF_FILE ]; then echo 错误: 文件不存在: $DXF_FILE exit 1 fi if grep -q ENTITIES $DXF_FILE; then echo 找到 ENTITIES 段 else echo 未找到 ENTITIES 段文件可能不包含几何实体 fi echo 实体类型统计 for entity_type in LINE CIRCLE ARC LWPOLYLINE SPLINE ELLIPSE TEXT DIMENSION; do count$(grep -A1 ^0$ $DXF_FILE | grep -c ^${entity_type}$) if [ $count -gt 0 ]; then printf %-15s %d\n $entity_type: $count fi done2.Qt验证器入口。#include #include #include dxflibverifier.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); if (argc 2) { qDebug() 用法: ./test_verifier ; return 1; } QString filePath QString::fromLocal8Bit(argv[1]); DxflibVerifier verifier; DxfVerifyResult result verifier.verifyFile(filePath); if (result.success result.entities.size() 0) { qDebug() 验证成功找到 result.entities.size() 个实体; return 0; } qDebug() 验证失败未找到有效几何实体; return 1; }3.验证结果数据结构。struct DxfEntity { QVector points; bool isClosed; QString layer; // 0LINE, 1CIRCLE, 2ARC, 3LWPOLYLINE, 4SPLINE int entityType; }; struct DxfVerifyResult { bool success; QString errorMessage; QVector entities; int totalLines; int totalCircles; int totalArcs; int totalPolylines; int totalSplines; double minX; double minY; double maxX; double maxY; }; DxfVerifyResult DxflibVerifier::verifyFile(const QString filePath) { DxfVerifyResult result; if (!parseDxfFile(filePath, result)) { result.success false; result.errorMessage 解析失败或文件不存在; return result; } result.success true; printSummary(result); return result; }4.解析DXF实体类型。bool DxflibVerifier::parseDxfFile(const QString filePath, DxfVerifyResult result) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } QTextStream in(file); in.setCodec(UTF-8); QStringList allCodes; QStringList allValues; // DXF 文本格式通常是“组码 值”成对出现 while (!in.atEnd()) { QString codeLine in.readLine().trimmed(); if (in.atEnd()) break; QString valueLine in.readLine().trimmed(); if (!codeLine.isEmpty() !valueLine.isEmpty()) { allCodes.append(codeLine); allValues.append(valueLine); } } file.close(); if (allCodes.isEmpty()) { return false; } for (int i 0; i allCodes.size(); i) { if (allCodes[i] ! 0) { continue; } QString entityType allValues[i].toUpper(); if (entityType LINE) { parseLine(allCodes.mid(i), allValues.mid(i), result); } else if (entityType CIRCLE) { parseCircle(allCodes.mid(i), allValues.mid(i), result); } else if (entityType ARC) { parseArc(allCodes.mid(i), allValues.mid(i), result); } else if (entityType LWPOLYLINE) { parseLWPolyline(allCodes.mid(i), allValues.mid(i), result); } else if (entityType SPLINE) { parseSpline(allCodes.mid(i), allValues.mid(i), result); } } return true; }六、这个Demo如何帮助排障加入DXF验证之后整个排障路径会清晰很多。如果第三方验证器能读到实体但正运动CAD库导入为空说明文件本身大概率不是空的。这时候应该重点检查导入参数是否合适。精度设置是否过粗。是否存在 SPLINE、ELLIPSE等复杂实体。是否需要将曲线转换成线段。程序是否漏读了某些返回字段。如果第三方验证器也读不到实体那排查方向就应该回到DXF文件本身。比如文件是否为二进制DXF。是否只有BLOCK定义但没有实际插入。是否只有文字、标注等非几何实体。是否需要另存为ASCII DXF R12/R14。是否需要在CAD软件里先执行炸开或转换。如果图形预览正常但设备执行异常那问题就不应该继续纠结在文件层。这时候应该看X/Y轴脉冲当量是否正确。最大速度和加速度是否合理。回零速度是否匹配当前设备。进给速度和主轴转速是否符合工艺。控制器IP和连接状态是否正常。运动控制指令是否正确执行检查函数返回值是否为零。这样一来原本模糊的“跑不了”就可以被拆成更明确的问题类型。教学视频ARM工控机Linux下从CAD图纸到运动加工(一)