实时双执行域
EnerOS 采用双执行域架构:通用执行域(General Domain)承载 Agent 编排、AI 推理、业务逻辑;实时执行域(Real-time Domain)承载保护逻辑、开关操作、闭环控制,保障 P99 < 1ms 的确定性时延。两个域通过内核态共享内存通信,避免进程切换开销。
电力系统对时延有严苛要求:继电保护必须在 4 个周波(80ms@50Hz)内动作,PMU 相量计算要求 30Hz 以上输出,闭环 AVR 控制周期在 100ms 级。通用操作系统(Linux CFS 调度)的毫秒级抖动无法满足这些场景。EnerOS 通过双执行域把「快慢分离」做到内核级。
架构总览
┌─────────────────────────────────────────────────┐
│ 通用执行域 (General Domain) │
│ - Agent 编排 │
│ - LLM 推理 │
│ - 业务流程 │
│ - 时序分析 │
│ - 调度:CFS(完全公平调度器) │
└──────────────────┬──────────────────────────────┘
│ 共享内存 / 无锁队列
│ 延迟 < 10μs
┌──────────────────┴──────────────────────────────┐
│ 实时执行域 (Real-time Domain) │
│ - 保护逻辑 (4ms) │
│ - 闭环控制 (1ms) │
│ - 开关操作 (100μs) │
│ - 相量计算 (250μs) │
│ - 调度:SCHED_FIFO / PREEMPT_RT / Xenomai │
└─────────────────────────────────────────────────┘
域职责划分
| 任务类型 | 通用执行域 | 实时执行域 |
|---|
| Agent 编排 | ✅ | - |
| LLM 推理 | ✅ | - |
| 潮流计算 | ✅ | - |
| 状态估计 | ✅ | 100ms 周期版本 |
| 数字孪生 What-If | ✅ | - |
| 保护逻辑(距离保护) | - | ✅ |
| 闭环控制(AVR / AGC) | - | ✅ |
| 开关操作(GOOSE) | - | ✅ |
| 相量计算(PMU) | - | ✅ |
| 故障录波触发 | - | ✅ |
实时域任务定义
实时任务通过 RealtimeTask trait 定义,遵循周期触发模型:
use eneros_realtime::{RealtimeTask, RtContext, RtResult};
use std::time::Duration;
struct ProtectionLogic {
period: Duration,
breaker_id: u32,
zone_reach: f64, // Ω
}
impl RealtimeTask for ProtectionLogic {
fn period(&self) -> Duration { self.period }
fn priority(&self) -> i32 { 99 } // SCHED_FIFO 优先级
fn cpu_affinity(&self) -> Option<Vec<u32>> {
Some(vec![0, 1]) // 绑定到 CPU 0,1
}
fn run(&mut self, ctx: &mut RtContext) -> RtResult<()> {
// 读取 PMU 数据(共享内存,零拷贝)
let pmu = ctx.pmu_snapshot();
// 距离保护算法
let z_seen = self.compute_impedance(&pmu);
if z_seen < self.zone_reach {
// 立即跳闸(无锁通道,< 100μs)
ctx.trip_breaker(self.breaker_id);
ctx.log_event(ProtectionEvent::Trip {
impedance: z_seen,
reach: self.zone_reach,
});
}
Ok(())
}
}
impl ProtectionLogic {
fn compute_impedance(&self, pmu: &PmuSnapshot) -> f64 {
let v = pmu.voltage_complex();
let i = pmu.current_complex();
(v / i).norm()
}
}
| 方法 | 类型 | 必填 | 说明 |
|---|
| period() | Duration | 是 | 任务周期 |
| priority() | i32 | 是 | SCHED_FIFO 优先级(1-99) |
| cpu_affinity() | Option<Vec> | 否 | CPU 亲和性 |
| run(&mut ctx) | RtResult | 是 | 任务主体 |
| init(&mut ctx) | RtResult | 否 | 初始化 |
| on_overrun(dur) | RtResult | 否 | 超时回调 |
| shutdown(&mut ctx) | RtResult | 否 | 退出清理 |
任务调度
实时域采用基于优先级的抢占式调度,配合 Linux PREEMPT_RT 或 Xenomai:
use eneros_realtime::{RtScheduler, SchedPolicy, IsolationConfig};
let mut sched = RtScheduler::new()
.policy(SchedPolicy::Fifo)
.cpu_affinity(0..2) // 隔离 CPU 0,1
.isolate_from_general(true)
.isolation(IsolationConfig {
isolcpus: true, // 内核启动参数 isolcpus
nohz_full: true, // NO_HZ_FULL
rcu_nocbs: true, // RCU 卸载
huge_pages: true, // 大页内存
disable_irq: true, // 禁用 IRQ
});
sched.spawn(ProtectionLogic::new(Duration::from_micros(4000), brk_id, 5.0));
sched.spawn(ClosedLoopControl::new(Duration::from_millis(1)));
sched.spawn(BreakerOperation::new(Duration::from_micros(100)));
sched.spawn(PhasorCalculator::new(Duration::from_micros(250)));
sched.start()?;
SchedPolicy 变体
| 策略 | 说明 | 适用场景 |
|---|
| Fifo | SCHED_FIFO 先进先出 | 大多数实时任务 |
| RoundRobin | SCHED_RR 时间片轮转 | 同优先级多任务 |
| Deadline | SCHED_DEADLINE EDF | 周期任务 |
| Xenomai | Xenomai Cobalt | 极致实时性 |
跨域通信
两个域通过无锁环形队列通信,避免锁竞争与缓存抖动:
use eneros_realtime::cross::{Channel, Message, ChannelConfig};
let (tx, rx) = Channel::unbounded::<ControlCmd>(
ChannelConfig {
capacity: 1024,
spin_wait_us: 5,
cache_align: true,
},
);
// 通用域 → 实时域
tx.send(ControlCmd::SetVoltage(1.02))?;
// 实时域接收(非阻塞)
if let Some(cmd) = rx.try_recv() {
match cmd {
ControlCmd::SetVoltage(v) => self.target_voltage = v,
ControlCmd::TripBreaker(id) => ctx.trip_breaker(id),
}
}
// 实时域 → 通用域(事件上报)
let (evt_tx, evt_rx) = Channel::bounded::<RtEvent>(256);
evt_tx.send(RtEvent::BreakerTripped { id: 5, reason: "overcurrent" });
Channel 类型
| 类型 | 容量 | 阻塞行为 | 适用场景 |
|---|
| unbounded | 无限 | 发送不阻塞 | 通用→实时 |
| bounded(n) | n | 满则阻塞 | 实时→通用 |
| spsc | 1 | 单生产单消费 | 高速数据流 |
| mpmc | n | 多生产多消费 | 一般场景 |
跨域消息延迟
| 路径 | 延迟(P99) |
|---|
| 通用 → 实时(无锁队列) | < 5μs |
| 实时 → 通用(事件队列) | < 10μs |
| 共享内存读取 | < 1μs |
| 共享内存写入 | < 1μs |
确定性保证
| 措施 | 说明 | 效果 |
|---|
| CPU 隔离 | isolcpus=0,1 隔离实时核心 | 避免被其他进程抢占 |
| 内存预分配 | 启动时分配,运行时无 GC | 避免分配抖动 |
| 无锁数据结构 | SPSC/MPMC 无锁队列 | 避免锁竞争 |
| 大页内存 | 2MB / 1GB hugepages | 减少 TLB miss |
| 中断亲和 | 网卡中断绑定到非实时核 | 避免 IRQ 打断 |
| 看门狗 | 任务超时自动重置 | 故障自愈 |
| NO_HZ_FULL | 减少 timer tick | 减少上下文切换 |
| RCU nocbs | RCU 回调卸载 | 减少抖动 |
内核启动参数示例
# /etc/default/grub
GRUB_CMDLINE_LINUX="isolcpus=0,1 nohz_full=0,1 rcu_nocbs=0,1 \
hugepagesz=2M hugepages=512 iommu=pt intel_idle.max_cstate=0 \
processor.max_cstate=0 idle=poll"
任务类型与周期
| 任务 | 周期 | 优先级 | 抖动 | CPU |
|---|
| 开关操作(GOOSE) | 100μs | 99 | < 5μs | 0 |
| 相量计算(PMU) | 250μs | 98 | < 10μs | 0 |
| 闭环控制(AVR) | 1ms | 95 | < 50μs | 1 |
| 闭环控制(AGC) | 4ms | 94 | < 100μs | 1 |
| 保护逻辑(距离) | 4ms | 90 | < 100μs | 0 |
| 保护逻辑(过流) | 4ms | 90 | < 100μs | 0 |
| 状态估计(快速) | 100ms | 50 | < 1ms | 1 |
| 通信(IEC 61850) | 1ms | 80 | < 50μs | 1 |
RtContext API
实时任务上下文提供的 API:
use eneros_realtime::RtContext;
impl RtContext {
// 数据访问(共享内存,零拷贝)
pub fn pmu_snapshot(&self) -> PmuSnapshot;
pub fn rtu_snapshot(&self) -> RtuSnapshot;
pub fn network_state(&self) -> NetworkState;
// 控制输出
pub fn trip_breaker(&self, id: u32);
pub fn close_breaker(&self, id: u32);
pub fn set_excitation(&self, gen_id: u32, v: f64);
pub fn set_tap(&self, xfmr_id: u32, tap: i16);
// 通信
pub fn send_event(&self, evt: RtEvent);
pub fn try_recv_cmd(&self) -> Option<ControlCmd>;
// 日志(lock-free)
pub fn log_event(&self, evt: impl Event);
pub fn waveform_record(&self, data: &WaveformBuffer);
// 时间
pub fn now(&self) -> Timestamp;
pub fn elapsed(&self) -> Duration;
}
故障切换
通用域故障不会影响实时域:实时域检测到通用域心跳超时后,自动切换到「安全模式」,按预设策略运行。
use eneros_realtime::SafetyMode;
sched.on_general_domain_timeout(|| {
// 切换到安全模式
SafetyMode::island_operation()
.hold_frequency(50.0)
.hold_voltage(1.0)
.disable_remote_control()
.enable_local_protection()
.activate();
});
// 心跳配置
sched.heartbeat_config(HeartbeatConfig {
interval: Duration::from_millis(100),
timeout: Duration::from_millis(500),
on_missed: HeartbeatAction::Warn,
on_timeout: HeartbeatAction::EnterSafetyMode,
});
SafetyMode 行为
| 行为 | 含义 |
|---|
| hold_frequency | 维持频率在设定值 |
| hold_voltage | 维持电压在设定值 |
| disable_remote_control | 拒绝远程控制命令 |
| enable_local_protection | 启用本地保护 |
| activate | 激活安全模式 |
性能指标
| 指标 | 数值 | 测试方法 |
|---|
| 任务调度抖动(P99) | < 50μs | 1 万次周期任务 |
| 任务调度抖动(P99.9) | < 100μs | 1 万次周期任务 |
| 跨域消息延迟 | < 10μs | 无锁队列 |
| GPIO 响应(开关操作) | < 100μs | 示波器测量 |
| 端到端保护动作(P99) | < 1ms | 故障注入 |
| 端到端保护动作(最坏) | < 4ms | 故障注入 |
| 最大连续无抖动运行 | > 30 天 | 7×24 测试 |
| CPU 利用率(实时核) | < 60% | 满负荷 |
| 内存占用(实时域) | < 100MB | 预分配 |
测试环境:Intel Xeon 4 核 / 8GB / Ubuntu 22.04 + PREEMPT_RT。
配置参数
eneros.toml 中实时域相关配置:
[realtime]
# 调度策略:fifo / round_robin / deadline / xenomai
policy = "fifo"
# 实时 CPU 核(与 isolcpus 一致)
cpus = [0, 1]
# 大页内存(MB)
huge_pages_mb = 1024
# 是否启用看门狗
watchdog = true
# 看门狗超时(ms)
watchdog_timeout_ms = 100
# 心跳间隔(ms)
heartbeat_interval_ms = 100
# 心跳超时(ms)
heartbeat_timeout_ms = 500
# 故障切换安全模式
safety_mode_on_timeout = true
# 实时任务最大数
max_tasks = 256
| 参数 | 类型 | 默认值 | 说明 |
|---|
| policy | enum | fifo | 调度策略 |
| cpus | Vec | [0,1] | 实时 CPU 核 |
| huge_pages_mb | u32 | 1024 | 大页内存大小 |
| watchdog | bool | true | 启用看门狗 |
| watchdog_timeout_ms | u32 | 100 | 看门狗超时 |
| heartbeat_interval_ms | u32 | 100 | 心跳间隔 |
| heartbeat_timeout_ms | u32 | 500 | 心跳超时 |
| safety_mode_on_timeout | bool | true | 超时切换安全模式 |
| max_tasks | u32 | 256 | 实时任务最大数 |
与其他能力的关系
| 关联能力 | 互动方式 |
|---|
| 安全守卫 | 实时域命令也经过网关(精简版) |
| 数字孪生引擎 | 与实时域同步镜像 |
| 设备模型库 | 实时域使用精简设备模型 |
| 多智能体协作 | 保护类 Agent 运行在实时域 |
| 时序原生操作 | PMU 数据流式写入 |
| 物理约束决策 | 实时域约束校验在微秒级完成 |
| 电网拓扑一等公民 | 拓扑变更实时同步到实时域 |
相关文档