七天学会 PLC 上位机到机器视觉:利用 Openness 与人工智能生成工程代码

从零打通 TIA Portal Openness:环境配置、连接博图到自动生成 SCL / 梯形图程序(V18 实战)

标签:#TIA Openness #西门子 PLC #S7-1200 #C# 二次开发 #PLC 自动编程 #博图自动化

前言
如果觉得看文档不直观,可以bibi查看配套视频,操作演示

当下工业自动化、机器视觉、AI 融合开发已成主流趋势,很多开发者希望打通上位机、PLC、人工智能全链路。西门子 TIA Portal 作为工业领域主流编程软件,手动编写 PLC 程序效率低、重复性工作多。

本文基于 TIA Portal V18 + S7-1200 + PLCSIM 仿真 实战,手把手教你使用 C# 结合 TIA Portal Openness 官方.NET API,实现自动连接博图、离线切换、批量生成 SCL / 梯形图代码。全文代码全部实测可用,汇总海量踩坑问题,不管是 PLC 上位机开发、自动化工程批量部署,还是结合 AI 自动生成 PLC 工程代码,都能直接拿来落地,也是从 PLC 开发进阶到机器视觉 + AI 融合项目的必备技能。

一、什么是 Openness?纠正 90% 新手的认知误区

TIA Portal Openness 是西门子官方推出的 .NET 接口套件,核心作用是用代码自动化操控博图(TIA Portal)软件,支持新建项目、添加硬件设备、生成程序块、编译、导入导出、程序下载等全流程操作,是实现 PLC 工程自动化、AI 生成代码的核心工具。

很多初学者极易混淆 Openness 与 PLC 通信协议,这里用表格清晰区分:

表格

业务场景 对应技术方案
自动化操作博图、新建项目、编写程序、编译下载 ✅ TIA Portal Openness
博图程序下载、在线连接 PLCSIM / 物理 PLC ✅ Openness 内置 DownloadProvider / OnlineProvider
读写 PLC 实时变量、DB 块数据、在线监控 ❌ 非 Openness,推荐 Sharp7、S7netPlus、OPC UA

核心总结:Openness 操作的是博图软件与离线工程文件,而非直接操控运行状态下的 PLC 硬件;它读取的设备信息,是工程内组态数据,不是仿真 / 真机的实时运行数据。

二、环境与前置条件(缺一不可,配置错一步直接报错)

本次实战固定版本组合,兼容性最佳,所有组件必须严格匹配:

表格

软件 / 组件 版本 & 要求 补充说明
TIA Portal V18(自带 Openness 组件) 安装博图时默认勾选 Openness,无需额外单独安装
开发工具 Visual Studio 2022 新建控制台项目即可,无需复杂窗体
.NET 运行库 .NET Framework 4.8 V18 专属适配版本,4.7.2 及以下大概率加载 DLL 失败
核心 DLL Siemens.Engineering.dll 路径:C:\Program Files\Siemens\Automation\Portal V18\PublicAPI\V18\
Windows 权限 当前用户加入 Siemens TIA Openness 用户组 头号报错根源,未添加会直接提示拒绝访问

2.1 关键配置:将用户加入 Openness 用户组

这是入门第一道关卡,提供两种配置方式,任选其一即可,配置完成必须注销并重新登录系统,否则不生效

方式 1:图形界面操作(适合新手)
  1. 按下 Win + R,输入 lusrmgr.msc 打开本地用户和组;
  2. 左侧选择,双击 Siemens TIA Openness
  3. 点击添加,选中当前 Windows 登录账号,确认保存;
  4. 注销电脑,重新登录。
方式 2:PowerShell 命令行(管理员模式,高效快捷)

以管理员身份打开 PowerShell,执行以下命令:

powershell

# 将当前用户加入Openness用户组
net localgroup "Siemens TIA Openness" "$env:USERNAME" /add
# 查看组成员,验证是否添加成功
net localgroup "Siemens TIA Openness"

执行完毕后,同样需要注销重登

三、创建项目并引用 Openness 依赖

  1. 打开 Visual Studio 2022,新建 .NET Framework 4.8 控制台应用
  2. 手动引用 Siemens.Engineering.dll,重点配置属性:Private=False(禁止复制 DLL 到运行目录,程序自动从系统注册表解析路径)。

3.1 项目配置文件 .csproj 核心代码

xml

<Reference Include="Siemens.Engineering">
  <HintPath>C:\Program Files\Siemens\Automation\Portal V18\PublicAPI\V18\Siemens.Engineering.dll</HintPath>
  <SpecificVersion>False</SpecificVersion>
  <Private>False</Private>
</Reference>

3.2 App.config 指定运行时版本

xml

<startup>
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>

四、核心难点:程序集解析 AssemblyResolve

Siemens.Engineering.dll 不会复制到程序运行目录,.NET 运行时会找不到程序集导致崩溃。解决方案:注册 AssemblyResolve 事件,从系统注册表精准匹配对应版本 DLL

踩坑提醒:电脑可能存在多个博图版本(V15.1/V16/V17/V18),必须按请求版本精确匹配,强行加载低版本 DLL 会触发TypeLoadException类型加载异常。

4.1 完整程序集解析代码

csharp

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Linq;
using Microsoft.Win32;

namespace TIAOpennessDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // 优先注册解析事件(必须在调用Openness类型之前)
            AppDomain.CurrentDomain.AssemblyResolve += OpennessAssemblyResolver;
            // 执行核心业务逻辑
            RunOpennessTest();
        }

        /// <summary>
        /// 从注册表精准加载对应版本的Openness程序集
        /// </summary>
        private static Assembly OpennessAssemblyResolver(object sender, ResolveEventArgs args)
        {
            var requested = new AssemblyName(args.Name);
            string assemblyName = requested.Name;
            Version requestedVersion = requested.Version;

            if (!assemblyName.StartsWith("Siemens.Engineering", StringComparison.OrdinalIgnoreCase))
                return null;

            // 读取64位注册表
            using (RegistryKey baseKey = RegistryKey
                .OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)
                .OpenSubKey(@"SOFTWARE\Siemens\Automation\Openness"))
            {
                if (baseKey == null) return null;
                var candidates = new List<KeyValuePair<Version, string>>();

                // 遍历所有Openness版本
                foreach (string versionName in baseKey.GetSubKeyNames())
                {
                    using (RegistryKey publicApiKey = baseKey.OpenSubKey(versionName + @"\PublicAPI"))
                    {
                        if (publicApiKey == null) continue;
                        foreach (string apiVersion in publicApiKey.GetSubKeyNames())
                        {
                            using (RegistryKey asmKey = publicApiKey.OpenSubKey(apiVersion))
                            {
                                string path = asmKey?.GetValue(assemblyName) as string;
                                if (string.IsNullOrEmpty(path) || !File.Exists(path)) continue;
                                Version.TryParse(apiVersion, out Version v);
                                candidates.Add(new KeyValuePair<Version, string>(v ?? new Version(0, 0), path));
                            }
                        }
                    }
                }

                if (candidates.Count == 0) return null;
                // 优先精确匹配版本,无匹配则选用最高版本
                string chosenPath =
                    candidates.FirstOrDefault(c => requestedVersion != null && c.Key == requestedVersion).Value
                    ?? candidates.OrderByDescending(c => c.Key).First().Value;

                return Assembly.LoadFrom(chosenPath);
            }
        }
    }
}

关键规则AssemblyResolve 事件必须最先注册,所有使用 Openness 类型的代码单独封装方法,避免 JIT 提前加载 DLL 报错。

五、连接博图:附加到正在运行的 TIA Portal 实例

实战最常用方式:直接附加到已打开的博图进程,无需后台静默启动软件。通过 TiaPortal.GetProcesses() 扫描本地博图实例,再执行附加连接。

5.1 连接博图 + 读取项目设备代码

在上述代码中补充 RunOpennessTest 方法、递归遍历设备工具方法:

csharp

using Siemens.Engineering;
using Siemens.Engineering.Device;

/// <summary>
/// 核心入口:附加博图、读取项目与设备
/// </summary>
private static void RunOpennessTest()
{
    var processes = TiaPortal.GetProcesses();
    Console.WriteLine($"检测到 {processes.Count} 个博图运行实例");
    if (processes.Count == 0)
    {
        Console.WriteLine("请先手动打开 TIA Portal V18!");
        return;
    }

    foreach (var process in processes)
    {
        using (TiaPortal tia = process.Attach())
        {
            Console.WriteLine("✅ 成功附加到博图实例,Openness连接正常");
            var project = tia.Projects.FirstOrDefault();
            if (project == null)
            {
                Console.WriteLine("当前博图无打开项目");
                continue;
            }

            Console.WriteLine($"当前项目名称:{project.Name}");
            Console.WriteLine($"项目设备总数:{project.Devices.Count}");

            // 遍历所有设备
            foreach (Device device in project.Devices)
            {
                Console.WriteLine($"\n设备名称:{device.Name}  设备类型:{device.TypeIdentifier}");
                foreach (var item in EnumerateDeviceItems(device))
                {
                    Console.WriteLine($"  - {item.Name} 【{item.Classification}】 {item.TypeIdentifier}");
                }

                // 查找PLC程序容器、切换离线、生成代码
                var plcSoftware = FindPlcSoftware(project);
                if (plcSoftware != null)
                {
                    GoOfflineIfNeeded(project);
                    GenerateMotorControlScl(plcSoftware);
                    project.Save();
                    Console.WriteLine("✅ 项目已保存,程序块生成完成");
                }
            }
        }
    }
}

/// <summary>
/// 递归遍历设备下所有硬件模块
/// </summary>
private static IEnumerable<DeviceItem> EnumerateDeviceItems(Device device)
{
    foreach (DeviceItem item in device.DeviceItems)
    {
        yield return item;
        foreach (var child in EnumerateDeviceItems(item))
            yield return child;
    }
}
private static IEnumerable<DeviceItem> EnumerateDeviceItems(DeviceItem parent)
{
    foreach (DeviceItem item in parent.DeviceItems)
    {
        yield return item;
        foreach (var child in EnumerateDeviceItems(item))
            yield return child;
    }
}

5.2 运行说明

  1. 手动打开 TIA Portal V18 并新建 / 打开一个 S7-1200 项目;
  2. 启动 C# 控制台程序,首次附加会弹出博图安全确认框,点击「允许」即可;
  3. 控制台正常输出设备、CPU 型号、接口等信息,代表连接成功。

六、定位 PLC 程序容器 PlcSoftware

所有 PLC 程序块、变量表、外部源都挂载在 PlcSoftware 下,通过 SoftwareContainer 服务遍历获取:

csharp

using Siemens.Engineering.Services;
using Siemens.Engineering.SW;

/// <summary>
/// 从项目中查找PLC程序容器
/// </summary>
private static PlcSoftware FindPlcSoftware(Project project)
{
    foreach (Device device in project.Devices)
    {
        foreach (DeviceItem item in EnumerateDeviceItems(device))
        {
            var container = item.GetService<SoftwareContainer>();
            if (container != null && container.Software is PlcSoftware plc)
                return plc;
        }
    }
    return null;
}

七、必做操作:在线转离线(修改程序硬性要求)

高频报错点:博图处于在线(连接 PLCSIM / 真机)状态时,禁止修改程序、生成代码,会抛出 This function is not supported in online mode

解决方案:代码自动检测在线状态,并切换至离线模式:

csharp

using Siemens.Engineering.Online;

/// <summary>
/// 自动将在线设备切换为离线
/// </summary>
private static void GoOfflineIfNeeded(Project project)
{
    foreach (Device device in project.Devices)
    {
        foreach (DeviceItem item in EnumerateDeviceItems(device))
        {
            OnlineProvider op = item.GetService<OnlineProvider>();
            if (op != null && op.State != OnlineState.Offline)
            {
                Console.WriteLine($"设备 {item.Name} 处于在线状态,正在切换离线...");
                op.GoOffline();
            }
        }
    }
}

八、自动生成 PLC 程序:SCL 与 梯形图 (LAD) 实战

Openness 生成代码分为两种方案,SCL 语法简单、稳定性高,优先推荐入门与 AI 代码生成场景;梯形图基于 XML 格式,结构复杂,适合需要可视化梯形图的场景。

8.1 推荐方案:SCL 语言(外部源生成,最简方案)

利用外部源文件 (External Source),直接编写标准 SCL 文本,由博图自动编译为程序块。案例:电机正反转互锁控制逻辑(工业经典案例)。

csharp

using Siemens.Engineering.SW.ExternalSources;

/// <summary>
/// 自动生成电机正反转SCL程序块
/// </summary>
private static void GenerateMotorControlScl(PlcSoftware plc)
{
    const string blockName = "MotorControl_FwdRev";
    string srcDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "src");
    Directory.CreateDirectory(srcDir);
    string sclFile = Path.Combine(srcDir, blockName + ".scl");

    // 标准SCL代码:电机正反转+自锁+互锁
    string sclCode =
$"FUNCTION \"{blockName}\" : Void\r\n" +
"{ S7_Optimized_Access := 'TRUE' }\r\n" +
"VERSION : 0.1\r\n" +
"\r\n" +
"BEGIN\r\n" +
"    // I0.0正转启动、I0.1反转启动、I0.2停止\r\n" +
"    // Q0.0正转输出、Q0.1反转输出,双向互锁\r\n" +
"    %Q0.0 := (%I0.0 OR %Q0.0) AND NOT %I0.2 AND NOT %Q0.1;\r\n" +
"    %Q0.1 := (%I0.1 OR %Q0.1) AND NOT %I0.2 AND NOT %Q0.0;\r\n" +
"END_FUNCTION\r\n";

    // 写入SCL文件(UTF-8编码)
    File.WriteAllText(sclFile, sclCode, new UTF8Encoding(true));

    // 删除旧文件,避免重复报错
    var oldSource = plc.ExternalSourceGroup.ExternalSources.Find(blockName);
    oldSource?.Delete();

    // 创建外部源并编译生成程序块
    PlcExternalSource source = plc.ExternalSourceGroup.ExternalSources.CreateFromFile(blockName, sclFile);
    var result = source.GenerateBlocksFromSource(GenerateBlockOption.KeepOnError);

    Console.WriteLine($"\n共生成 {result.Count} 个程序对象:");
    foreach (var obj in result)
    {
        if (obj is PlcBlock blk)
            Console.WriteLine($"✅ 程序块:{blk.Name}  编程语言:{blk.ProgrammingLanguage}");
    }
}

运行后,博图项目的程序块目录下会自动出现 MotorControl_FwdRev 功能块,编译、调用即可运行。该方案非常适合对接 AI 大模型:AI 生成 SCL 文本 → 代码写入文件 → Openness 自动编译为 PLC 程序。

8.2 进阶方案:梯形图 LAD(FlgNet XML)

梯形图依赖 FlgNet 格式 XML 描述触点、线圈、连线,格式复杂,不建议手写。最佳流程:

  1. 在博图中手动绘制一段梯形图;
  2. 使用 Export 接口导出 XML 模板;
  3. 基于模板动态拼接 XML,再通过 Import 导入生成梯形图。
基础 XML 模板(常开触点 + 线圈)

xml

<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v1">
  <Parts>
    <Access Scope="GlobalVariable" UId="21"><Symbol><Component Name="Start"/></Symbol></Access>
    <Access Scope="GlobalVariable" UId="22"><Symbol><Component Name="Lamp"/></Symbol></Access>
    <Part Name="Contact" UId="23"/>
    <Part Name="Coil" UId="24"/>
  </Parts>
  <Wires>
    <Wire UId="25"><Powerrail/><NameCon UId="23" Name="in"/></Wire>
    <Wire UId="26"><IdentCon UId="21"/><NameCon UId="23" Name="operand"/></Wire>
    <Wire UId="27"><NameCon UId="23" Name="out"/><NameCon UId="24" Name="in"/></Wire>
    <Wire UId="28"><IdentCon UId="22"/><NameCon UId="24" Name="operand"/></Wire>
  </Wires>
</FlgNet>
导出 / 导入核心代码

csharp

// 导出现有梯形图为XML模板
block.Export(new FileInfo("lad_temp.xml"), ExportOptions.WithDefaults);

// 导入XML生成梯形图程序块
plcSoftware.BlockGroup.Blocks.Import(new FileInfo("lad_temp.xml"), ImportOptions.Override);

注意:XML 命名空间、版本随博图版本变化,务必以本机导出的模板为准。

九、常见报错速查表(排错必备)

表格

报错信息 根因 解决方案
UnauthorizedAccessException 拒绝访问 用户未加入 Siemens TIA Openness 添加用户组,注销重登
TypeLoadException 加载类型失败 加载了低版本 DLL(V15.1 等) 优化 AssemblyResolve,按版本精准匹配
This function is not supported in online mode 设备处于在线状态 执行 GoOffline () 切换离线
梯形图触点显示??? XML 中操作数 / 地址配置错误 导出原生梯形图模板,仿写格式
找不到 Siemens.Engineering 程序集 未注册 AssemblyResolve 事件 程序入口优先注册解析事件
程序附加后卡死不动 博图弹出权限确认框 切换到博图,点击「允许」

十、完整业务工作流(PLC+AI + 机器视觉通用流程)

结合 Openness、AI 生成代码、机器视觉项目,整理全链路标准流程:

  1. 环境准备:安装 TIA Portal V18、VS2022、.NET4.8,配置用户组权限;
  2. 项目搭建:创建.NET4.8 控制台项目,引用 DLL 并配置Private=False
  3. 程序集解析:注册AssemblyResolve事件,从注册表加载 DLL;
  4. 连接博图:扫描并附加本地博图进程,读取项目与硬件设备;
  5. 状态切换:检测 PLC 设备状态,自动切换为离线模式;
  6. 代码生成:对接 AI 生成 SCL 文本 / 拼接 LAD-XML,自动生成程序块;
  7. 项目保存:调用 Save () 保存工程文件;
  8. 调试运行:切换在线模式,下载程序到 PLCSIM / 物理 PLC,联动机器视觉设备调试。

十一、拓展:对接人工智能与机器视觉落地思路

本文实现了Openness 自动生成 PLC 代码的基础能力,在此之上可快速拓展为 AI+PLC + 机器视觉一体化方案:

  1. AI 生成 PLC 代码:通过大模型接收业务需求(如视觉分拣、定位逻辑),自动输出标准 SCL 代码;
  2. 代码自动落地:C# 程序接收 AI 返回的 SCL 文本,调用 Openness 写入博图生成程序块;
  3. 全链路联动:机器视觉相机采集图像 → 上位机算法处理 → 下发指令至 PLC → PLC 驱动执行机构动作。

该方案大幅减少人工编程工作量,也是工业智能化项目的主流开发方向。

结语

本文基于 TIA Portal V18 完成了 Openness 从环境配置、进程连接、离线切换到自动生成 SCL / 梯形图的全流程实战,所有代码经过真机 + 仿真验证。掌握这套技术,不仅能实现 PLC 工程自动化,更是打通PLC 上位机、人工智能、机器视觉三大技术栈的关键一步。

后续大家可基于本文代码拓展:批量创建工程、自动组态硬件、一键编译下载、AI 智能生成复杂控制逻辑等功能。如果在配置、代码调试、AI 对接过程中遇到问题,欢迎留言交流。


原创不易,欢迎点赞 + 收藏 + 关注,持续分享工业自动化、PLC 二次开发、机器视觉实战教程!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐