国外开源PLC源码拆解:IEC 61131-3梯形图编辑器与编译链路

发布时间:2026/10/26 5:46:12

国外开源PLC源码拆解:IEC 61131-3梯形图编辑器与编译链路 简介这份资源是面向工业自动化开发者与PLC编程学习者的国外开源PLC编译器项目源码目标是为IEC 61131-3标准所定义的五种编程语言提供开源实现涵盖IL、ST、FBD、LD与SFC可用于理解PLC编程语言的解析、编译与执行机制适合具备一定C/C基础、希望深入梯形图与结构化文本底层实现的中高级读者研究参考。压缩包共216个文件约840KB以cc与hh源文件为主体辅以txt说明文档、test测试用例、h头文件及am、m4等构建脚本另有少量sh脚本与readme、todo等工程说明整体呈现典型的开源编译工具链目录结构。目前已有4563人学习下载。读者可从中获取五种IEC 61131-3语言的编译器前端实现思路、词法与语法分析相关源码组织方式以及构建与测试脚本的配置范例便于对照标准理解工业控制语言的编译流程并在此基础上开展二次开发或教学实验。1. 拿到一份国外开源 PLC 源码先别急着编译前阵子有个做产线改造的朋友丢给我一个压缩包说是一套国外开源 PLC 的源码想让我看看能不能把里面的梯形图编辑器抠出来接到他们自己的组态软件里。我打开一看IEC 61131-3 那套东西基本都在梯形图LD、功能块图FBD、结构化文本ST、指令表IL几种语言的编辑器骨架加上编译成中间表示、再往下生成目标代码的链路。这类开源 PLC 项目对工控开发者来说价值很直接——你不用从零啃 IEC 61131-3 那几百页标准直接看别人怎么把标准落成数据结构、怎么把梯形图编译成可执行逻辑。适合两类人一是想理解 PLC 编程环境内部怎么运转的工控软件开发者二是想拿现成编辑器做二次开发、省掉自研组态前端的人。下面我按「这项目是什么 → 怎么跑起来 → 核心模块怎么拆 → 怎么改」的顺序把这份源码拆一遍。2. 项目定位与技术栈一套 IEC 61131-3 的完整实现骨架2.1 它到底实现了标准里的哪几块IEC 61131-3 标准管的是 PLC 编程语言的语法和语义一份完整的开源实现通常要覆盖四层语言前端各语言的解析器和编辑器、中间表示把不同语言统一成一种可编译的 IR、代码生成把 IR 变成目标平台能跑的代码或字节码、运行时扫描周期、变量表、I/O 映射。这份源码的目录结构基本就是按这四层切的我拿到手先看顶层目录能立刻判断出它的完成度。常见的顶层结构长这样不同项目命名有差异但职责划分大同小异src/ editors/ # 各语言编辑器前端LD / FBD / ST / IL ld/ # 梯形图梯级、触点、线圈、分支 fbd/ # 功能块图块实例、连线 st/ # 结构化文本词法、语法、AST compiler/ # 前端 AST - 中间表示 ast/ ir/ codegen/ # IR - 目标代码C / 字节码 / 特定 PLC 指令 runtime/ # 扫描周期、变量内存、标准功能块库 libs/ # IEC 标准功能块TON、CTU、R_TRIG 等看到editors/ld和compiler/ir同时存在说明它不是个纯编辑器玩具而是奔着「能编译能跑」去的。这一点很关键市面上不少所谓开源 PLC 只做了图形编辑器导出个 XML 就完事编译和运行时是空的。判断方法很简单看codegen和runtime目录里有没有实质代码以及libs里有没有实现定时器、计数器这些标准功能块——有才说明它真能跑逻辑。2.2 技术栈与构建方式这类项目主流是 C 打底性能和跨平台编辑器前端可能用 Qt 做图形界面也可能做成 Web 前端TypeScript Canvas/SVG方便嵌入。我这份源码是 C 主体加 CMake 构建编辑器部分用 Qt Widgets 画梯形图的梯级和连线。判断依据是根目录有CMakeLists.txteditors/下有.ui文件或QGraphicsScene相关代码。构建工具链的典型组合组件常见选型作用构建系统CMake 3.16跨平台生成 Makefile / VS 工程编译器GCC 9 / Clang 10 / MSVC 2019C17 是常见底线GUI 框架Qt 5.15 或 Qt 6梯形图、功能块图的可视化编辑脚本/测试Python 3 pytest 或 CTest编译器前端和运行时的单元测试目标代码生成C 代码或自定义字节码决定最终在哪跑如果你的机器上没装 Qt构建会直接卡在find_package(Qt5 ...)那一步这是新手第一个翻车点后面避坑章节会细说。技术栈这块我的建议是先只构建compiler和runtime两个子目标把编辑器放一边这样能最快验证「这套源码的编译链路是不是通的」不被 GUI 依赖拖住。2.3 和商业 PLC 软件的关系得说清楚一点开源 PLC 源码不等于能直接替代商业 PLC 编程软件。商业软件各家都有自己的强在设备驱动、在线监控、仿真调试、认证合规这一整套生态。开源项目的价值在于「标准怎么落地」这件事本身——它的 AST 设计、IR 设计、功能块库实现都是可以直接借鉴的工程范本。我一般把它当「参考实现 可裁剪的组件库」用而不是当成品软件用。想清楚这个定位后面改造时就不会纠结「为什么它没有某某商业功能」。3. 跑起来从零构建到第一个梯形图程序3.1 环境准备与依赖安装先确认编译器版本。C17 是这类项目的常见要求GCC 低于 9 会在std::filesystem或结构化绑定上报错。Ubuntu 系一条命令装齐基础依赖# 基础构建工具与 C17 编译器 sudo apt update sudo apt install -y build-essential cmake git # Qt 图形依赖编辑器前端需要只跑编译器可跳过 sudo apt install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools # 确认版本 cmake --version # 需要 3.16 以上 g --version # 需要 9 以上参数说明qtbase5-dev提供 Qt5 的核心开发头文件和库qtchooser用来在多个 Qt 版本间切换。如果你系统里同时有 Qt5 和 Qt6构建时要用-DCMAKE_PREFIX_PATH明确指向其中一个否则 CMake 可能找到错的那个链接阶段报一堆 undefined reference。这一步我踩过症状是编译全过、链接全挂看着像代码问题其实是环境问题。3.2 CMake 构建与常见开关依赖齐了之后标准流程是建独立构建目录、跑 CMake、再编译。不要在原目录里直接cmake .会把生成文件混进源码树后面清理很麻烦。# 在源码根目录外建构建目录 mkdir -p build cd build # 配置指定安装前缀关闭不需要的组件 cmake .. \ -DCMAKE_BUILD_TYPERelease \ -DCMAKE_INSTALL_PREFIX/usr/local \ -DBUILD_EDITORON \ -DBUILD_TESTSON # 并行编译-j 后面跟 CPU 核数 cmake --build . -j$(nproc) # 跑单元测试验证编译器和运行时 ctest --output-on-failure参数说明CMAKE_BUILD_TYPERelease开优化调试编译器前端时改成Debug并加-DCMAKE_CXX_FLAGS-g -O0BUILD_EDITOR控制是否编译 Qt 图形部分只想验证编译链路就设OFF能省掉一大半依赖BUILD_TESTS打开后ctest才有东西可跑。-j$(nproc)用满所有核编译大型 C 项目时能省不少等待时间。如果ctest里有失败项先别慌多半是测试用例依赖了特定路径或环境变量看--output-on-failure打出来的具体断言。3.3 写第一个梯形图并编译构建成功后一般会产出一个命令行工具比如plcc之类和一个 GUI 程序。先用命令行验证编译链路写一个最简单的梯形图程序一个常开触点串一个线圈对应「输入 I0.0 直接驱动输出 Q0.0」。梯形图在源码里通常用 XML 或自定义文本格式描述常见结构是「梯级rung→ 元素element→ 连接connection」。一个最小示例格式因项目而异看editors/ld下的解析器实现!-- 单梯级触点 I0.0 驱动线圈 Q0.0 -- program namemain rung contact typeNO variableI0.0 / coil typeOUT variableQ0.0 / /rung /program逻辑说明contact typeNO是常开触点variable指向输入变量coil typeOUT是普通输出线圈。编译时前端把这段 XML 解析成 AST再转成 IR 里的「读变量 → 条件跳转 → 写变量」序列。参数上要注意变量命名必须符合项目的变量表约定I/Q/M前缀分别对应输入、输出、中间变量写错了编译器可能不报错但运行时读不到值——这是梯形图调试里最隐蔽的一类问题。编译命令通常是# 把梯形图源文件编译成目标代码 ./build/bin/plcc compile main.ld -o main.c # 或者直接生成字节码交给运行时执行 ./build/bin/plcc run main.ld如果compile成功打开生成的main.c能看到类似if (I0_0) { Q0_0 1; }的代码说明整条链路是通的。到这一步你就有了一个可以继续往下拆的活体样本。4. 核心模块拆解梯形图从图形到可执行逻辑的链路4.1 梯形图编辑器梯级、触点、线圈的数据模型梯形图编辑器的核心不是画图是「图形元素 ↔ 数据模型」的双向映射。源码里editors/ld下一般有个LdElement基类和Contact、Coil、Branch等子类每个元素持有变量引用和连接点左、右、上、下。图形层用 Qt 的QGraphicsItem承载数据层是纯 C 对象两者通过一个LdDocument同步。关键设计点在于「连接」怎么表达。梯形图的语义是电流从左母线流到右母线中间经过触点和分支所以数据模型里每个元素要有明确的输入/输出端口分支用Branch节点表示并联。我拆这类代码时习惯先找serialize/deserialize函数因为序列化格式最能暴露数据模型的真实结构——哪些字段是必须的、连接关系怎么存一目了然。// 触点元素的数据模型示意字段名以实际源码为准 class Contact : public LdElement { public: enum Type { NormallyOpen, NormallyClosed }; Type type; // 常开 / 常闭 std::string variable; // 绑定的变量名如 I0.0 Port in, out; // 左右连接端口 // 序列化到 XML供保存和编译使用 void serialize(XmlWriter w) override { w.startElement(contact); w.attr(type, type NormallyOpen ? NO : NC); w.attr(variable, variable); w.endElement(); } };逻辑说明type决定编译时生成「条件为真」还是「条件取反」variable是变量表里的键in/out端口在编译阶段被用来做拓扑排序——编译器要按电流流向把元素排成执行顺序端口连接关系就是排序依据。参数上variable建议在编辑器层就做合法性校验是否在变量表中、类型是否匹配否则错误会推迟到编译甚至运行时才暴露排查成本翻倍。4.2 编译器前端AST 到中间表示不同语言LD/FBD/ST/IL最终都要收敛到同一种中间表示这是整个项目架构上最值得看的部分。常见做法是定义一套统一的 IR 指令比如「加载变量、逻辑与、逻辑或、取反、赋值、跳转」各语言前端只负责把自己的语法翻译成这套指令序列。以梯形图为例一个「常开触点 A 串常闭触点 B 驱动线圈 C」的梯级翻译成 IR 大致是LOAD A JMPF end ; A 为假则整条梯级不导通 LOAD B JMPT end ; B 为真则整条梯级不导通常闭取反 STORE C end:这种「跳转式」IR 的好处是贴近最终代码生成坏处是分支多时跳转标签管理复杂。另一派做法是先转成 SSA 形式再做优化代码更干净但实现门槛高。我这份源码用的是前者简单直接适合二次开发时快速加新语言——你只要写个前端把新语言翻译成这套 IR 指令后面的代码生成和运行时完全复用。// IR 指令的典型定义示意 struct Instruction { enum Op { Load, Store, And, Or, Not, Jmp, JmpIfFalse, JmpIfTrue } op; std::string operand; // 变量名或跳转标签 int line; // 源文件行号用于报错定位 };参数说明operand在Load/Store时是变量名在Jmp系列时是标签line保留源行号编译报错时能指回梯形图的具体梯级这个字段在调试时非常有用别省。4.3 代码生成与运行时扫描周期怎么落地代码生成层把 IR 变成目标代码。常见目标是 C 代码再交给目标平台的编译器或自定义字节码交给项目自带的虚拟机。C 代码路线的好处是能直接嵌进嵌入式工程字节码路线的好处是跨平台、能在线热更新。运行时这块PLC 的核心是「扫描周期」读输入 → 执行程序 → 写输出循环往复。源码里runtime目录一般有个ScanCycle或PlcRuntime类维护输入映像区、输出映像区、中间变量区三块内存每个周期开始时把物理输入刷进映像区执行完把映像区刷到物理输出。// 扫描周期主循环示意 void PlcRuntime::run() { while (running) { readInputs(); // 物理输入 - 输入映像区 executeProgram(); // 执行编译后的逻辑读写映像区 writeOutputs(); // 输出映像区 - 物理输出 sleepFor(cycleMs); // 固定周期常见 1~10ms } }逻辑说明readInputs/writeOutputs是 I/O 抽象层实际项目里会对接具体硬件驱动或仿真接口executeProgram跑的是编译产物cycleMs是扫描周期设太小 CPU 占用高设太大响应慢工控场景常见 1~10ms。二次开发时如果要接真实 I/O改的就是readInputs/writeOutputs这两个函数程序逻辑部分不用动——这个分层设计是这套源码最值得学的地方。4.4 标准功能块库TON、CTU 这些怎么实现libs目录里是 IEC 标准功能块的实现定时器 TON、计数器 CTU、边沿检测 R_TRIG 这些。它们本质是有内部状态的函数块每个实例有自己的内存。以 TON接通延时定时器为例输入 IN 为真后开始计时累计到 PT 后输出 Q 置位。// TON 接通延时定时器示意 struct TON { bool IN false; // 输入条件 int PT 0; // 预设时间ms int ET 0; // 已计时 bool Q false; // 输出 bool prevIN false; // 上一周期输入用于检测上升沿 void update(int deltaMs) { if (IN !prevIN) ET 0; // 上升沿复位计时 if (IN) { ET deltaMs; if (ET PT) Q true; } else { ET 0; Q false; // 输入断开即复位 } prevIN IN; } };逻辑说明deltaMs是本次调用距上次的毫秒数由运行时按扫描周期传入prevIN用来检测上升沿保证每次 IN 从假变真时计时从头开始。参数上PT的单位要和deltaMs一致混用秒和毫秒是定时器不准的头号原因。这类功能块的实现看着简单但边界条件输入抖动、周期不均处理得好不好直接决定运行时稳不稳值得逐行读。5. 避坑与排查构建和运行阶段最容易翻车的几处5.1 Qt 版本冲突导致链接失败现象编译全部通过链接阶段报大量undefined reference to QWidget::...之类。原因系统里装了多个 Qt 版本CMake 找到的头文件版本和链接的库版本不一致。解决配置时显式指定-DCMAKE_PREFIX_PATH/usr/lib/x86_64-linux-gnu/cmake/Qt5路径按实际装的位置改或者用qtchooser -list-versions看清有哪些版本再选。我一般会在 CMake 配置输出里搜Qt5_DIR确认它到底用了哪个。5.2 变量表没加载导致运行时全零现象程序编译成功运行时所有输出都是 0逻辑像没执行。原因变量表变量名到内存地址的映射没加载运行时读到的输入全是默认值。解决检查运行时的启动参数有没有指定变量表文件以及变量表里的变量名和梯形图里用的名字是否完全一致大小写、前缀都算。这类问题没有报错只能靠打印变量表内容来定位建议在readInputs里加一行调试输出。5.3 扫描周期设置不当引发逻辑抖动现象定时器计时不准或者输出频繁通断。原因扫描周期设得比逻辑里最短的定时精度还大或者周期不固定用了sleep但没补偿执行耗时。解决把周期设成固定值并用「下次唤醒时间 本次开始时间 周期」的方式调度而不是「执行完再 sleep 周期」后者会累积漂移。工控场景对时间敏感的逻辑周期别超过 10ms。5.4 编辑器保存的格式和编译器期望的不一致现象GUI 里画好的梯形图保存后命令行编译器解析报错。原因编辑器序列化时带了 GUI 专用字段坐标、颜色编译器前端解析器不认。解决看serialize和编译器解析器是不是共用同一套 schema不是的话要么统一 schema要么在编译前做一次格式转换。这个坑在二次开发加新元素时特别容易踩加字段前先确认两边都认。5.5 目标代码生成后变量名被改写现象生成的 C 代码里变量名变成了I0_0这种和源里的I0.0对不上接真实 I/O 时映射错位。原因代码生成器对变量名做了合法化处理点号在 C 里不合法。解决在代码生成层维护一张「源变量名 ↔ 生成变量名」的映射表I/O 绑定时查这张表别靠字符串替换硬猜。这个映射表在调试时还能当变量监视用一举两得。6. 二次开发把编辑器抠出来接进自己的组态软件改造思路我按「最小可用」来推先不碰编译器和运行时只把editors/ld这块抽成一个独立库对外暴露「加载梯形图文件 → 编辑 → 保存」三个接口这样能最快验证它能不能嵌进你的组态前端。具体做法是把LdDocument和图形层一起编成静态库头文件只暴露文档操作接口Qt 的QGraphicsView作为控件嵌进你的窗口。验证方法上我习惯先写一个最小宿主程序一个空窗口中间放梯形图编辑控件菜单里加「打开/保存」跑通后再逐步加变量表联动和编译按钮。这样每一步都有可回退的点不会一上来改一大片然后不知道哪坏了。几个改造时的具体技巧改造点做法注意变量表对接把项目的变量表接口抽象成回调编辑器不直接读文件别让编辑器依赖具体存储格式新增语言元素继承LdElement实现serialize和 IR 翻译两边 schema 要同步改目标平台切换只换codegen子目录IR 和前端不动先跑通一个最小程序再扩在线监控复用变量名映射表周期性读运行时内存映射表要双向可查最后说个我自己的教训。早些年我拿到这类源码总想一口气把编辑器、编译器、运行时全改成自己想要的形态结果改到一半发现前端和 IR 的接口对不上前面全白干。从那以后我每次动这类项目都强制先跑通「一个梯形图 → 编译 → 运行 → 看到输出」这条最小链路再动任何一块改完立刻回归这条链路。这套源码的价值不在它现在有多完整而在它的分层足够清楚你按层改、按链路验就不会翻车。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/26 5:41:06

Windows BLE蓝牙C/C++开发:GATT扫描连接读写与通知订阅

简介:这份面向Windows平台C/C开发者的BLE蓝牙操作工程示例,基于WinRT蓝牙API封装,适合需要为桌面应用加入低功耗蓝牙扫描、设备连接及GATT服务与特征读写、通知订阅能力的初学者和中级开发者参考。压缩包共11个文件,以4个.h头文件…

2026/10/26 5:41:06

基于YOLOv8的遛狗不牵绳检测数据集与实战指南

1. 项目概述:遛狗不牵绳检测数据集解析这个1047张标注好的VOCYOLO格式数据集,是专门针对"遛狗不牵绳"这一城市管理痛点的计算机视觉解决方案。我在实际城市安防项目中发现,传统人工巡查方式存在效率低、覆盖范围有限等问题&#xf…

2026/10/26 5:41:06

MCP协议深度解析:从原理到落地,AI工具集成新范式

最近不管刷技术社区还是看各种AI工具更新,总能看到同一个词:MCP(Model Context Protocol,模型上下文协议)。本来以为又是一个刷屏的概念,但深入用了几周之后,我意识到这东西确实不是空炒热度。它…

2026/10/26 6:41:15

C语言嵌入式无人机飞控固件:架构、姿态解算与串级PID实战

简介:这份C语言嵌入式无人机开源项目源代码面向具备一定单片机基础、希望深入理解飞控系统实现的开发者与学生,可用于课程设计、毕业设计及飞控学习参考。压缩包共1740个文件,约19.23MB,涵盖258个C源文件、278个头文件&#xff0c…

2026/10/26 6:41:15

STM32F4到STM32F1的MPU9250九轴驱动移植与SPI/I2C接口重写

简介:面向嵌入式开发者的 MPU9250 九轴传感器移植例程,重点解决把 STM32F4 上的惯性测量代码迁移到 STM32F1 时遇到的端口映射、中断向量、时钟配置与内存受限等问题,可作为低成本运动检测、姿态解算与导航类项目的参考工程。包内共有 219 个…

2026/10/26 6:41:15

KV-Cache技术解析:大模型推理性能优化的关键

1. 为什么KV-Cache成为大模型推理的标配?在Transformer架构的大模型推理过程中,KV-Cache(Key-Value缓存)技术已经成为性能优化的标准配置。这源于自注意力机制的计算特性——每个解码步骤都需要重复计算之前所有token的Key和Value…

2026/10/26 6:41:15

NLP环境搭建实战:从虚拟环境到版本锁定的可复现配置

1. 先把环境这件事想清楚:整体思路与方案选型自然语言处理这个方向有个很典型的特点:算法论文看得懂、模型结构画得出来,但真正动手的第一天往往卡在环境上。很多人对 NLP 学习的热情就消耗在“装完 A 又缺 B,装完 B 又和 C 打架”…

2026/10/26 6:41:15

NLP学习第一步:从零搭建可复现的Python深度学习环境

1. NLP 的第一课为什么不是模型,而是环境我见过太多人学自然语言处理,第一周兴致勃勃地打开教程,复制粘贴一段情感分类的代码,结果卡在ImportError上一整晚。第二天再打开,昨天能跑的东西今天又报错,于是得出结论:这行不适合我。其实问题从来不在模型,也不在数学,而…

2026/10/26 6:36:15

Ollama本地大模型部署工具安装与使用指南

1. Ollama项目概述Ollama是一款开源的本地大模型部署工具,它让开发者能够在个人电脑上快速运行和测试各种开源大语言模型。不同于需要云端API调用的方案,Ollama提供了轻量级的本地运行环境,特别适合需要数据隐私保护或离线使用的场景。我最初…

2026/10/26 0:20:20

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/26 1:53:47

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/26 0:20:17

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/26 0:00:48

Nested Learning:解决AI灾难性遗忘的分层记忆架构

1. 为什么AI需要"记忆"能力?在传统深度学习模型中,我们常遇到一个根本性问题:模型在处理新任务时,往往会完全遗忘之前学到的知识。这种现象被业界称为"灾难性遗忘"(Catastrophic Forgetting)。想象一下&#…

2026/10/26 0:00:48

企业级大模型应用:OCR预处理、Dify编排与DeepSeek落地实战

1. 这不是“用大模型”,而是重构工作流的起点“大模型的应用和工具”——这八个字现在被贴在无数技术分享、招聘JD、内部立项书的标题栏里,但绝大多数人点进去后看到的,是零散的API调用示例、几个开源UI截图、或者一句“接入LLM就能降本增效”…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑