实时系统优化:推理任务专属环境配置

1. 核心隔离 (Core Isolation)

目的:禁止系统进程抢占核心 6 和 7,确保推理任务拥有专属物理领地。

永久操作

1
2
3
4
5
6
7
# 1. 编辑配置文件
sudo vim /etc/default/grub
# 找到 GRUB_CMDLINE_LINUX_DEFAULT 行,引号内添加 isolcpus=6,7
# 修改后示例: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=6,7"

# 2. 更新引导并重启
sudo update-grub && sudo reboot

验证

1
cat /proc/cmdline | grep "isolcpus=6,7"

2. 解除实时限流 (RT Throttling)

目的:解决 1ms 循环中出现的规律性 51ms 延迟毛刺。

临时操作 (立即生效)

1
echo -1 | sudo tee /proc/sys/kernel/sched_rt_runtime_us

永久操作

1
2
3
4
5
6
# 1. 编辑系统控制文件
sudo vim /etc/sysctl.conf
# 在末尾添加:kernel.sched_rt_runtime_us = -1

# 2. 使配置生效
sudo sysctl -p

验证

1
cat /proc/sys/kernel/sched_rt_runtime_us | grep -- "-1"

3. 关闭透明巨页 (THP)

目的:消除内核后台整理内存导致的毫秒级随机延迟抖动。

临时操作 (立即生效)

1
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

永久操作

1
2
3
4
5
6
7
# 1. 编辑配置文件
sudo vim /etc/default/grub
# 在 GRUB_CMDLINE_LINUX_DEFAULT 参数末尾追加 transparent_hugepage=never
# 修改后示例: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=6,7 transparent_hugepage=never"

# 2. 更新引导并重启
sudo update-grub && sudo reboot

验证

1
cat /sys/kernel/mm/transparent_hugepage/enabled | grep "\[never\]"

优化检查清单

优化项 验证命令 预期结果
核心隔离 cat /proc/cmdline 包含 isolcpus=6,7
限流解除 cat /proc/sys/kernel/sched_rt_runtime_us 显示 -1
THP 关闭 cat /sys/kernel/mm/transparent_hugepage/enabled 包含 [never]
编译
1
2
3
cd ~/xiao/build && cmake .. && make -j4
cd ~/xiao/build && rm -rf * && cmake .. && make -j$(nproc)
sudo ./RealTimeHIL
真实的边缘计算端通信接口代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#include <iostream>
#include <chrono>
#include <openvino/openvino.hpp>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sched.h>
#include <unistd.h>
#include <cstring>
#include <iomanip>
#include <vector>
#include <cmath>
#include <algorithm> // 必须包含此头文件以支持 std::min/max

using namespace std;
using namespace chrono;

// ---------------- 配置参数 (保留文件 A) ----------------
const int EDGE_RECV_PORT = 12345;
const int BOX_RECV_PORT = 12346;
const double SCALE = 100000.0;
const double OFFSET = 1200000000.0;
const int RECV_BYTES = 28;

#pragma pack(push, 1)
struct BoxPacket {
uint32_t data[7];
};
#pragma pack(pop)

// 9 个输入变量名
const vector<string> INPUT_NAMES = {
"Vbus", "Vref", "io", "ib", "ib_prev", "isc", "isc_prev", "Ts", "C"
};

// ---------------- 核心逻辑 (不修改编解码) ----------------

void decode_packet(const BoxPacket& rx, float* inputs) {
for(int i = 0; i < 7; ++i) {
uint32_t val = ntohl(rx.data[i]);
inputs[i] = (float)((double)val - OFFSET) / SCALE;
}
inputs[7] = 1e-4f;
inputs[8] = 5e-4f;
}

uint32_t encode_result(double y_pred) {
double y_temp = y_pred * SCALE + OFFSET;
if (y_temp < 0.0) y_temp = 0.0;
if (y_temp > 4294967295.0) y_temp = 4294967295.0;
return htonl((uint32_t)y_temp);
}

// ---------------- 优化版仪表盘 (3x3 矩阵布局) ----------------

void render_dashboard(int count, float* inputs, float output,
double d_in, double avg_in, double min_in, double max_in,
double d_proc, double d_out, double d_total, double max_jitter) {

printf("\033[2J\033[1;1H"); // 清屏并复位光标

// 状态判定
const char* status_tag = (d_total <= 1.0) ? "● NORMAL" : "▲ TIMEOUT";
const char* status_color = (d_total <= 1.0) ? "\033[1;32m" : "\033[1;31m";

printf("\033[1;36m┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\033[0m\n");
printf("\033[1;36m┃\033[0m %sHIL REAL-TIME MONITORING | Status: %-15s\033[0m \033[1;36m┃\033[0m\n", status_color, status_tag);
printf("\033[1;36m┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\033[0m\n");

printf(" 循环计数: \033[1;32m%-8d\033[0m | RTT 峰值抖动: \033[1;33m%8.4f ms\033[0m\n", count, max_jitter);
printf("----------------------------------------------------------------------\n");

// 3x3 矩阵排列显示 9 个输入
printf("\033[1;34m [INPUT TENSOR GRID (3x3)]\033[0m\n");
printf(" %-9s: %-10s | %-9s: %-10s | %-9s: %-10s\n", "NAME", "VALUE", "NAME", "VALUE", "NAME", "VALUE");
printf(" ----------|------------|----------|------------|----------|-----------\n");

for(int row = 0; row < 3; ++row) {
printf(" ");
for(int col = 0; col < 3; ++col) {
int idx = row * 3 + col;
printf("%-9s: \033[1;37m%10.4f\033[0m", INPUT_NAMES[idx].c_str(), inputs[idx]);
if(col < 2) printf(" | ");
}
printf("\n");
}
printf("----------------------------------------------------------------------\n");

// 预测结果:移除 NaN,改为文字提示
printf("\033[1;33m [PREDICTION OUTPUT]\033[0m\n");
if(std::isnan(output)) {
printf(" Target Value (y_pred): \033[1;33m[ INITIALIZING... ]\033[0m\n");
} else {
printf(" Target Value (y_pred): \033[1;32m%10.4f\033[0m\n", output);
}
printf("----------------------------------------------------------------------\n");

// 延迟统计
printf("\033[1;35m [LATENCY STATISTICS]\033[0m\n");
printf(" 1. UDP In (接收间隔) : %8.4f ms\n", d_in);
printf(" (平均/最小/最大) : \033[0;37m%7.3f / %7.3f / %7.3f ms\033[0m\n", avg_in, min_in, max_in);
printf(" 2. Inference (模型推理) : %8.4f ms\n", d_proc);
printf(" 3. UDP Out (结果发回) : %8.4f ms\n", d_out);
printf(" 4. Total RTT (本地总耗时): %s%8.4f ms\033[0m\n", status_color, d_total);
printf("======================================================================\n");

fflush(stdout);
}

int main() {
// 实时性锁定
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(7, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
struct sched_param param; param.sched_priority = 99;
sched_setscheduler(0, SCHED_FIFO, &param);
mlockall(MCL_CURRENT | MCL_FUTURE);

// OpenVINO 初始化
ov::Core core;
auto model = core.read_model("/home/xiaoxi/xiao/simulik/pinnsformer_P.onnx");
auto compiled = core.compile_model(model, "CPU", {{"INFERENCE_NUM_THREADS", "1"}});
auto req = compiled.create_infer_request();
float* input_tensor_data = req.get_input_tensor().data<float>();

// UDP 初始化
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in local_addr{}, client_addr{};
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(EDGE_RECV_PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
bind(sock_fd, (struct sockaddr*)&local_addr, sizeof(local_addr));

// 统计变量
int count = 0;
double max_jitter = 0;
double sum_interval = 0;
double min_interval = 1e9;
double max_interval = 0;
auto last_recv_time = high_resolution_clock::now();

BoxPacket rx_buf;

while (true) {
socklen_t addr_len = sizeof(client_addr);
int recv_len = recvfrom(sock_fd, &rx_buf, sizeof(rx_buf), 0,
(struct sockaddr*)&client_addr, &addr_len);

if (recv_len == RECV_BYTES) {
auto t1 = high_resolution_clock::now();

double d_in = 0;
if (count > 0) {
d_in = duration<double, milli>(t1 - last_recv_time).count();
sum_interval += d_in;
// 使用 std::min/max 避免宏冲突
min_interval = std::min(min_interval, d_in);
max_interval = std::max(max_interval, d_in);
}
last_recv_time = t1;

decode_packet(rx_buf, input_tensor_data);

auto t2 = high_resolution_clock::now();
req.infer();
float y_pred = req.get_output_tensor().data<float>()[0];
// y_pred = 80.0f;
auto t3 = high_resolution_clock::now();

sockaddr_in target_addr = client_addr;
target_addr.sin_port = htons(BOX_RECV_PORT);
uint32_t out_val = encode_result(y_pred);
sendto(sock_fd, &out_val, sizeof(out_val), 0,
(struct sockaddr*)&target_addr, sizeof(target_addr));
auto t4 = high_resolution_clock::now();

double d_proc = duration<double, milli>(t3 - t2).count();
double d_out = duration<double, milli>(t4 - t3).count();
double d_total = duration<double, milli>(t4 - t1).count();

if (count > 1000) {
max_jitter = std::max(max_jitter, d_total);
}

count++;
if (count % 500 == 0) {
double avg_in = (count > 1) ? sum_interval / (count - 1) : 0;
render_dashboard(count, input_tensor_data, y_pred,
d_in, avg_in, min_interval, max_interval,
d_proc, d_out, d_total, max_jitter);
}
}
}

close(sock_fd);
return 0;
}
编译

编译运行

1
2
3
cd ~/xiao/build && cmake .. && make -j4
cd ~/xiao/build && rm -rf * && cmake .. && make -j$(nproc)
sudo ./RealTimeHIL
帮助
cpu和gpu环境搭建和编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/bin/bash

# ==========================================================
# 🛡️ xiao 实时推理空间 (HIL) 旗舰版构建程序 V5.8
# 核心改进:修复 OpenVINO 编译错误、模型路径引导增强、路径全动态对齐
# ==========================================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

echo -e "${GREEN}==========================================================${NC}"
echo -e "${YELLOW}🛡️ 启动 xiao 实时推理“旗舰版”全自动构建程序 (全后端修复版)${NC}"
echo -e "${GREEN}==========================================================${NC}"

# ==========================================================
# [步骤 1/7] 核心隔离管理器 (针对 Xeon W-2225)
# ==========================================================
echo -e "\n${GREEN}[步骤 1/7] 检查 CPU 核心隔离状态${NC}"
ISOL_STR="isolcpus=6,7"
if grep -q "$ISOL_STR" "/etc/default/grub"; then
echo -e " ✅ 物理核心 8 (Core 7) 已隔离保护。"
else
echo -e " ❌ 未检测到核心隔离。"
read -p "❓ 是否执行核心隔离? (y/n) [默认y]: " do_isol
if [ "${do_isol:-y}" == "y" ]; then
sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=6,7 /' /etc/default/grub
sudo update-grub
echo -e "${YELLOW}🚀 配置已写入!必须重启电脑生效。脚本退出。${NC}"; exit 0
fi
fi

# ==========================================================
# [步骤 2/7] 部署模式选择
# ==========================================================
echo -e "\n${GREEN}[步骤 2/7] 推理后端模式选择${NC}"
echo -e " (1) CPU 模式 (OpenVINO)"
echo -e " (2) GPU 模式 (TensorRT)"
read -p "➡️ 请输入选项 (1/2): " mode_choice

if [ "$mode_choice" == "2" ]; then
DEPLOY_MODE="GPU"
DEFAULT_MODEL_NAME="pinnsformer_P.engine"
EXT=".engine"
else
DEPLOY_MODE="CPU"
DEFAULT_MODEL_NAME="pinnsformer_P.onnx"
EXT=".onnx"
fi

# ==========================================================
# [步骤 3/7] 工作空间与模型权重确认 (增强引导)
# ==========================================================
DEFAULT_WS="$HOME/xiao"
SIMULIK_PATH="$DEFAULT_WS/simulik"
BUILD_PATH="$DEFAULT_WS/build"
mkdir -p "$SIMULIK_PATH" "$BUILD_PATH"

DEFAULT_MODEL_PATH="$SIMULIK_PATH/$DEFAULT_MODEL_NAME"

while true; do
if [ -f "$DEFAULT_MODEL_PATH" ]; then
echo -e " ✅ 检测到模型: ${GREEN}$DEFAULT_MODEL_PATH${NC}"
read -p "❓ 确认使用此模型? (y/n) [默认y]: " c_use
if [ "${c_use:-y}" == "y" ]; then
FINAL_MODEL_PATH="$DEFAULT_MODEL_PATH"
break
fi
mv "$DEFAULT_MODEL_PATH" "$DEFAULT_MODEL_PATH.bak"
fi

echo -e " ❌ ${RED}未发现模型${NC}"
echo -e " 💡 请将后缀为 ${YELLOW}$EXT${NC} 的模型放入目录: ${YELLOW}$SIMULIK_PATH${NC}"
echo -e " 💡 或者直接在此处【拖入模型文件】/ 手动输入绝对路径:"
read -p "➡️ 路径: " manual_path
manual_path=$(echo "$manual_path" | sed "s/['\"]//g")
if [ -f "$manual_path" ]; then
cp "$manual_path" "$DEFAULT_MODEL_PATH"
echo -e " ✅ 模型已复制到 $SIMULIK_PATH"
fi
done

# ==========================================================
# [步骤 4/7] 严格环境复刻 (锁定 cu12 版本)
# ==========================================================
ENV_NAME="xiao"
CONDA_BASE=$(conda info --base)
ENV_PATH="$CONDA_BASE/envs/$ENV_NAME"

if [ ! -d "$ENV_PATH" ]; then
echo " 🛠️ 执行环境复刻 (锁定 cu12)..."
conda create -n $ENV_NAME python=3.10.12 -y
# 按照 gpu部署.docx 执行 [cite: 1-17]
$ENV_PATH/bin/pip install nvidia-cuda-runtime-cu12 nvidia-cudnn-cu12 -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=100
$ENV_PATH/bin/pip install --extra-index-url https://pypi.nvidia.com tensorrt-cu12==10.16.0.72 tensorrt-cu12-libs==10.16.0.72 tensorrt-cu12-bindings==10.16.0.72
$ENV_PATH/bin/pip install --extra-index-url https://pypi.nvidia.com tensorrt==10.16.0.72 --no-deps
$ENV_PATH/bin/pip uninstall tensorrt-cu12-dev tensorrt-cu12-libs -y
$ENV_PATH/bin/pip install --extra-index-url https://pypi.nvidia.com tensorrt-cu12-libs==10.16.0.72
$ENV_PATH/bin/pip install openvino==2024.6.0 onnx==1.16.0 --quiet
sudo apt-get install -y libnvinfer-headers-dev
fi

# ==========================================================
# [步骤 5/7] 路径精准对齐 (修复 OpenVINO 搜索逻辑)
# ==========================================================
echo -e "\n${GREEN}[步骤 5/7] 正在对齐库路径与头文件...${NC}"

# CPU/OpenVINO 路径搜寻
ENV_PYTHON="$ENV_PATH/bin/python3"
OV_DIR=$($ENV_PYTHON -c "import openvino; import os; p=os.path.join(os.path.dirname(openvino.__file__), 'cmake'); print(p) if os.path.exists(p) else print('')")
if [ -z "$OV_DIR" ]; then OV_DIR=$(find "$ENV_PATH" -name "OpenVINOConfig.cmake" 2>/dev/null | head -n 1 | xargs dirname); fi

# GPU/TensorRT 路径搜寻 [cite: 37]
POTENTIAL_CRT=$(find "$HOME" -path "*/include/crt/host_defines.h" 2>/dev/null | head -n 1)
SYSTEM_CUDA_INC=$(echo "$POTENTIAL_CRT" | sed 's/\/crt\/host_defines.h//')
[ -z "$SYSTEM_CUDA_INC" ] && SYSTEM_CUDA_INC="$HOME/cuda-11.8/targets/x86_64-linux/include"

TRT_LIB_PATH=$(find "$ENV_PATH" -name "libnvinfer.so.10" | head -n 1)
CUDA_LIB_PATH=$(find "$ENV_PATH" -name "libcudart.so.12" | head -n 1)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ENV_PATH/lib/python3.10/site-packages/nvidia/cuda_runtime/lib

read -p "❓ 请确认模型输入维度 (默认 9): " node_dim
node_dim=${node_dim:-9}

# ==========================================================
# [步骤 6/7] 源码全量生成 (日志提示完全对齐)
# ==========================================================
echo -e "\n${GREEN}[步骤 6/7] 生成 C++ 源码与 CMake 配置${NC}"

if [ "$DEPLOY_MODE" == "CPU" ]; then
# -------------------- CPU (OpenVINO) 详尽反馈版 --------------------
cat > "$DEFAULT_WS/main.cpp" <<EOF
#include <iostream>
#include <chrono>
#include <openvino/openvino.hpp>
#include <sys/mman.h>
#include <sched.h>
#include <pthread.h>

using namespace std;

void setup_realtime_fortress() {
mlockall(MCL_CURRENT | MCL_FUTURE);
struct sched_param param; param.sched_priority = 99;
sched_setscheduler(0, SCHED_FIFO, &param);
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(7, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
cout << ">>> [实时配置] 优先级: 99 | 已锁定隔离核心: CPU 8" << endl;
}

int main() {
setup_realtime_fortress();
try {
ov::Core core;
core.set_property("CPU", ov::inference_num_threads(1));
auto model = core.read_model("$FINAL_MODEL_PATH");

cout << "\n>>> [检查] OpenVINO 模型维度配置详情:" << endl;
for (const auto& input : model->inputs())
cout << " - Input [" << input.get_any_name() << "] | 维度: " << input.get_partial_shape() << endl;

model->reshape(ov::PartialShape({1, $node_dim}));
ov::CompiledModel compiled_model = core.compile_model(model, "CPU");
ov::InferRequest infer_request = compiled_model.create_infer_request();

cout << "\n>>> [热身] 正在进行 1000 次预热推理..." << endl;
for(int i=0; i<1000; i++) infer_request.infer();

cout << ">>> [压测] 开始 120s 模拟测试..." << endl;
double max_latency = 0, total_latency = 0;
long long count = 0;
auto start = chrono::steady_clock::now();

while (chrono::steady_clock::now() - start < chrono::seconds(120)) {
auto t1 = chrono::high_resolution_clock::now();
infer_request.infer();
auto t2 = chrono::high_resolution_clock::now();
double duration = chrono::duration<double, milli>(t2 - t1).count();
if (count > 50 && duration > max_latency) max_latency = duration;
total_latency += duration;
count++;
}

cout << "\n========================================" << endl;
cout << "CPU 推理报告 (OpenVINO 2024.6)" << endl;
cout << "最大延迟 (Max): " << max_latency << " ms" << endl;
cout << "平均延迟: " << (total_latency / count) << " ms" << endl;
cout << "1ms 闭环判定: " << (max_latency < 1.0 ? "✅ 达标" : "❌ 超时") << endl;
cout << "========================================\n" << endl;
} catch (const exception& ex) { cerr << ex.what() << endl; return 1; }
return 0;
}
EOF

# 修复后的 CPU CMakeLists
cat > "$DEFAULT_WS/CMakeLists.txt" <<EOF
cmake_minimum_required(VERSION 3.10)
project(RealTimeHIL)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)

set(OpenVINO_DIR "$OV_DIR")
find_package(OpenVINO REQUIRED HINTS "$OV_DIR")

add_executable(RealTimeHIL main.cpp)
target_link_libraries(RealTimeHIL PRIVATE openvino::runtime pthread)
EOF

else
# -------------------- GPU (TensorRT) 源码 (保持详尽提示) --------------------
cat > "$DEFAULT_WS/main.cpp" <<EOF
#include <iostream>
#include <fstream>
#include <vector>
#include <chrono>
#include <cuda_runtime_api.h>
#include <NvInfer.h>
#include <sys/mman.h>
#include <sched.h>
#include <pthread.h>

using namespace nvinfer1;
using namespace std;

class TRTLogger : public ILogger {
void log(Severity severity, const char* msg) noexcept override {
if (severity <= Severity::kWARNING) cout << "[TRT] " << msg << endl;
}
} gLogger;

void setup_realtime_context() {
mlockall(MCL_CURRENT | MCL_FUTURE);
struct sched_param param; param.sched_priority = 98;
sched_setscheduler(0, SCHED_FIFO, &param);
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(7, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
cout << ">>> [实时配置] 优先级: 98 | 已锁定隔离核心: CPU 8" << endl;
}

int main() {
setup_realtime_context();
const string engine_path = "$FINAL_MODEL_PATH";
ifstream file(engine_path, ios::binary);
vector<char> engine_data((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(engine_data.data(), engine_data.size());
IExecutionContext* context = engine->createExecutionContext();

cout << "\n>>> [检查] TensorRT 模型维度配置详情:" << endl;
for (int i = 0; i < engine->getNbIOTensors(); ++i) {
auto name = engine->getIOTensorName(i);
Dims dims = engine->getTensorShape(name);
cout << " - Tensor [" << i << "]: " << name << " | 维度: [";
for (int j = 0; j < dims.nbDims; ++j) cout << dims.d[j] << (j < dims.nbDims - 1 ? "x" : "");
cout << "]" << endl;
}

float *h_input, *h_output; void *d_input, *d_output;
cudaHostAlloc((void**)&h_input, $node_dim * sizeof(float), cudaHostAllocDefault);
cudaHostAlloc((void**)&h_output, 1 * sizeof(float), cudaHostAllocDefault);
cudaMalloc(&d_input, $node_dim * sizeof(float));
cudaMalloc(&d_output, 1 * sizeof(float));

context->setTensorAddress("state_input", d_input);
context->setTensorAddress("P_pred", d_output);

cudaStream_t stream; cudaStreamCreate(&stream);
cout << "\n>>> [热身] 正在进行 5000 次深度预热..." << endl;
for(int i=0; i<5000; i++) context->enqueueV3(stream);
cudaStreamSynchronize(stream);

auto start = chrono::steady_clock::now();
while (chrono::steady_clock::now() - start < chrono::seconds(120)) {
cudaMemcpyAsync(d_input, h_input, $node_dim * sizeof(float), cudaMemcpyHostToDevice, stream);
context->enqueueV3(stream);
cudaMemcpyAsync(h_output, d_output, 1 * sizeof(float), cudaMemcpyDeviceToHost, stream);
cudaStreamSynchronize(stream);
}
return 0;
}
EOF

# 修复后的 GPU CMakeLists [cite: 37]
cat > "$DEFAULT_WS/CMakeLists.txt" <<EOF
cmake_minimum_required(VERSION 3.10)
project(RealTimeHIL)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)

include_directories(/usr/include/x86_64-linux-gnu)
include_directories("$SYSTEM_CUDA_INC")

set(TRT_SO "$TRT_LIB_PATH")
set(CUDA_SO "$CUDA_LIB_PATH")

add_executable(RealTimeHIL main.cpp)
target_link_libraries(RealTimeHIL PRIVATE \${TRT_SO} \${CUDA_SO} pthread dl)
EOF

fi

# ==========================================================
# [步骤 7/7] 自动编译任务 (包含路径修复)
# ==========================================================
echo -e "\n${GREEN}[步骤 7/7] 启动自动编译任务${NC}"
cd "$BUILD_PATH"
rm -rf * # CPU 编译时显式注入路径
if [ "$DEPLOY_MODE" == "CPU" ]; then
cmake .. -DOpenVINO_DIR="$OV_DIR"
else
cmake ..
fi

if make -j$(nproc); then
echo -e "\n${GREEN}==========================================================${NC}"
echo -e "🎉 部署任务全量完成!"
echo -e " 🚀 执行指令:${YELLOW}cd $BUILD_PATH && sudo ./RealTimeHIL${NC}"
echo -e "${GREEN}==========================================================${NC}"
else
echo -e "\n${RED}❌ 编译失败!请检查 OpenVINO 路径是否正确。${NC}"
exit 1
fi
npu环境搭建和编译
pth文件转onnx文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import torch
import os
import sys
import importlib.util
import inspect

def get_user_input(prompt, default=None):
if default is not None:
user_input = input(f"{prompt} [默认: {default}]: ").strip()
return user_input if user_input else default
return input(f"{prompt}: ").strip()

def load_model_class(model_py_path, class_name):
"""动态加载用户提供的 .py 文件中的类"""
module_name = os.path.basename(model_py_path).replace(".py", "")
spec = importlib.util.spec_from_file_location(module_name, model_py_path)
module = importlib.util.module_from_spec(spec)
sys.path.insert(0, os.path.dirname(model_py_path)) # 加入系统路径以防内部 import 失败
spec.loader.exec_module(module)
return getattr(module, class_name)

def main():
print("=== 通用模型导出工具 (.pth -> .onnx) ===")

# 1. 获取模型定义
model_py = get_user_input("1. 请输入模型定义文件路径 (例如 pinnsformer_model.py)", default="pinnsformer_model.py")
class_name = get_user_input("2. 请输入模型类名 (例如 PINNsFormer)", default="PINNsFormer")

try:
ModelClass = load_model_class(model_py, class_name)
# 获取构造函数的参数列表,方便给用户提示
sig = inspect.signature(ModelClass.__init__)
params = [p for p in sig.parameters if p != 'self']
print(f"检测到 {class_name} 的初始化参数: {params}")
except Exception as e:
print(f"加载类失败: {e}")
return

# 3. 收集初始化参数
print("\n--- 请输入初始化参数 (直接回车使用默认值) ---")
init_kwargs = {}
for p in params:
val = get_user_input(f"参数 {p}")
if val:
# 尝试自动转为整数或浮点数,否则保持字符串
try:
if '.' in val: init_kwargs[p] = float(val)
else: init_kwargs[p] = int(val)
except:
init_kwargs[p] = val

# 4. 加载权重
ckpt_path = get_user_input("\n3. 请输入 .pth 权重文件路径")
if not os.path.exists(ckpt_path):
print("错误: 找不到权重文件")
return

# 5. 精度选择
precision = get_user_input("4. 选择导出精度: 0-FP32 (默认), 1-FP16", default="0")

# 6. 实例化与加载
try:
model = ModelClass(**init_kwargs)
model.load_state_dict(torch.load(ckpt_path, map_location='cpu'))
model.eval()

if precision == "1":
model = model.half()
print(">>> 已切换至 FP16 模式")
except Exception as e:
print(f"模型实例化或加载权重失败: {e}")
return

# 7. 导出设置
d_in = init_kwargs.get('d_in', 9) # 尝试获取输入维度,默认为 9
input_name = get_user_input("5. 输入节点名称", default="state_input")
output_name = get_user_input("6. 输出节点名称", default="P_pred")
onnx_path = ckpt_path.replace(".pth", ".onnx")

print(f"\n>>> 正在导出至: {onnx_path} ...")
dummy_input = torch.randn(1, int(d_in))
if precision == "1": dummy_input = dummy_input.half()

torch.onnx.export(
model,
dummy_input,
onnx_path,
export_params=True,
opset_version=13,
input_names=[input_name],
output_names=[output_name],
dynamic_axes={input_name: {0: 'batch_size'}, output_name: {0: 'batch_size'}}
)
print(f"✅ 成功导出!")

if __name__ == "__main__":
main()
onnx文件转engine文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import tensorrt as trt
import os
import argparse

def get_user_input(prompt, default=None):
"""获取用户输入,如果用户直接按回车则返回默认值"""
if default:
user_input = input(f"{prompt} [{default}]: ").strip()
return user_input if user_input else default
return input(f"{prompt}: ").strip()

def build_engine(onnx_path, engine_path, workspace_size=1, use_fp16=False):
# 验证输入路径
if not os.path.exists(onnx_path):
print(f"\n[错误] 找不到 ONNX 文件: {onnx_path}")
return

logger = trt.Logger(trt.Logger.INFO)
builder = trt.Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, logger)
config = builder.create_builder_config()

# 显存设置
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, int(workspace_size) << 30)

# 精度设置
if use_fp16:
if builder.platform_has_fast_fp16:
config.set_flag(trt.BuilderFlag.FP16)
print(">>> 模式: 已开启 FP16 高速推理")
else:
print(">>> 警告: 硬件不支持快速 FP16,回退至 FP32")

print(f"\n>>> 正在解析: {onnx_path} ...")
with open(onnx_path, 'rb') as model:
if not parser.parse(model.read()):
for error in range(parser.num_errors):
print(f"解析错误: {parser.get_error(error)}")
return

# 打印节点
print("-" * 30)
for i in range(network.num_inputs):
t = network.get_input(i)
print(f"输入: '{t.name}', 形状: {t.shape}")
for i in range(network.num_outputs):
t = network.get_output(i)
print(f"输出: '{t.name}', 形状: {t.shape}")
print("-" * 30)

print(">>> 正在构建 Engine,请稍候...")
serialized_engine = builder.build_serialized_network(network, config)

if serialized_engine:
with open(engine_path, 'wb') as f:
f.write(serialized_engine)
print(f"\n[成功] Engine 已保存至: {engine_path}")
else:
print("\n[失败] 构建过程中出现问题")

def main():
print("=== TensorRT Engine 交互构建工具 ===")

# 尝试从交互式输入获取
onnx_path = get_user_input("请输入 ONNX 模型的完整路径")

# 自动生成默认的输出路径
default_engine = onnx_path.replace(".onnx", ".engine") if ".onnx" in onnx_path else "model.engine"
engine_path = get_user_input("请输入输出 Engine 的保存路径", default=default_engine)

fp16_choice = get_user_input("是否开启 FP16 模式? (y/n)", default="n").lower()
use_fp16 = True if fp16_choice == 'y' else False

workspace = get_user_input("最大工作显存 (单位 GB)", default="1")

# 执行构建
build_engine(onnx_path, engine_path, workspace, use_fp16)

if __name__ == "__main__":
main()
onnx文件转HEF文件
C++下的onnx精度验证脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/bin/bash

# ==========================================================
# 🛡️ 精度验证工具 (V6.3) - 多格式自适应与物理特征对齐
# 核心功能:支持 .mat/.csv 双格式,自动嗅探环境,自动对齐特征
# ==========================================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

echo -e "${GREEN}==========================================================${NC}"
echo -e "${YELLOW}🛡️ 启动模型验证编排器 (全格式通用模式)${NC}"
echo -e "${GREEN}==========================================================${NC}"

# ==========================================================
# [步骤 1] 智能环境嗅探
# ==========================================================
echo -e "\n${GREEN}[步骤 1] 自动探测科研环境...${NC}"
CONDA_BASE=$(conda info --base)
ALL_ENVS=$(conda env list | grep -v "#" | awk '{print $1}')

PY_ENV_NAME=""
CPP_ENV_NAME=""

for env in $ALL_ENVS; do
if [ -z "$PY_ENV_NAME" ]; then
BIN="$CONDA_BASE/bin/python3"; [ "$env" != "base" ] && BIN="$CONDA_BASE/envs/$env/bin/python3"
$BIN -c "import torch, scipy" 2>/dev/null && PY_ENV_NAME=$env && PY_BIN=$BIN
fi
if [ -z "$CPP_ENV_NAME" ]; then
BIN="$CONDA_BASE/envs/$env/bin/python3"
$BIN -c "import openvino" 2>/dev/null && CPP_ENV_NAME=$env && CPP_BIN=$BIN
fi
done

echo -e " ✅ 环境探测: Python端(${YELLOW}$PY_ENV_NAME${NC}) | C++端(${YELLOW}$CPP_ENV_NAME${NC})"

# ==========================================================
# [步骤 2] 数据源解析 (支持 .mat / .csv)
# ==========================================================
echo -e "\n${GREEN}[步骤 2] 数据源解析与特征对齐${NC}"
WORK_DIR="$(pwd)/verify_workspace"
mkdir -p "$WORK_DIR"

# 自动识别当前目录下的数据文件
DEFAULT_DATA=$(ls *.mat *.csv 2>/dev/null | head -n 1)
read -p "➡️ 请输入数据文件路径 (.mat 或 .csv, 默认 $DEFAULT_DATA): " data_path
data_path=$(readlink -f "${data_path:-$DEFAULT_DATA}")
[ ! -f "$data_path" ] && { echo -e "${RED}错误: 文件不存在${NC}"; exit 1; }

EXT="${data_path##*.}"

if [ "$EXT" == "mat" ]; then
echo -e " 📂 检测到 ${YELLOW}.mat${NC} 格式,正在提取 Key..."
$PY_BIN -c "import scipy.io as sio; d=sio.loadmat('$data_path'); [print(f'{i}|{k}') for i,k in enumerate([k for k in d.keys() if not k.startswith(\"__\")])]" > "$WORK_DIR/keys_map.txt"
else
echo -e " 📂 检测到 ${YELLOW}.csv${NC} 格式,正在读取表头..."
$PY_BIN -c "import pandas as pd; df=pd.read_csv('$data_path'); [print(f'{i}|{c}') for i,c in enumerate(df.columns)]" > "$WORK_DIR/keys_map.txt"
fi

# 智能识别 9 维物理顺序
TARGET_X=("Vbus" "Vbus_next" "io" "ib" "ib_prev" "isc" "isc_prev" "Ts" "C")
AUTO_X=""
for target in "${TARGET_X[@]}"; do
idx=$(grep -i "|$target$" "$WORK_DIR/keys_map.txt" | cut -d'|' -f1)
if [ ! -z "$idx" ]; then AUTO_X="$AUTO_X $idx"; fi
done
AUTO_Y=$(grep -i "|P$" "$WORK_DIR/keys_map.txt" | cut -d'|' -f1)

# 确认配置
if [ $(echo "$AUTO_X" | wc -w) -ge 7 ]; then
echo -e " ✨ ${GREEN}自动识别成功!已匹配到物理特征列。${NC}"
x_idxs=$AUTO_X; y_idx=$AUTO_Y
else
cat "$WORK_DIR/keys_map.txt" | sed 's/|/ : /'
read -p "➡️ 请手动输入 P 的编号: " y_idx
read -p "➡️ 请手动输入 X 编号序列 (按模型输入顺序): " x_idxs
fi

X_KEYS_STR=$($PY_BIN -c "import pandas as pd; import scipy.io as sio; ks=[];
if '$EXT'=='mat': ks=[k for k in sio.loadmat('$data_path').keys() if not k.startswith('__')]
else: ks=list(pd.read_csv('$data_path').columns)
print(' '.join([ks[int(i)] for i in '$x_idxs'.split()]))")

Y_KEY_NAME=$($PY_BIN -c "import pandas as pd; import scipy.io as sio; ks=[];
if '$EXT'=='mat': ks=[k for k in sio.loadmat('$data_path').keys() if not k.startswith('__')]
else: ks=list(pd.read_csv('$data_path').columns)
print(ks[$y_idx])")

FINAL_IN_DIM=$(echo "$x_idxs" | wc -w)

# ==========================================================
# [步骤 3] 基准推理 (Python端生成 validation_data.csv)
# ==========================================================
echo -e "\n${GREEN}[步骤 3] 运行 $PY_ENV_NAME 推理并生成对标 CSV${NC}"
DEFAULT_PT=$(ls *.pt 2>/dev/null | head -n 1)
read -p "➡️ .pt 模型路径 (默认 $DEFAULT_PT): " pt_path
pt_path=$(readlink -f "${pt_path:-$DEFAULT_PT}")

cat > "$WORK_DIR/gen_data.py" <<EOF
import torch, scipy.io as sio, pandas as pd, numpy as np
try:
# 1. 加载原始数据
if '$EXT' == 'mat':
data = sio.loadmat('$data_path')
x_keys = '$X_KEYS_STR'.split()
ref_len = data['Vbus'].size if 'Vbus' in data else max([data[k].size for k in x_keys if data[k].size > 1])
cols = [np.full((ref_len, 1), data[k].item(), dtype=np.float32) if data[k].size==1 else data[k].astype(np.float32).reshape(-1, 1) for k in x_keys]
X = np.hstack(cols)
Y_true = data['$Y_KEY_NAME'].flatten()
else:
df_raw = pd.read_csv('$data_path')
X = df_raw['$X_KEYS_STR'.split()].values.astype(np.float32)
Y_true = df_raw['$Y_KEY_NAME'].values.flatten()

# 2. PT 模型推理
model = torch.jit.load('$pt_path').cpu().eval()
with torch.no_grad():
y_pt = model(torch.from_numpy(X)).numpy().flatten()

# 3. 构造并保存采样数据
res_df = pd.DataFrame(X)
res_df['P_True'] = Y_true
res_df['P_PT'] = y_pt
total = len(res_df)
sample = pd.concat([res_df.head(100), res_df.iloc[total//2-50:total//2+50], res_df.tail(100)]) if total > 300 else res_df
sample.to_csv('$WORK_DIR/validation_data.csv', index=False)
print(" ✅ validation_data.csv 已生成 (包含 P_PT 列)")
except Exception as e:
import traceback; traceback.print_exc(); exit(1)
EOF

$PY_BIN "$WORK_DIR/gen_data.py" || exit 1

# ==========================================================
# [步骤 4] 编译与验证 (C++ 端)
# ==========================================================
echo -e "\n${GREEN}[步骤 4] 构建 C++ 对标程序 (xiao 环境)${NC}"
DEFAULT_ONNX=$(ls simulik/*.onnx *.onnx 2>/dev/null | head -n 1)
read -p "➡️ .onnx 路径 (默认 $DEFAULT_ONNX): " onnx_path
onnx_path=$(readlink -f "${onnx_path:-$DEFAULT_ONNX}")

OV_DIR=$($CPP_BIN -c "import openvino; import os; p=os.path.join(os.path.dirname(openvino.__file__), 'cmake'); print(p) if os.path.exists(p) else print('')")
if [ -z "$OV_DIR" ]; then OV_DIR=$(find "$CONDA_BASE/envs/xiao" -name "OpenVINOConfig.cmake" 2>/dev/null | head -n 1 | xargs dirname); fi

cat > "$WORK_DIR/CMakeLists.txt" <<EOF
cmake_minimum_required(VERSION 3.10)
project(VerifyHIL)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
set(OpenVINO_DIR "$OV_DIR")
find_package(OpenVINO REQUIRED)
add_executable(verify verify.cpp)
target_link_libraries(verify PRIVATE openvino::runtime pthread)
EOF

cat > "$WORK_DIR/verify.cpp" <<EOF
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <openvino/openvino.hpp>
using namespace std;

vector<float> parse_csv(const string& l) {
vector<float> r; stringstream ss(l); string v;
while(getline(ss, v, ',')) { try{r.push_back(stof(v));}catch(...){}}
return r;
}

int main() {
try {
ov::Core core; auto model = core.read_model("$onnx_path");
auto compiled = core.compile_model(model, "CPU");
auto req = compiled.create_infer_request();
float* input_ptr = req.get_input_tensor().data<float>();

ifstream f("$WORK_DIR/validation_data.csv");
string l; getline(f, l);
int count = 0; double total_mae = 0;
int in_dim = $FINAL_IN_DIM;

cout << "ID | True_P | PT_Pred | OV_Pred | Error(OV-PT)" << endl;
while(getline(f, l)) {
auto r = parse_csv(l);
if(r.size() < (size_t)(in_dim + 2)) continue;
for(int i=0; i<in_dim; ++i) input_ptr[i] = r[i];
req.infer();
float ov_p = req.get_output_tensor().data<float>()[0];
float diff = abs(ov_p - r[in_dim+1]);
total_mae += diff; count++;
if(count%50==0 || count<=3) printf("%d | %.4f | %.4f | %.4f | %.2e\\n", count, r[in_dim], r[in_dim+1], ov_p, diff);
}
cout << "------------------------------------------" << endl;
cout << "对标完成!平均误差 (MAE): " << total_mae / count << endl;
} catch(const exception& e) { cerr << e.what() << endl; }
return 0;
}
EOF

echo -e "\n${GREEN}[步骤 5] 执行自动编译与验证...${NC}"
mkdir -p "$WORK_DIR/build" && cd "$WORK_DIR/build"
cmake .. && make -j$(nproc)

if [ $? -eq 0 ]; then
echo -e "\n${YELLOW}>>> 验证结果如下: <<<${NC}"
./verify
fi
C++下的onnx与UDP实时性验证脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/bin/bash

# ==========================================================
# 🛡️ HIL 全自动自生型部署脚本 (V10.3)
# 针对 Xeon W-2225 优化 | 解决 UI 歪斜与进程堆积
# 特性:去掉 -f 标志、全量代码生成、Conda 路径探测、一键部署运行
# ==========================================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

WS=$(pwd)

# ----------------------------------------------------------
# 1. 部署状态自检 (已部署则询问直接启动)
# ----------------------------------------------------------
if [[ -f "$WS/edge_node/build/HIL_Edge_Core" && -f "$WS/box_side/build/Box_Simulator" ]]; then
echo -e "${GREEN}检测到系统已完成部署。${NC}"
read -p "是否跳过部署直接运行测试? [回车确认 / N 重新部署]: " SKIP_RUN
if [[ -z "$SKIP_RUN" || "$SKIP_RUN" == "y" ]]; then
goto_run=true
fi
fi

if [[ "$goto_run" != true ]]; then
# ----------------------------------------------------------
# 2. 文件夹初始化 (处理“空文件夹”情况)
# ----------------------------------------------------------
echo -e "\n${GREEN}[1/5] 正在初始化物理空间...${NC}"
mkdir -p $WS/edge_node/build
mkdir -p $WS/box_side/build
mkdir -p $WS/simulik
echo -e " ✅ 目录结构已就绪。"

# ----------------------------------------------------------
# 3. 智能环境探测 (自动寻找 xiao 环境下的 OpenVINO)
# ----------------------------------------------------------
echo -e "\n${GREEN}[2/5] 正在全盘扫描 xiao 环境中的 OpenVINO 编译器...${NC}"

# 探测 Conda 根目录
CONDA_ROOT=$(conda info --base 2>/dev/null || echo "$HOME/anaconda3")

# 在所有环境里寻找含有 openvino 的 cmake 路径
OV_CMAKE=$(find "$CONDA_ROOT/envs" -maxdepth 6 -name "cmake" -path "*/openvino/*" -type d 2>/dev/null | head -n 1)

if [ -z "$OV_CMAKE" ] || [ ! -d "$OV_CMAKE" ]; then
echo -e "${RED}❌ 错误:在任何 Conda 环境中均未找到 OpenVINO 路径!${NC}"
echo -e "请确认是否安装了 OpenVINO,或者环境名是否正确。"
exit 1
fi
echo -e " 📍 成功锁定编译器: ${YELLOW}$OV_CMAKE${NC}"

# ----------------------------------------------------------
# 4. 源码与配置“无中生有” (Here Document 注入)
# ----------------------------------------------------------
echo -e "\n${GREEN}[3/5] 正在生成所有 C++ 源码、CMake 配置与说明文档...${NC}"

# --- A. 推理端 CMakeLists ---
cat > $WS/edge_node/CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.10)
project(EdgeNode)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
set(OpenVINO_DIR "$OV_CMAKE")
find_package(OpenVINO REQUIRED)
add_executable(HIL_Edge_Core HIL_Edge_Core.cpp)
target_link_libraries(HIL_Edge_Core PRIVATE openvino::runtime pthread)
EOF

# --- B. 推理端源码 (锁定 Core 7 / CPU 8) ---
cat > $WS/edge_node/HIL_Edge_Core.cpp <<EOF
#include <iostream>
#include <chrono>
#include <openvino/openvino.hpp>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sched.h>

using namespace std;
struct BoxPacket { uint32_t data[7]; int64_t t1; };
struct EdgeResult { uint32_t val; int64_t t1; int64_t t2; int64_t t3; };

int main() {
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(7, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
struct sched_param param; param.sched_priority = 99;
sched_setscheduler(0, SCHED_FIFO, &param);
mlockall(MCL_CURRENT | MCL_FUTURE);

ov::Core core;
auto model = core.read_model("/home/henu/xiao/simulik/pinnsformer_P.onnx");
model->reshape(ov::PartialShape({1, 9}));
auto compiled = core.compile_model(model, "CPU", {{"INFERENCE_NUM_THREADS", "1"}});
auto req = compiled.create_infer_request();
float* input = req.get_input_tensor().data<float>();

int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr_rx{}, addr_tx{};
addr_rx.sin_family = AF_INET; addr_rx.sin_port = htons(12345); addr_rx.sin_addr.s_addr = INADDR_ANY;
bind(sock, (struct sockaddr*)&addr_rx, sizeof(addr_rx));
addr_tx.sin_family = AF_INET; addr_tx.sin_port = htons(12346); inet_pton(AF_INET, "127.0.0.1", &addr_tx.sin_addr);

BoxPacket rx_pkt; EdgeResult tx_pkt;
while (true) {
recvfrom(sock, &rx_pkt, sizeof(rx_pkt), 0, nullptr, nullptr);
auto t2 = chrono::high_resolution_clock::now().time_since_epoch().count();
for(int i=0; i<7; ++i) input[i] = 1.0f; input[7] = 1e-4f; input[8] = 5e-4f;
req.infer();
auto t3 = chrono::high_resolution_clock::now().time_since_epoch().count();
tx_pkt.val = 1; tx_pkt.t1 = rx_pkt.t1; tx_pkt.t2 = t2; tx_pkt.t3 = t3;
sendto(sock, &tx_pkt, sizeof(tx_pkt), 0, (struct sockaddr*)&addr_tx, sizeof(addr_tx));
}
return 0;
}
EOF

# --- C. 监控端 CMakeLists ---
cat > $WS/box_side/CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.10)
project(BoxSimulator)
set(CMAKE_CXX_STANDARD 17)
add_executable(Box_Simulator Box_Simulator.cpp)
target_link_libraries(Box_Simulator PRIVATE pthread)
EOF

# --- D. 监控端源码 (带 \r 绝对对齐) ---
cat > $WS/box_side/Box_Simulator.cpp <<EOF
#include <iostream>
#include <chrono>
#include <thread>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iomanip>

using namespace std;
struct BoxPacket { uint32_t data[7]; int64_t t1; };
struct EdgeResult { uint32_t val; int64_t t1; int64_t t2; int64_t t3; };

void render_ui(double d_in, double d_proc, double d_out, double d_total, double max_j, int count) {
printf("\033[1;1H");
printf("\r================================================\n");
printf("\r HIL 实时闭环监控系统 V10.3 (1ms) \n");
printf("\r================================================\n");
printf("\r%-24s : %d\n", "当前循环周期 (ID)", count);
printf("\r------------------------------------------------\n");
printf("\r%-24s : %8.4f ms\n", "1. 机箱 -> 边缘机 (UDP)", d_in);
printf("\r%-24s : %8.4f ms\n", "2. 边缘机推理处理", d_proc);
printf("\r%-24s : %8.4f ms\n", "3. 边缘机 -> 机箱 (UDP)", d_out);
printf("\r------------------------------------------------\n");
const char* tag = (d_total < 1.0) ? "\033[1;32m[ 达标 ]\033[0m" : "\033[1;31m[ 超时 ]\033[0m";
printf("\r%-24s : %8.4f ms %s\n", "闭环总时延 (RTT)", d_total, tag);
printf("\r%-24s : %8.4f ms\n", "推理峰值抖动 (Max)", max_j);
printf("\r================================================\n");
fflush(stdout);
}

int main() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr_edge{}, addr_local{};
addr_edge.sin_family = AF_INET; addr_edge.sin_port = htons(12345);
inet_pton(AF_INET, "127.0.0.1", &addr_edge.sin_addr);
addr_local.sin_family = AF_INET; addr_local.sin_port = htons(12346);
addr_local.sin_addr.s_addr = INADDR_ANY; bind(sock, (struct sockaddr*)&addr_local, sizeof(addr_local));
BoxPacket tx_pkt; EdgeResult rx_pkt; double max_jitter = 0; int cycle = 0; printf("\033[2J");
while (true) {
auto t1_now = chrono::high_resolution_clock::now();
tx_pkt.t1 = t1_now.time_since_epoch().count();
sendto(sock, &tx_pkt, sizeof(tx_pkt), 0, (struct sockaddr*)&addr_edge, sizeof(addr_edge));
if (recvfrom(sock, &rx_pkt, sizeof(rx_pkt), 0, nullptr, nullptr) > 0) {
auto t4_now = chrono::high_resolution_clock::now().time_since_epoch().count();
double d_in = (double)(rx_pkt.t2 - rx_pkt.t1) / 1e6;
double d_proc = (double)(rx_pkt.t3 - rx_pkt.t2) / 1e6;
double d_total = (double)(t4_now - rx_pkt.t1) / 1e6;
if (cycle > 1000 && d_proc > max_jitter) max_jitter = d_proc;
if (++cycle % 500 == 0) render_ui(d_in, d_proc, (double)(t4_now - rx_pkt.t3)/1e6, d_total, max_jitter, cycle);
}
this_thread::sleep_until(t1_now + chrono::milliseconds(1));
}
return 0;
}
EOF

# --- E. 生成模型迁移说明 ---
cat > $WS/README_MODEL_MIGRATION.txt <<EOF
模型更换指南 (交给大模型修改):
1. 请提供 edge_node/HIL_Edge_Core.cpp 给大模型。
2. 指令示例: "请修改代码以适配新模型 new.onnx,其输入维度为 [1, 12]。
请同步更新 read_model 路径、reshape 参数以及 input 数组的填充逻辑。"
EOF

# ----------------------------------------------------------
# 5. 自动化构建
# ----------------------------------------------------------
echo -e "\n${GREEN}[4/5] 并行编译实时工程...${NC}"
cd $WS/edge_node/build && cmake .. >/dev/null && make -j$(nproc) >/dev/null
cd $WS/box_side/build && cmake .. >/dev/null && make -j$(nproc) >/dev/null
echo -e " ✅ 部署成功。"
fi

# ----------------------------------------------------------
# 6. 交互式启动与监控
# ----------------------------------------------------------
echo -e "\n${GREEN}[5/5] 启动闭环实时链路...${NC}"

# 强力清场 (去掉 -f 标志)
EXIST_EDGE=$(pgrep HIL_Edge_Core)
if [ ! -z "$EXIST_EDGE" ]; then
read -p "检测到核心 8 已被占用,回车清理并运行: "
( sudo pkill -9 HIL_Edge_Core; pkill -9 Box_Simulator ) >/dev/null 2>&1
sleep 1
fi

# 硬件优化
echo 1 | sudo tee /proc/irq/default_smp_affinity > /dev/null

# 启动任务
sudo taskset -c 7 $WS/edge_node/build/HIL_Edge_Core > /dev/null 2>&1 &
EDGE_PID=$!
sleep 2
clear
taskset -c 5 $WS/box_side/build/Box_Simulator

# 资源回收逻辑
cleanup() {
( sudo kill -9 $EDGE_PID; sudo pkill -9 HIL_Edge_Core; pkill -9 Box_Simulator ) >/dev/null 2>&1
echo -e "\n${GREEN}>>> 实时测试结束。${NC}"; exit 0
}
trap cleanup SIGINT SIGTERM
C++下的onnx与UDP真实传输启动脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#!/bin/bash

# ==========================================================
# 🛡️ HIL 闭环开发与实装脚本 (V12.1 智能引导版)
# 特性:模型路径自动校验、引导式交互输入、1ms 阈值红绿灯
# ==========================================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

WS=$(pwd)
sudo -v # 预验证权限

# ----------------------------------------------------------
# 1. 模型路径校验与指引 (核心新增逻辑)
# ----------------------------------------------------------
echo -e "\n${YELLOW}===== [步骤 1/5] 模型路径校验 =====${NC}"
DEFAULT_MODEL_DIR="$WS/simulik"
DEFAULT_MODEL_NAME="pinnsformer_P.onnx"
DEFAULT_MODEL_PATH="$DEFAULT_MODEL_DIR/$DEFAULT_MODEL_NAME"

# 检查默认路径是否存在模型
if [ -f "$DEFAULT_MODEL_PATH" ]; then
echo -e "${GREEN}✅ 在默认路径找到模型:${NC}$DEFAULT_MODEL_PATH"
FINAL_MODEL_PATH=$DEFAULT_MODEL_PATH
else
echo -e "${RED}❌ 默认路径未找到模型:${NC}$DEFAULT_MODEL_PATH"
echo -e "${YELLOW}💡 提示:您可以将模型放在上述文件夹,或在下方输入自定义路径。${NC}"
echo -e "示例输入:/home/henu/xiao/models/my_model.onnx"

read -p "请输入 ONNX 模型完整路径 (或按 Ctrl+C 退出): " USER_PATH
if [ -f "$USER_PATH" ]; then
echo -e "${GREEN}✅ 已确认自定义模型:${NC}$USER_PATH"
FINAL_MODEL_PATH=$USER_PATH
else
echo -e "${RED}❌ 错误:路径 $USER_PATH 不存在或不是文件,程序无法启动。${NC}"
exit 1
fi
fi

# ----------------------------------------------------------
# 2. 运行模式与部署选项
# ----------------------------------------------------------
echo -e "\n${YELLOW}===== [步骤 2/5] 运行模式选择 =====${NC}"
echo "1. 模拟模式 (启动推理机 + 随机数据模拟器)"
echo "2. 实装模式 (只启动推理机,对接硬件机箱)"
read -p "请选择模式 [默认 1]: " RUN_MODE
RUN_MODE=${RUN_MODE:-1}

read -p "是否需要重新生成源码 (覆盖旧的 IP/路径)? [y/N]: " REWRITE_SRC
read -p "是否需要重新执行编译? [y/N]: " NEED_BUILD

# ----------------------------------------------------------
# 3. 源码生成逻辑 (增强了报错提示)
# ----------------------------------------------------------
if [[ "$REWRITE_SRC" == "y" || "$REWRITE_SRC" == "Y" ]]; then
echo -e "\n${GREEN}[步骤 3/5] 正在生成核心源码...${NC}"
mkdir -p $WS/edge_node/build $WS/box_side/build

# --- 推理端 (HIL_Edge_Core.cpp) ---
cat > $WS/edge_node/HIL_Edge_Core.cpp <<EOF
#include <iostream>
#include <chrono>
#include <openvino/openvino.hpp>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sched.h>
#include <iomanip>

using namespace std;

// [部署配置]
const char* TARGET_IP = "127.0.0.1"; // 联调时改此 IP
const int DEST_PORT = 12346;
const double SCALE = 100000.0; const double OFFSET = 1200000000.0;
const float TS_VAL = 1e-4f; const float C_VAL = 5e-4f;

struct BoxPacket { uint32_t data[7]; int64_t t1; };
struct EdgeResult { uint32_t val; int64_t t1; int64_t t2; int64_t t3; };

void render_dashboard(const float* phys, double d_in, double d_proc, double d_out, double d_total, float p, int count, double max_j) {
printf("\033[1;1H");
const char* status_tag = (d_total <= 1.0) ? "● NORMAL" : "▲ TIMEOUT";
const char* status_color = (d_total <= 1.0) ? "\033[1;32m" : "\033[1;31m";

printf("===========================================================\n");
printf(" HIL 实时监控 V12.1 | 状态: %s%s\033[0m \n", status_color, status_tag);
printf("===========================================================\n");
printf(" 循环次数: %-10d | RTT 峰值抖动: \033[1;33m%8.4f ms\033[0m \n", count, max_j);
printf("-----------------------------------------------------------\n");
printf(" 1. UDP In (Box->Edge) | %8.4f ms \n", d_in);
printf(" 2. Inference (OpenVINO) | %8.4f ms \n", d_proc);
printf(" 3. UDP Out (Edge->Box) | %8.4f ms \n", d_out);
printf(" 4. Total RTT (闭环总时延) | %s %8.4f ms\033[0m \n", status_color, d_total);
printf("-----------------------------------------------------------\n");
const char* n[] = {"Vbus", "Vbus_next", "io", "ib", "ib_prev", "isc", "isc_prev"};
for(int i=0; i<7; ++i) printf(" %-29s | %12.6f \n", n[i], phys[i]);
printf(" %-29s | %12.6f \n", "Ts (Fixed)", TS_VAL);
printf(" %-29s | %12.6f \n", "C (Fixed)", C_VAL);
printf("-----------------------------------------------------------\n");
printf(" 神经网络输出 (P) | %12.6f \n", p);
printf("===========================================================\n");
fflush(stdout);
}

int main() {
// 实时性锁定
cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(7, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
struct sched_param param; param.sched_priority = 99;
sched_setscheduler(0, SCHED_FIFO, &param);
mlockall(MCL_CURRENT | MCL_FUTURE);

string model_path = "$FINAL_MODEL_PATH";
cout << ">>> 正在加载模型: " << model_path << endl;

ov::Core core;
shared_ptr<ov::Model> model;

try {
model = core.read_model(model_path);
model->reshape(ov::PartialShape({1, 9}));
auto compiled = core.compile_model(model, "CPU", {{"INFERENCE_NUM_THREADS", "1"}});
auto req = compiled.create_infer_request();
float* input = req.get_input_tensor().data<float>();

int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in rx_addr{}, tx_addr{};
rx_addr.sin_family = AF_INET; rx_addr.sin_port = htons(12345); rx_addr.sin_addr.s_addr = INADDR_ANY;
bind(sock, (struct sockaddr*)&rx_addr, sizeof(rx_addr));
tx_addr.sin_family = AF_INET; tx_addr.sin_port = htons(DEST_PORT); inet_pton(AF_INET, TARGET_IP, &tx_addr.sin_addr);

printf("\033[2J\033[1;1H\033[1;32m>>> 模型加载成功,等待机箱信号...\033[0m\n");
BoxPacket rx; EdgeResult tx; float phys[7]; int count = 0; double max_jitter = 0;

while (true) {
if (recvfrom(sock, &rx, sizeof(rx), 0, nullptr, nullptr) > 0) {
auto t2 = chrono::high_resolution_clock::now().time_since_epoch().count();
for(int i=0; i<7; ++i) {
phys[i] = (float)((double)rx.data[i] - OFFSET) / SCALE;
input[i] = phys[i];
}
input[7] = TS_VAL; input[8] = C_VAL;
req.infer();
auto t3 = chrono::high_resolution_clock::now().time_since_epoch().count();
tx.val = 1; tx.t1 = rx.t1; tx.t2 = t2; tx.t3 = t3;
sendto(sock, &tx, sizeof(tx), 0, (struct sockaddr*)&tx_addr, sizeof(tx_addr));
auto t4 = chrono::high_resolution_clock::now().time_since_epoch().count();

double rtt = (double)(t4 - rx.t1) / 1e6;
if (count > 1000 && rtt > max_jitter) max_jitter = rtt;
if (++count % 500 == 0) render_dashboard(phys, (double)(t2-rx.t1)/1e6, (double)(t3-t2)/1e6, (double)(t4-t3)/1e6, rtt, req.get_output_tensor().data<float>()[0], count, max_jitter);
}
}
} catch (const exception& e) {
cerr << "\033[1;31m!!! 模型加载严重错误 !!!\033[0m" << endl;
cerr << "原因: " << e.what() << endl;
cerr << "请检查 ONNX 文件是否损坏或输入维度是否匹配。" << endl;
return 1;
}
return 0;
}
EOF

# --- 模拟器源码 (Box_Simulator.cpp) ---
cat > $WS/box_side/Box_Simulator.cpp <<EOF
#include <iostream>
#include <chrono>
#include <thread>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <random>

using namespace std;
struct BoxPacket { uint32_t data[7]; int64_t t1; };
struct EdgeResult { uint32_t val; int64_t t1; int64_t t2; int64_t t3; };

int main() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in edge_addr{}, local_addr{};
edge_addr.sin_family = AF_INET; edge_addr.sin_port = htons(12345); inet_pton(AF_INET, "127.0.0.1", &edge_addr.sin_addr);
local_addr.sin_family = AF_INET; local_addr.sin_port = htons(12346); local_addr.sin_addr.s_addr = INADDR_ANY;
bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr));
struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
mt19937 gen(time(0));
uniform_int_distribution<uint32_t> dist(1200140000, 1200155000);
BoxPacket tx; EdgeResult rx;
while (true) {
tx.t1 = chrono::high_resolution_clock::now().time_since_epoch().count();
for(int i=0; i<7; ++i) tx.data[i] = dist(gen);
sendto(sock, &tx, sizeof(tx), 0, (struct sockaddr*)&edge_addr, sizeof(edge_addr));
recvfrom(sock, &rx, sizeof(rx), 0, nullptr, nullptr);
this_thread::sleep_for(chrono::milliseconds(1));
}
return 0;
}
EOF
fi

# ----------------------------------------------------------
# 4. 构建与启动
# ----------------------------------------------------------
if [[ "$NEED_BUILD" == "y" || "$NEED_BUILD" == "Y" ]]; then
echo -e "\n${GREEN}[步骤 4/5] 正在构建 HIL 实时系统...${NC}"
CONDA_ROOT=$(conda info --base 2>/dev/null || echo "$HOME/anaconda3")
OV_CMAKE=$(find "$CONDA_ROOT/envs" -name "cmake" -path "*/openvino/*" -type d 2>/dev/null | head -n 1)
cat > $WS/edge_node/CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.10)
project(EdgeNode)
set(CMAKE_CXX_STANDARD 17)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
set(OpenVINO_DIR "$OV_CMAKE")
find_package(OpenVINO REQUIRED)
add_executable(HIL_Edge_Core HIL_Edge_Core.cpp)
target_link_libraries(HIL_Edge_Core PRIVATE openvino::runtime pthread)
EOF
cd $WS/edge_node/build && cmake .. >/dev/null && make -j$(nproc) >/dev/null
cd $WS/box_side && mkdir -p build && g++ Box_Simulator.cpp -o build/Box_Simulator -lpthread
echo -e " ✅ 编译成功。"
fi

echo -e "\n${GREEN}[步骤 5/5] 启动实时回路...${NC}"
( sudo pkill -9 HIL_Edge_Core; pkill -9 Box_Simulator ) >/dev/null 2>&1
sleep 1
if [[ "$RUN_MODE" == "1" ]]; then
taskset -c 5 $WS/box_side/build/Box_Simulator &
BOX_PID=$!
fi
sudo taskset -c 7 $WS/edge_node/build/HIL_Edge_Core

cleanup() { [[ ! -z "$BOX_PID" ]] && kill -9 $BOX_PID 2>/dev/null; sudo pkill -9 HIL_Edge_Core 2>/dev/null; exit 0; }
trap cleanup SIGINT SIGTERM