性能优化
扫描周期抖动太大怎么办?
抖动 (Jitter) 是扫描周期偏离目标值的波动。硬实时应用 (如 EtherCAT 伺服控制) 要求抖动 < 10us。
BIOS 设置优化
| 设置项 | 推荐值 | 说明 |
|---|---|---|
| C-State | 关闭 | CPU 节能状态会导致唤醒延迟 |
| SpeedStep (EIST) | 关闭 | 频率切换导致执行时间波动 |
| Turbo Boost | 关闭 | 频率提升/回落导致不确定性 |
| Hyper-Threading | 关闭 | 超线程导致核心资源竞争 |
| VT-d | 关闭 | I/O 虚拟化增加中断延迟 |
| USB Legacy | 关闭 | USB 轮询产生 SMI 中断 |
| Serial Port | 关闭 | 串口中断干扰 |
操作系统优化
Windows:
# 设置电源计划为高性能
powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
# 禁用动态时钟
bcdedit /set disabledynamictick yes
# 设置 OS 可用核心数 (给实时任务留出隔离核心)
bcdedit /set numproc 12 # 16核 CPU 留 4 核给实时任务
核心分配
IDE 内 设置 → 运行时 → 核心分配 页,可将实时任务绑定到独立 CPU 核心:
EtherCAT 收发 → 最高优先级核心
机器人运动学 → 独立计算核心
PLC 扫描 → 独立执行核心
默认配置已针对 8/16 核常见配置优化,无需手工调整。
使用性能面板的「最大抖动」指标持续观察。目标值:
- EtherCAT 伺服: < 10 us
- PLC 扫描: < 50 us
- 通用控制: < 1 ms
1ms 周期下 CPU 占用率很高怎么办?
分析 CPU 占用
在性能面板中查看扫描周期利用率分解:
IO 输入读取: 50 us ( 5%)
PLC 扫描: 320 us (32%)
IO 输出写入: 80 us ( 8%)
运动学计算: 200 us (20%)
通讯处理: 50 us ( 5%)
────────────────────────────
总计: 700 us (70%)
空闲: 300 us (30%)
优化方案
-
减少 PLC 扫描时间
- 精简主循环逻辑
- 将非实时任务移到低优先级 OB
- 避免在主循环中使用字符串操作和浮点除法
-
优化运动学计算
- 减少同时运动的机器人数量
- 使用解析解 (SCARA/笛卡尔) 替代数值解 (6轴迭代)
- 降低插补精度要求
-
降低周期频率
- 如果应用不需要 1ms,将周期设为 2ms 或 4ms
- 大多数 IO 控制场景 4ms 周期已足够
-
升级硬件
- 使用更高频的 CPU
- 使用更多核心并合理分配
CPU 利用率持续超过 80% 时,应立即优化或降频。超过 100% 会导致扫描周期超时 (Overrun),影响控制精度甚至安全。
内存使用持续增长是内存泄漏吗?
正常增长场景
以下情况内存增长是正常的:
- 趋势数据缓存 — 实时数据在内存中累积,达到上限后滚动丢弃
- 报警历史 — 报警记录持续累积
- 数据记录缓冲 — 刷盘前在内存中缓冲
异常增长排查
使用性能面板的「内存分析」功能:
- 观察内存分布饼图,确认哪个区域在增长
- 检查是否有变量被重复创建而未释放
- 检查通讯缓冲区是否因对端不消费导致堆积
常见内存泄漏原因
| 原因 | 解决方案 |
|---|---|
| 字符串拼接循环 | 限制字符串长度,使用固定缓冲 |
| 事件订阅未取消 | 确保 Dispose 时取消所有订阅 |
| 数据记录未刷盘 | 配置合理的刷盘间隔和缓冲上限 |
| 通讯消息堆积 | 添加消息队列上限,溢出时丢弃旧消息 |
如何减少 IO 延迟?
IO 延迟组成
总延迟 = 扫描周期 + 通讯延迟 + 设备响应时间
EtherCAT: 1ms + 0.1ms + 0ms ≈ 1.1ms
Modbus TCP: 50ms + 3ms + 5ms ≈ 58ms
串口 RTU: 50ms + 15ms + 10ms ≈ 75ms
优化方法
- 使用 EtherCAT — 硬实时,延迟最低
- 缩短扫描周期 — 从 50ms 降到 10ms 可显著降低响应延迟
- IO 地址打包 — 相邻地址合并为批量读写,减少通讯次数
- 异步预读 — 在主循环外提前发起 IO 读取请求
EtherCAT DC 同步精度不好怎么调?
Distributed Clock (DC) 同步精度受以下因素影响:
影响因素
| 因素 | 影响 | 优化 |
|---|---|---|
| 参考时钟选择 | 第一个支持 DC 的从站 | 选择网络拓扑中最稳定的从站 |
| 传播延迟补偿 | 帧到达各从站的时间差 | 运行时自动测量并补偿 |
| 系统时钟漂移 | 长时间运行后累积偏差 | 周期性重同步 (默认每 10 秒) |
| 网络拓扑 | 从站数量和级联深度 | 减少级联层数,使用星型拓扑 |
DC 配置
DCConfig.SyncMode := DC_SYNC0; (* SYNC0 中断同步 *)
DCConfig.CycleTime := T#1ms; (* 同步周期 *)
DCConfig.ShiftTime := T#200us; (* 偏移时间, 留出处理余量 *)
DC 同步精度的目标值通常为 < 1us。可在诊断面板中查看各从站的 DC 偏差值 (System Time Difference)。
如何评估系统是否满足实时性要求?
运行以下检查清单:
| 检查项 | 合格标准 | 测量方法 |
|---|---|---|
| 扫描周期抖动 | < 目标周期 10% | 性能面板 → 最大抖动 |
| CPU 利用率 | < 80% | 性能面板 → CPU 仪表 |
| 内存使用 | < 70% 上限 | 性能面板 → 内存饼图 |
| WKC 错误率 | < 0.001% | 诊断面板 → EtherCAT 统计 |
| DC 同步偏差 | < 1 us | 诊断面板 → DC 偏差 |
| 连续运行 | > 24 小时无超时 | 报警历史 → 过滤超时报警 |
部署与运维层性能问题
扫描周期抖动 >50μs 排查清单
当抖动超出预期值时, 按下表从外到内逐层排查:
| 层 | 检查项 | 典型现象 | 工具 |
|---|---|---|---|
| 硬件 | C-State / Turbo / HT 未关 | 周期性大抖动 (秒级) | BIOS 核查 |
| 硬件 | SMI 中断 (系统管理中断) | 规律性毛刺 (10-100ms) | C:\Windows\LatencyMon.exe |
| 驱动 | 第三方 NDIS 协议抢占 | 突发大抖动 | 设备管理器看网卡协议栈 |
| 驱动 | 非实时驱动 ISR 过长 | 抖动源来自特定中断号 | DPCLat / Xperf |
| 系统 | Windows Update 守护进程 | 下班后半夜抖动暴增 | 确认更新已禁用 |
| 系统 | 杀毒软件扫描 | 抖动与磁盘 I/O 同步 | Defender 排除或禁用 |
| 系统 | GC (托管 Service) | 抖动周期约几秒一次 | 性能计数器 .NET GC |
| 应用 | 大数组/字符串在主循环 | 稳态抖动偏高 | 扫描周期分析面板 |
| 应用 | 日志写盘阻塞 | 大抖动点恰好在记录处 | 改异步日志 |
LatencyMon 输出的 "DPC count" 排名前三的驱动就是主要抖动源。常见嫌犯: 无线网卡驱动 (关掉!) / 显卡驱动 / 蓝牙。
EtherCAT DC 同步失败
DC (Distributed Clock) 同步失败表现: 从站诊断中 DcSyncStatus != InSync 或 SYNC0 中断漂移大。
| 原因 | 处理 |
|---|---|
| 参考时钟选错 (选了不稳定从站) | IDE 配置中手工指定参考从站为主站旁第一个 |
| 参考从站不支持 64 位 DC | 升级从站固件或换支持 64 位的型号 |
| 传播延迟测量失败 | 重启主站让主站重新测量 (自动) |
| 参考从站掉线 | 主站自动选次优从站, 日志会报记录 |
网线过长 (>100m) 信号失真 | 加中继或改光纤段 |
DC 抖动 >1μs | 减小总线周期或检查 BIOS C-State |
查看当前参考时钟:
ecat dc status
Reference Clock: Slave[1] (0x1002)
System Time Offset: +125 ns
DC Sync Status: InSync
内存泄漏定位
Service 进程内存持续上涨 (超过 4 GB 或持续增长 24 小时不回落) 视为泄漏。定位步骤:
-
确认是否真泄漏
# 连续采样 1 小时, 每分钟记录
while ($true) {
$p = Get-Process DarraService -ErrorAction SilentlyContinue
"{0:yyyy-MM-dd HH:mm} {1:N0} KB" -f (Get-Date), ($p.WorkingSet64/1KB)
Start-Sleep 60
}趋势持续上涨 = 泄漏; 震荡 = 正常缓存。
-
用 VMMap 看内存分布
- SysInternals VMMap 附加到 DarraService
- 观察 "Heap" / "Managed Heap" 哪部分在涨
- Heap 涨 = 原生泄漏; Managed Heap 涨 = .NET 对象未释放
-
Perfmon 计数器
Process\Private Bytes— 整体内存.NET CLR Memory\# Bytes in all Heaps— 托管堆.NET CLR Memory\# Gen 2 Collections— 应周期发生, 不发生说明 GC 失效
-
常见泄漏模式
| 模式 | 排查 |
|---|---|
| Historian 缓冲未刷盘 | 调 historian.flushIntervalMs 小一点 |
| 订阅未解绑 | OPC UA 客户端断开时未 -= 事件 |
| 通讯队列无上限 | 通讯队列配 maxQueueSize = 10000 |
| string 拼接循环 | 主循环改 StringBuilder 固定大小 |
日志写磁盘导致性能下降
表现: 主循环偶发卡顿 100-500 ms, 时刻对应大量日志写入。
| 处理 | 效果 |
|---|---|
| 生产环境改 Info 级别 (从 Debug) | 降 90% 日志量 |
| 改异步日志 (NLog AsyncWrapper) | 不阻塞写入线程 |
| SSD 替换机械硬盘 | 写延迟 10ms → 0.5ms |
| 日志目录放到独立磁盘 | 隔离业务 I/O |
| 关闭 Windows 文件索引 | 减少后台磁盘争用 |
# 关闭 %AppData%\DarraPLC 目录的索引
$folder = "$env:AppData\DarraPLC"
attrib +I $folder /S /D
Historian 写入拖慢扫描
当 Historian 以同步方式写数据时, 每次扫描会阻塞等磁盘返回, 周期超时风险高。对策:
- 用异步数据库管道: 扫描只把数据投入内存队列, 专门线程批量落盘
- 批量大小建议 100-1000 条/批
- 磁盘 SSD + 日志分盘
参考实现思路类似 "Producer-Consumer" 模式, 详见 PLC 模块内高速采样相关文档。
多核 CPU 核心分配
推荐分配 (8 核 CPU 为例):
| 核 | 用途 | 优先级 |
|---|---|---|
| Core 0 | Windows 内核 + 驱动 ISR | 默认 |
| Core 1 | Windows 其他应用 | 默认 |
| Core 2 | DarraRT_Eth.sys EtherCAT 收发 | 最高 |
| Core 3 | DarraRT_PLC.sys PLC 扫描 | 最高 |
| Core 4 | 机器人运动学计算 | 高 |
| Core 5 | Service 通讯线程 | 中 |
| Core 6 | HMI Web / OPC UA | 中 |
| Core 7 | Historian / 日志 | 低 |
配置方法: IDE → 设置 → 运行时 → 核心分配。使用 IOCTL_DARRT_ISOLATE_CORE 向内核注册隔离核心, Windows 调度器不会把普通线程派到这些核上。
网络拥塞 (办公网和控制网必须分离)
| 网络 | 必须独立 | 原因 |
|---|---|---|
| EtherCAT 总线 (LAN2) | 绝对独立 | 专用 DarraRT_Eth 协议, 与 IP 不共存 |
| 管理网 (LAN1) | 推荐独立 VLAN | 办公广播风暴影响中断 |
| 同步链路 (冗余) | 必须独立 | 心跳丢失 = 切换误触发 |
| 存储/数据库 | 可共用管理网 | 不频繁则 OK |
办公网不隔离典型故障: 上午 9 点打印机批量启动, PLC 扫描周期集体抖动。详见 冗余功能。
长期运行性能退化
连续运行 30 天后性能变差的常见原因:
| 原因 | 排查 |
|---|---|
| 日志文件未清理, 磁盘满 | 自动清理策略见 general.md |
| Historian 数据库索引膨胀 | 每季度 VACUUM 一次 SQLite |
| RETAIN 持久化文件碎片 | 停机时 Service 会自动整理 |
| Windows 页面文件碎片 | 季度维护窗口内 defrag |
| 风扇积灰导致降频 | 现场维护计划 |
维护建议周期:
| 周期 | 任务 |
|---|---|
| 每天 | 查看报警历史, 确认无致命报警 |
| 每周 | 检查磁盘空间, 日志量 |
| 每月 | 重启 Service 清理缓存 (低峰期) |
| 每季度 | 数据库 VACUUM + 硬件清灰 |
| 每年 | 硬件全面检查, UPS 电池检测 |