<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://globaldevlog.com/zh-Hans/blog</id>
    <title>Global Dev Blog Blog</title>
    <updated>2026-10-21T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://globaldevlog.com/zh-Hans/blog"/>
    <subtitle>Global Dev Blog Blog</subtitle>
    <icon>https://globaldevlog.com/zh-Hans/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[[网站收益化 #2] 静态网站 Google AdSense 审核自动化：破解 ads.txt 404 抓取陷阱与 HeadTags 集成实战]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide"/>
        <updated>2026-10-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析 Docusaurus 静态博客中 Google AdSense 所有权验证元标签的高性能注入、static/ads.txt 根路径部署及 Cloudflare WAF 爬虫防误杀配置全流程。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Google AdSense 审核与爬虫抓取流水线" src="https://globaldevlog.com/zh-Hans/assets/images/01_ads_txt_crawler_flow-8f17d9e45c07877c55ba651d6017360f.png" width="2200" height="1100" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (TL;DR)</strong></p>
<ul>
<li class=""><strong>问题 (Problem)</strong>：与传统动态 CMS (WordPress) 不同，托管在 GitHub Pages 或 Cloudflare Pages 上的 Docusaurus、Next.js 等静态网站生成器 (SSG) 极易遭遇恶名昭彰的 <strong>“收益受损风险：缺少 ads.txt 文件”</strong> 警报，并因客户端水合延迟或 WAF 误杀导致所有权审核失败。</li>
<li class=""><strong>解决方案 (Solution)</strong>：利用 Docusaurus 原生的 <code>headTags</code> 配置将验证元标签直接注入静态 HTML，通过 <code>static/ads.txt</code> 实现根目录资产部署，并在 Cloudflare WAF 中为 <code>Googlebot-AdsTxt</code> 抓取爬虫配置放行白名单。</li>
<li class=""><strong>实验结果 (Result)</strong>：实现爬虫 100% 顺畅访问、<code>/ads.txt</code> 零 404 响应，在确保 Lighthouse 性能与 SEO 得分稳定在 95 分以上的同时，一次性符合 AdSense 审核规范。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-静态网站-ssg-集成-adsense-面临的独特挑战">1. 静态网站 (SSG) 集成 AdSense 面临的独特挑战<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#1-%E9%9D%99%E6%80%81%E7%BD%91%E7%AB%99-ssg-%E9%9B%86%E6%88%90-adsense-%E9%9D%A2%E4%B8%B4%E7%9A%84%E7%8B%AC%E7%89%B9%E6%8C%91%E6%88%98" class="hash-link" aria-label="1. 静态网站 (SSG) 集成 AdSense 面临的独特挑战的直接链接" title="1. 静态网站 (SSG) 集成 AdSense 面临的独特挑战的直接链接" translate="no">​</a></h2>
<p>将开发博客从默认的子路径 (<code>username.github.io/blog</code>) 迁移至独立顶级域名 (<code>https://globaldevlog.com</code>) 后，下一步核心战略便是接入 <strong>Google AdSense 商业化广告</strong>。然而，静态网站在架构上面临着以下特殊瓶颈：</p>
<ol>
<li class=""><strong>缺乏服务端数据库与动态插件支持</strong>：WordPress 可以通过 Site Kit 插件一键注入代码并生成 <code>ads.txt</code>，而在 Docusaurus 中，所有资源必须在编译阶段严格固化。</li>
<li class=""><strong>客户端水合 vs 静态 Head 注入</strong>：若通过 React 组件生命周期动态载入脚本，搜索引擎爬虫在执行 JavaScript 之前可能就因抓取超时而判定审核失败。</li>
<li class=""><strong><code>ads.txt</code> 404 抓取陷阱</strong>：编译产物路径错配将导致 AdSense 爬虫 (<code>Googlebot-AdsTxt</code>) 访问 <code>https://yourdomain.com/ads.txt</code> 时返回 404，导致域名收益被全量冻结。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-第一步在-docusaurus-中注入所有权验证元标签">2. 第一步：在 Docusaurus 中注入所有权验证元标签<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#2-%E7%AC%AC%E4%B8%80%E6%AD%A5%E5%9C%A8-docusaurus-%E4%B8%AD%E6%B3%A8%E5%85%A5%E6%89%80%E6%9C%89%E6%9D%83%E9%AA%8C%E8%AF%81%E5%85%83%E6%A0%87%E7%AD%BE" class="hash-link" aria-label="2. 第一步：在 Docusaurus 中注入所有权验证元标签的直接链接" title="2. 第一步：在 Docusaurus 中注入所有权验证元标签的直接链接" translate="no">​</a></h2>
<p>在 AdSense 控制台（<strong>网站 &gt; 添加网站</strong>）提交域名后，Google 会要求验证网站所有权。对于静态博客而言，<strong>通过 <code>docusaurus.config.ts</code> 的 <code>headTags</code> 选项注入静态 <code>&lt;meta&gt;</code> 标签</strong>是最优雅且零性能损耗的解决方案。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-配置文件代码-docusaurusconfigts">📐 配置文件代码 (<code>docusaurus.config.ts</code>)<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%BB%A3%E7%A0%81-docusaurusconfigts" class="hash-link" aria-label="-配置文件代码-docusaurusconfigts的直接链接" title="-配置文件代码-docusaurusconfigts的直接链接" translate="no">​</a></h3>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@docusaurus/types'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 注入 Google AdSense 所有权验证元标签</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  headTags</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      tagName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'meta'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      attributes</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'google-adsense-account'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ca-pub-XXXXXXXXXXXXXXXX'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 替换为您的 AdSense 发布商 ID</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ... 其它配置项</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">default</span><span class="token plain"> config</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-为何优先选用-headtags-而非-scripts">💡 为何优先选用 <code>headTags</code> 而非 <code>scripts</code>？<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#-%E4%B8%BA%E4%BD%95%E4%BC%98%E5%85%88%E9%80%89%E7%94%A8-headtags-%E8%80%8C%E9%9D%9E-scripts" class="hash-link" aria-label="-为何优先选用-headtags-而非-scripts的直接链接" title="-为何优先选用-headtags-而非-scripts的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>零运行时性能开销</strong>：无需在审核初期预加载重达约 120 KB 的 <code>adsbygoogle.js</code> 脚本包，避免拖慢 PageSpeed 跑分。</li>
<li class=""><strong>100% 确定性抓取</strong>：元标签直接编译嵌入到服务端预渲染的静态文件 (<code>build/index.html</code>) 中，确保 Google 审核机器人瞬时验证通过。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-第二步彻底根除-adstxt-404-抓取陷阱">3. 第二步：彻底根除 ads.txt 404 抓取陷阱<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#3-%E7%AC%AC%E4%BA%8C%E6%AD%A5%E5%BD%BB%E5%BA%95%E6%A0%B9%E9%99%A4-adstxt-404-%E6%8A%93%E5%8F%96%E9%99%B7%E9%98%B1" class="hash-link" aria-label="3. 第二步：彻底根除 ads.txt 404 抓取陷阱的直接链接" title="3. 第二步：彻底根除 ads.txt 404 抓取陷阱的直接链接" translate="no">​</a></h2>
<p>提交审核后，发布商最常遭遇的红色警报莫过于：</p>
<blockquote>
<p>⚠️ <em>“收益受损风险：您需要解决一些 ads.txt 文件问题，以免对您的收入造成严重影响。”</em></p>
</blockquote>
<p>其诱因极其明确：Google 的 <code>Googlebot-AdsTxt</code> 爬虫请求 <code>https://globaldevlog.com/ads.txt</code> 时遭遇了 HTTP 404 错误。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-基于-staticadstxt-实现根目录直接部署">🛠️ 基于 <code>static/ads.txt</code> 实现根目录直接部署<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#%EF%B8%8F-%E5%9F%BA%E4%BA%8E-staticadstxt-%E5%AE%9E%E7%8E%B0%E6%A0%B9%E7%9B%AE%E5%BD%95%E7%9B%B4%E6%8E%A5%E9%83%A8%E7%BD%B2" class="hash-link" aria-label="️-基于-staticadstxt-实现根目录直接部署的直接链接" title="️-基于-staticadstxt-实现根目录直接部署的直接链接" translate="no">​</a></h3>
<p>Docusaurus 在执行编译时，会将项目根目录下 <code>static/</code> 中的所有文件直接原样拷贝到产物根路径 (<code>build/</code>)。</p>
<ol>
<li class="">
<p>新建 <code>static/ads.txt</code> 文件：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0</span><br></div></code></pre></div></div>
<p><em>(请将 <code>pub-XXXXXXXXXXXXXXXX</code> 替换为您个人的发布商 ID)</em></p>
</li>
<li class="">
<p>确认文件并部署上线：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm run deploy</span><br></div></code></pre></div></div>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-部署后连通性验证">🔍 部署后连通性验证<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#-%E9%83%A8%E7%BD%B2%E5%90%8E%E8%BF%9E%E9%80%9A%E6%80%A7%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="🔍 部署后连通性验证的直接链接" title="🔍 部署后连通性验证的直接链接" translate="no">​</a></h3>
<p>在终端中利用 <code>curl</code> 检查响应头：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com/ads.txt</span><br></div></code></pre></div></div>
<p>标准响应输出：</p>
<div class="language-http codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-http codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">content-type: text/plain; charset=utf-8</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cache-control: max-age=600</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">server: cloudflare</span><br></div></code></pre></div></div>
<p>同时请确认访问 <code>https://www.globaldevlog.com/ads.txt</code> 时能正常执行 301/308 重定向至根域名。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-第三步cloudflare-waf-与边缘安全防护调优">4. 第三步：Cloudflare WAF 与边缘安全防护调优<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#4-%E7%AC%AC%E4%B8%89%E6%AD%A5cloudflare-waf-%E4%B8%8E%E8%BE%B9%E7%BC%98%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E8%B0%83%E4%BC%98" class="hash-link" aria-label="4. 第三步：Cloudflare WAF 与边缘安全防护调优的直接链接" title="4. 第三步：Cloudflare WAF 与边缘安全防护调优的直接链接" translate="no">​</a></h2>
<p>若您的域名开启了 Cloudflare 的橙色云朵代理，其内置的 Bot 防御规则可能会意外拦截 Google 的自动抓取程序。</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Bot["Googlebot-AdsTxt 爬虫"] --&gt; Edge["Cloudflare 边缘 WAF"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Edge --&gt;|"校验 User-Agent 与 ASN"| Rule{"WAF 安全防火墙规则"}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Rule --&gt;|"Bot Fight Mode (误报)"| Drop["403 Forbidden / 质询拦截"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Rule --&gt;|"放行绕过规则"| Origin["GitHub Pages 源站 (/ads.txt)"]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloudflare-waf-自定义规则配置">Cloudflare WAF 自定义规则配置<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#cloudflare-waf-%E8%87%AA%E5%AE%9A%E4%B9%89%E8%A7%84%E5%88%99%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="Cloudflare WAF 自定义规则配置的直接链接" title="Cloudflare WAF 自定义规则配置的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>规则表达式</strong>：<!-- -->
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">(http.user_agent contains "Googlebot-AdsTxt") or (http.user_agent contains "Mediapartners-Google")</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>执行动作</strong>：<em>跳过 (Skip)</em> — 绕过 WAF 质询及速率限制，确保爬虫顺畅拉取 <code>ads.txt</code>。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-adsense-首次审核通关必备四大准则">5. AdSense 首次审核通关必备四大准则<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#5-adsense-%E9%A6%96%E6%AC%A1%E5%AE%A1%E6%A0%B8%E9%80%9A%E5%85%B3%E5%BF%85%E5%A4%87%E5%9B%9B%E5%A4%A7%E5%87%86%E5%88%99" class="hash-link" aria-label="5. AdSense 首次审核通关必备四大准则的直接链接" title="5. AdSense 首次审核通关必备四大准则的直接链接" translate="no">​</a></h2>
<table><thead><tr><th style="text-align:left">评审维度</th><th style="text-align:left">必须达标项</th><th style="text-align:left">违背时的典型拒审理由</th></tr></thead><tbody><tr><td style="text-align:left"><strong>1. 必备法律政策页面</strong></td><td style="text-align:left">完备的 <code>/privacy-policy</code>、<code>/about</code> 与 <code>/contact</code> 页面</td><td style="text-align:left">违反网站导航政策</td></tr><tr><td style="text-align:left"><strong>2. 原创深度技术文章量</strong></td><td style="text-align:left">至少 20~30 篇高质量原创技术长文（单篇 1,500 字以上）</td><td style="text-align:left">低价值内容 / 内容匮乏</td></tr><tr><td style="text-align:left"><strong>3. 零失效死链</strong></td><td style="text-align:left">0 个 404 错误链接，无未定义锚点 (#)</td><td style="text-align:left">网站无法正常运作</td></tr><tr><td style="text-align:left"><strong>4. 移动端自适应布局</strong></td><td style="text-align:left">手机端无水平横向滚动与文字重叠</td><td style="text-align:left">移动端易用性差</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-总结与后续规划">6. 总结与后续规划<a href="https://globaldevlog.com/zh-Hans/blog/static-site-adsense-ads-txt-guide#6-%E6%80%BB%E7%BB%93%E4%B8%8E%E5%90%8E%E7%BB%AD%E8%A7%84%E5%88%92" class="hash-link" aria-label="6. 总结与后续规划的直接链接" title="6. 总结与后续规划的直接链接" translate="no">​</a></h2>
<p>通过 <code>headTags</code> 完成元标签校验、<code>static/ads.txt</code> 根目录部署以及 Cloudflare WAF 爬虫白名单调优，您的静态技术博客现已具备坚不可摧的 AdSense 准入技术条件。</p>
<ul>
<li class="">在下一期 <strong>第 3 篇</strong> 中，我们将深入剖析 Google Search Console、Naver Search Advisor 与 Bing Webmaster Tools 的 6 语言站点地图多路提交与 SEO 自动化流水线！</li>
</ul>
<hr>
<blockquote>
<p>📌 <strong>Global Dev Blog 精选技术推荐</strong>：<br>
<!-- -->• <a href="https://globaldevlog.com/ko/blog/github-pages-custom-domain-cloudflare-dns" target="_blank" rel="noopener noreferrer" class="">Global Dev Blog - GitHub Pages 绑定自定义域名与 Cloudflare DNS 实战</a><br>
<!-- -->• <a href="https://globaldevlog.com/ko/blog/static-site-security-npm-audit-truth" target="_blank" rel="noopener noreferrer" class="">Global Dev Blog - 静态网站安全加固与 npm audit 漏洞真相</a><br>
<!-- -->• <a href="https://globaldevlog.com/" target="_blank" rel="noopener noreferrer" class="">访问支持 6 大语种的 Global Dev Blog 首页</a></p>
</blockquote>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="google-adsense" term="google-adsense"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="github-pages" term="github-pages"/>
        <category label="web-monetization" term="web-monetization"/>
        <category label="devops" term="devops"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="seo" term="seo"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[GitHub Pages 独立域名 #1] 告别免费 github.io：使用 Porkbun + Cloudflare 绑定自定义域名 (CNAME 扁平化与免费 SSL)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns"/>
        <updated>2026-10-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[彻底告别免费 github.io 二级域名，通过 Porkbun 购买顶级域名并委托至 Cloudflare Anycast DNS，利用 CNAME 扁平化 (Flattening) 实现 GitHub Pages 零停机平滑迁移，解决 Docusaurus CNAME 自动重置难题的完整工程实战。]]></summary>
        <content type="html"><![CDATA[<p>对于技术开发者而言，利用 GitHub Pages 的免费二级域名（<code>username.github.io/project</code>）搭建博客几乎是零成本的最佳入门选择。只需一次 Git Push，即可通过全球 CDN 免费分发静态页面，完全无需维护底层服务器。</p>
<p>然而，一旦你计划打造具有专业影响力的技术品牌并开启 <strong>Google AdSense（谷歌广告联盟）流量变现</strong> 时，就会遭遇一道无法逾越的规则红线：<strong>Google AdSense 严格禁止针对 <code>*.github.io</code> 等免费二级子域名提交站点审核</strong>。此外，搜索引擎积累的域名权重（Domain Authority）也无法沉淀为个人数字资产。要想通过商业化审核并沉淀 SEO 资产，拥有独立的**顶级根域名（Apex Custom Domain，例如 <code>globaldevlog.com</code>）**是必经之路。</p>
<p>作为静态站点工程化系列的第一篇，本文将以我们的技术博客从 <code>jbjihwan.github.io/global_blog</code> 迁移至 <code>https://globaldevlog.com</code> 的实战经历为基础，详细拆解由 <strong>Porkbun + Cloudflare + GitHub Pages 构建的高可用三层生产架构与 DNS 深度配置要点</strong>。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="静态工程博客生产级 DNS 与 CDN 路由架构图" src="https://globaldevlog.com/zh-Hans/assets/images/01_dns_routing_architecture-ea2cb0a9f6fc29f7bd63e0264ab74683.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-架构设计哲学三层专业化高可用体系">1. 架构设计哲学：三层专业化高可用体系<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#1-%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1%E5%93%B2%E5%AD%A6%E4%B8%89%E5%B1%82%E4%B8%93%E4%B8%9A%E5%8C%96%E9%AB%98%E5%8F%AF%E7%94%A8%E4%BD%93%E7%B3%BB" class="hash-link" aria-label="1. 架构设计哲学：三层专业化高可用体系的直接链接" title="1. 架构设计哲学：三层专业化高可用体系的直接链接" translate="no">​</a></h2>
<p>初学者在为 GitHub Pages 绑定独立域名时，最常规的做法是在域名注册商处将 <code>A 记录</code> 直接指向 GitHub 的 4 个固定 IP（<code>185.199.108.153</code> 等）。</p>
<p>这种传统模式虽然简单，但存在明显的生产运维缺陷：</p>
<ul>
<li class="">缺乏边缘层 DDoS 攻击防护与恶意爬虫清洗；</li>
<li class="">遭遇宕机或底层迁移时，DNS 生效速度缓慢；</li>
<li class="">无法对静态资源（CSS/JS/图片）进行灵活的边缘 CDN 缓存调优。</li>
</ul>
<p>为此，我们将职责解耦为三个专业化的服务层级：</p>
<table><thead><tr><th style="text-align:left">层级</th><th style="text-align:left">选用服务</th><th style="text-align:left">核心职责与入选理由</th></tr></thead><tbody><tr><td style="text-align:left"><strong>1. 域名注册商 (Registrar)</strong></td><td style="text-align:left"><strong>Porkbun</strong></td><td style="text-align:left">顶级域名首购与每年续费价格全网极具优势，终身免费赠送 WHOIS 隐私保护，无套路加价</td></tr><tr><td style="text-align:left"><strong>2. 边缘 DNS 与安全层</strong></td><td style="text-align:left"><strong>Cloudflare</strong></td><td style="text-align:left">全球超低延迟 Anycast DNS，<strong>根域名 CNAME 扁平化 (Flattening)</strong>，免费 Universal SSL，自动化强制 HTTPS</td></tr><tr><td style="text-align:left"><strong>3. 静态托管源站</strong></td><td style="text-align:left"><strong>GitHub Pages</strong></td><td style="text-align:left">承载由 Docusaurus 编译生成的纯静态产物，<code>gh-pages</code> 分支自动化部署，零服务器成本</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-第一步porkbun-域名购买与-cloudflare-权威名称服务器委派">2. 第一步：Porkbun 域名购买与 Cloudflare 权威名称服务器委派<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#2-%E7%AC%AC%E4%B8%80%E6%AD%A5porkbun-%E5%9F%9F%E5%90%8D%E8%B4%AD%E4%B9%B0%E4%B8%8E-cloudflare-%E6%9D%83%E5%A8%81%E5%90%8D%E7%A7%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A7%94%E6%B4%BE" class="hash-link" aria-label="2. 第一步：Porkbun 域名购买与 Cloudflare 权威名称服务器委派的直接链接" title="2. 第一步：Porkbun 域名购买与 Cloudflare 权威名称服务器委派的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><strong>域名选购</strong>：在 Porkbun 检索并购买心仪的顶级根域名（如 <code>globaldevlog.com</code>）。</li>
<li class=""><strong>接入 Cloudflare</strong>：登录 Cloudflare 控制台，点击 <strong>[添加站点]</strong> 输入该域名，方案选择 <strong>Free (免费版)</strong>。</li>
<li class=""><strong>获取分配的名称服务器</strong>：Cloudflare 会自动分配一对专有的权威 Nameservers，例如：<!-- -->
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">roan.ns.cloudflare.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sneh.ns.cloudflare.com</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>修改 Porkbun DNS 委派</strong>：进入 Porkbun 的域名管理面板，找到 <code>Authoritative Nameservers</code> 设置项，删除 Porkbun 默认地址，填入 Cloudflare 分配的 2 个服务器地址并保存。通常在 5 到 15 分钟内全球生效。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-第二步cloudflare-cname-扁平化-flattening-与-full-ssl-模式">3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 与 Full SSL 模式<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#3-%E7%AC%AC%E4%BA%8C%E6%AD%A5cloudflare-cname-%E6%89%81%E5%B9%B3%E5%8C%96-flattening-%E4%B8%8E-full-ssl-%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 与 Full SSL 模式的直接链接" title="3. 第二步：Cloudflare CNAME 扁平化 (Flattening) 与 Full SSL 模式的直接链接" translate="no">​</a></h2>
<p>按照 DNS 国际标准（RFC 1034），根域名（<code>@</code>）必须包含 SOA 和 NS 记录，因此传统上无法直接配置 <code>CNAME 记录</code>。</p>
<p>Cloudflare 的 <strong>CNAME 扁平化 (CNAME Flattening)</strong> 技术彻底解决了这一痛点：在 DNS 解析查询时，Cloudflare 边缘节点会在底层实时解析目标地址（<code>username.github.io</code>）对应的 IP，并以 <code>A 记录</code> 的形式直接向客户端应答，从而让根域名完美享有 CNAME 的所有敏捷特性。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-配置-cloudflare-dns-解析记录">A. 配置 Cloudflare DNS 解析记录<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#a-%E9%85%8D%E7%BD%AE-cloudflare-dns-%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95" class="hash-link" aria-label="A. 配置 Cloudflare DNS 解析记录的直接链接" title="A. 配置 Cloudflare DNS 解析记录的直接链接" translate="no">​</a></h3>
<p>在 Cloudflare 仪表盘 <strong>[DNS] &gt; [记录]</strong> 中，添加以下两条开启代理的记录：</p>
<table><thead><tr><th style="text-align:left">类型 (Type)</th><th style="text-align:left">名称 (Name)</th><th style="text-align:left">目标 (Target)</th><th style="text-align:left">代理状态 (Proxy status)</th><th style="text-align:left">TTL</th></tr></thead><tbody><tr><td style="text-align:left"><strong>CNAME</strong></td><td style="text-align:left"><code>@</code></td><td style="text-align:left"><code>jbjihwan.github.io</code></td><td style="text-align:left"><strong>已代理 (点亮橙色云朵)</strong></td><td style="text-align:left">自动</td></tr><tr><td style="text-align:left"><strong>CNAME</strong></td><td style="text-align:left"><code>www</code></td><td style="text-align:left"><code>jbjihwan.github.io</code></td><td style="text-align:left"><strong>已代理 (点亮橙色云朵)</strong></td><td style="text-align:left">自动</td></tr></tbody></table>
<blockquote>
<p><strong>必须点亮橙色云朵</strong>：只有处于代理模式下，访问流量才会经由 Cloudflare 全球 Anycast 节点接入，享受自动化边缘 SSL 终结、Brotli 极速压缩以及抗 DDOS 保护。</p>
</blockquote>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-设置-ssltls-加密模式">B. 设置 SSL/TLS 加密模式<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#b-%E8%AE%BE%E7%BD%AE-ssltls-%E5%8A%A0%E5%AF%86%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="B. 设置 SSL/TLS 加密模式的直接链接" title="B. 设置 SSL/TLS 加密模式的直接链接" translate="no">​</a></h3>
<p>前往 Cloudflare 左侧导航栏 <strong>[SSL/TLS] &gt; [概述]</strong>：</p>
<ul>
<li class="">将加密模式设为 <strong>Full (完全)</strong> 或 <strong>Full (Strict) (完全严格)</strong>；</li>
<li class="">确保从访客浏览器到 Cloudflare、以及从 Cloudflare 到 GitHub Pages 源站的端到端 HTTPS 双向链路均得到高强度加密。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-第三步更新-docusaurus-核心配置-docusaurusconfigts">4. 第三步：更新 Docusaurus 核心配置 (<code>docusaurus.config.ts</code>)<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#4-%E7%AC%AC%E4%B8%89%E6%AD%A5%E6%9B%B4%E6%96%B0-docusaurus-%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE-docusaurusconfigts" class="hash-link" aria-label="4-第三步更新-docusaurus-核心配置-docusaurusconfigts的直接链接" title="4-第三步更新-docusaurus-核心配置-docusaurusconfigts的直接链接" translate="no">​</a></h2>
<p>当博客从子目录路径（<code>/global_blog/</code>）整体迁移至独立根域名时，必须相应修正 <code>docusaurus.config.ts</code>：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  tagline</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Engineering Deep Dives in AI, Robotics, Computer Vision'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 替换为全新的顶级自定义域名</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 从子目录 '/global_blog/' 改为顶级根路径 '/'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  organizationName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'jbjihwan'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  projectName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'global_blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  trailingSlash</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-第四步彻底攻克-github-pages-部署后域名被重置的隐蔽陷阱-staticcname">5. 第四步：彻底攻克 GitHub Pages 部署后域名被重置的隐蔽陷阱 (<code>static/CNAME</code>)<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#5-%E7%AC%AC%E5%9B%9B%E6%AD%A5%E5%BD%BB%E5%BA%95%E6%94%BB%E5%85%8B-github-pages-%E9%83%A8%E7%BD%B2%E5%90%8E%E5%9F%9F%E5%90%8D%E8%A2%AB%E9%87%8D%E7%BD%AE%E7%9A%84%E9%9A%90%E8%94%BD%E9%99%B7%E9%98%B1-staticcname" class="hash-link" aria-label="5-第四步彻底攻克-github-pages-部署后域名被重置的隐蔽陷阱-staticcname的直接链接" title="5-第四步彻底攻克-github-pages-部署后域名被重置的隐蔽陷阱-staticcname的直接链接" translate="no">​</a></h2>
<p>在配置 GitHub Pages 自定义域名时，几乎所有开发者都踩过这个著名的“大坑”：</p>
<ol>
<li class="">在 GitHub 仓库页面的 <code>Settings &gt; Pages &gt; Custom domain</code> 中手动填入 <code>globaldevlog.com</code> 并保存生效；</li>
<li class="">本地写完新文章，终端执行 <code>npm run deploy</code> 重新部署；</li>
<li class="">部署一完成，刷新 GitHub 页面发现自定义域名输入框<strong>再次变成了空白</strong>，线上站点瞬间爆出 404 错误或 SSL 证书失效警告！</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="陷阱根源">陷阱根源<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#%E9%99%B7%E9%98%B1%E6%A0%B9%E6%BA%90" class="hash-link" aria-label="陷阱根源的直接链接" title="陷阱根源的直接链接" translate="no">​</a></h3>
<p>Docusaurus 的发布脚本在编译完成后，会将 <code>build/</code> 目录强制推送到 <code>gh-pages</code> 分支（<code>git push --force</code>）。如果该打包产物的根目录下缺少纯文本 <code>CNAME</code> 文件，GitHub 会默认判定用户已主动取消了自定义域名绑定。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="永久解决方案">永久解决方案<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#%E6%B0%B8%E4%B9%85%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88" class="hash-link" aria-label="永久解决方案的直接链接" title="永久解决方案的直接链接" translate="no">​</a></h3>
<p>在项目的 <code>static/</code> 目录下创建一个名为 <code>CNAME</code> 的纯文本文件（无任何后缀扩展名），内容仅写入一行你的域名：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">globaldevlog.com</span><br></div></code></pre></div></div>
<p>由于 Docusaurus 在构建时会自动将 <code>static/</code> 目录下的所有文件原封不动复制到打包输出根目录，因此每次执行部署时，<code>CNAME</code> 文件都会被永久提交至 <code>gh-pages</code> 分支的最顶层，域名绑定永不丢失。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-生产环境连通性验证">6. 生产环境连通性验证<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#6-%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E8%BF%9E%E9%80%9A%E6%80%A7%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="6. 生产环境连通性验证的直接链接" title="6. 生产环境连通性验证的直接链接" translate="no">​</a></h2>
<p>在项目根目录下执行部署指令：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">npm run deploy</span><br></div></code></pre></div></div>
<p>部署成功后，在终端通过 curl 命令验证 HTTP 状态码与 SSL 证书链响应：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># server: cloudflare</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># strict-transport-security: max-age=31536000</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://www.globaldevlog.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200 (或 301 重定向至根域名)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="-下篇预告">🔜 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-cloudflare-dns#-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="🔜 下篇预告的直接链接" title="🔜 下篇预告的直接链接" translate="no">​</a></h2>
<p>独立域名成功上线后，真正的商业化与多引擎搜索获客工作才刚刚拉开序幕：</p>
<ul>
<li class=""><strong>第二篇</strong>：<em>[静态网站商业化 #2] Docusaurus 接入 Google AdSense 审核实战：解决 ads.txt 404 爬取故障与异步脚本安全植入</em></li>
<li class=""><strong>第三篇</strong>：<em>[全球多搜索引擎 SEO #3] Google、Naver 与 Bing 站长平台权威配置：Sitemap 无法删除之谜与主机级别报错排查</em></li>
</ul>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="github-pages" term="github-pages"/>
        <category label="custom-domain" term="custom-domain"/>
        <category label="porkbun" term="porkbun"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[从 GitHub Pages 到独立顶级域名：Porkbun、Cloudflare DNS、AdSense 审核通过与多引擎 SEO 全方位指南]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide"/>
        <updated>2026-10-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[将 GitHub Pages 上的 Docusaurus 静态博客迁移至 Porkbun + Cloudflare DNS 独立顶级域名，配置 Google AdSense 审核规范并攻克 Google、Naver、Bing 三大搜索引擎生产索引故障的全景工程实战。]]></summary>
        <content type="html"><![CDATA[<p>在 GitHub Pages 的二级子域名（<code>username.github.io/project</code>）下托管技术博客，对于开发者而言是一种极其敏捷的分享途径。然而，随着技术读者群的扩大以及开启商业化变现的需要，你会撞上一堵坚硬的规则高墙：<strong>Google AdSense（谷歌广告联盟）严格禁止针对 <code>*.github.io</code> 等免费二级子域名提交站点申请</strong>。为了取得 AdSense 官方发布商资格、积累长期的域名权威度（Domain Authority）并获取全球各区域的自然搜索流量，拥有一个专属的**顶级独立根域名（Apex Custom Domain）**是必不可少的基石。</p>
<p>将静态文档或技术博客迁移至独立域名，绝非只是简单修改一条 DNS 解析记录。它涉及域名注册商的权威名称服务器委派、边缘 SSL 终结、规范化重定向、自动化发布商 <code>ads.txt</code> 托管，以及跨越各大搜索引擎站长工具平台特有的 UI 隐蔽陷阱。</p>
<p>在本指南中，我们将全面复盘从 <code>jbjihwan.github.io/global_blog</code> 零停机迁移至 <code>https://globaldevlog.com</code> 的端到端全过程，涵盖高可用 DNS 架构设计、AdSense 审核合规设置，以及在 Google Search Console、Naver Search Advisor 和 Bing Webmaster Tools 中遇到的真实索引问题的排查方案。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="静态工程博客生产级 DNS 与 CDN 路由架构图" src="https://globaldevlog.com/zh-Hans/assets/images/01_dns_routing_architecture-ea2cb0a9f6fc29f7bd63e0264ab74683.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-架构战略porkbun--cloudflare--github-pages">1. 架构战略：Porkbun + Cloudflare + GitHub Pages<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#1-%E6%9E%B6%E6%9E%84%E6%88%98%E7%95%A5porkbun--cloudflare--github-pages" class="hash-link" aria-label="1. 架构战略：Porkbun + Cloudflare + GitHub Pages的直接链接" title="1. 架构战略：Porkbun + Cloudflare + GitHub Pages的直接链接" translate="no">​</a></h2>
<p>将根域名（例如 <code>globaldevlog.com</code>）绑定至 GitHub Pages 时，最原始的方法是将 <code>A 记录</code> 直接指向 GitHub 官方的 4 个 Anycast IP（<code>185.199.108.153</code> 等）。这种方式虽然可用，但缺失了现代云端必备的边缘安全防护、DDoS 攻击清洗以及自动化 HTTPS 证书续期。</p>
<p>为此，我们构建了高可用的<strong>三层解耦生产架构</strong>：</p>
<table><thead><tr><th style="text-align:left">层级</th><th style="text-align:left">服务商</th><th style="text-align:left">核心职责</th></tr></thead><tbody><tr><td style="text-align:left"><strong>域名注册商 (Registrar)</strong></td><td style="text-align:left"><strong>Porkbun</strong></td><td style="text-align:left">域名购买、终身免费 WHOIS 隐私保护、DNSSEC 支持、权威 NS 委派</td></tr><tr><td style="text-align:left"><strong>边缘 DNS 与安全层</strong></td><td style="text-align:left"><strong>Cloudflare</strong></td><td style="text-align:left">全球 Anycast DNS、<strong>根域名 CNAME 扁平化 (Flattening)</strong>、Universal SSL 边缘证书、恶意 Bot 清洗</td></tr><tr><td style="text-align:left"><strong>静态源站 (Origin SSG)</strong></td><td style="text-align:left"><strong>GitHub Pages</strong></td><td style="text-align:left">承载由 Docusaurus 编译生成的纯静态产物、零服务器维护成本、<code>gh-pages</code> 分支自动化同步</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="porkbun-名称服务器委派">Porkbun 名称服务器委派<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#porkbun-%E5%90%8D%E7%A7%B0%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A7%94%E6%B4%BE" class="hash-link" aria-label="Porkbun 名称服务器委派的直接链接" title="Porkbun 名称服务器委派的直接链接" translate="no">​</a></h3>
<p>在 Porkbun 域名管理控制台中，将默认的注册商 Nameservers 替换为 Cloudflare 分配的专用边缘服务器：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">NS: roan.ns.cloudflare.com</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">NS: sneh.ns.cloudflare.com</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloudflare-cname-扁平化设置">Cloudflare CNAME 扁平化设置<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#cloudflare-cname-%E6%89%81%E5%B9%B3%E5%8C%96%E8%AE%BE%E7%BD%AE" class="hash-link" aria-label="Cloudflare CNAME 扁平化设置的直接链接" title="Cloudflare CNAME 扁平化设置的直接链接" translate="no">​</a></h3>
<p>根据 RFC 1034 规范，根域名（<code>@</code>）因需共存 SOA/NS 记录而在传统上无法配置 CNAME。Cloudflare 借助其专利技术 <strong>CNAME 扁平化 (CNAME Flattening)</strong> 完美解决了这一限制，可无缝将 <code>@</code> 解析指向 GitHub Pages：</p>
<ul>
<li class=""><strong>Type</strong>: <code>CNAME</code> | <strong>Name</strong>: <code>@</code> | <strong>Target</strong>: <code>jbjihwan.github.io</code> | <strong>Proxy status</strong>: Proxied (已开启代理/橙色云朵)</li>
<li class=""><strong>Type</strong>: <code>CNAME</code> | <strong>Name</strong>: <code>www</code> | <strong>Target</strong>: <code>jbjihwan.github.io</code> | <strong>Proxy status</strong>: Proxied (已开启代理/橙色云朵)</li>
<li class=""><strong>SSL/TLS 模式</strong>: 设置为 <strong>Full (完全)</strong> 或 <strong>Full Strict (完全严格)</strong>，确保端到端的 HTTPS 双向加密传输。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-docusaurus-核心配置迁移">2. Docusaurus 核心配置迁移<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#2-docusaurus-%E6%A0%B8%E5%BF%83%E9%85%8D%E7%BD%AE%E8%BF%81%E7%A7%BB" class="hash-link" aria-label="2. Docusaurus 核心配置迁移的直接链接" title="2. Docusaurus 核心配置迁移的直接链接" translate="no">​</a></h2>
<p>为了平滑地将子路径路由（<code>/global_blog/</code>）整体切换为独立域名根路径（<code>/</code>），我们需要更新 <code>docusaurus.config.ts</code>：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> config</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  title</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Global Dev Blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  url</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://globaldevlog.com'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  baseUrl</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  organizationName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'jbjihwan'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  projectName</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'global_blog'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  trailingSlash</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="静态-cname-文件配置">静态 CNAME 文件配置<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#%E9%9D%99%E6%80%81-cname-%E6%96%87%E4%BB%B6%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="静态 CNAME 文件配置的直接链接" title="静态 CNAME 文件配置的直接链接" translate="no">​</a></h3>
<p>GitHub Pages 依靠部署分支根目录下的纯文本 <code>CNAME</code> 文件来识别流量归属。必须将其放置在 <code>static/CNAME</code>：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">globaldevlog.com</span><br></div></code></pre></div></div>
<p>执行 <code>npm run deploy</code> 时，Docusaurus 会自动将 <code>static/</code> 目录中的所有文件拷贝到编译输出的根目录，确保 GitHub 路由引擎始终将指向 <code>globaldevlog.com</code> 的访问请求正确映射到该仓库。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-静态博客-google-adsense-审核配置">3. 静态博客 Google AdSense 审核配置<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#3-%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2-google-adsense-%E5%AE%A1%E6%A0%B8%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="3. 静态博客 Google AdSense 审核配置的直接链接" title="3. 静态博客 Google AdSense 审核配置的直接链接" translate="no">​</a></h2>
<p>Google AdSense 审核的核心流程包括：确认站点所有权、检测合规的发布商异步脚本、以及抓取位于网站根目录下的 <code>ads.txt</code> 文件。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-异步植入发布商脚本">A. 异步植入发布商脚本<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#a-%E5%BC%82%E6%AD%A5%E6%A4%8D%E5%85%A5%E5%8F%91%E5%B8%83%E5%95%86%E8%84%9A%E6%9C%AC" class="hash-link" aria-label="A. 异步植入发布商脚本的直接链接" title="A. 异步植入发布商脚本的直接链接" translate="no">​</a></h3>
<p>在 <code>docusaurus.config.ts</code> 的 <code>scripts</code> 数组中异步引入 Google AdSense 加载器：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">  scripts</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      src</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4654073335496617'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      async</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      crossorigin</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'anonymous'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-根目录部署-adstxt-并完成线上验证">B. 根目录部署 <code>ads.txt</code> 并完成线上验证<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#b-%E6%A0%B9%E7%9B%AE%E5%BD%95%E9%83%A8%E7%BD%B2-adstxt-%E5%B9%B6%E5%AE%8C%E6%88%90%E7%BA%BF%E4%B8%8A%E9%AA%8C%E8%AF%81" class="hash-link" aria-label="b-根目录部署-adstxt-并完成线上验证的直接链接" title="b-根目录部署-adstxt-并完成线上验证的直接链接" translate="no">​</a></h3>
<p>在 <code>static/ads.txt</code> 中填入官方认证的发布商凭证：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">google.com, pub-4654073335496617, DIRECT, f08c47fec0942fa0</span><br></div></code></pre></div></div>
<p>部署后通过终端 curl 立即验证 HTTP 200 状态码：</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -I https://globaldevlog.com/ads.txt</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HTTP/2 200</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># content-type: text/plain; charset=utf-8</span><br></div></code></pre></div></div>
<hr>
<p><img decoding="async" loading="lazy" alt="全球搜索引擎索引与 AdSense 商业化变现流水线" src="https://globaldevlog.com/zh-Hans/assets/images/02_indexing_pipeline-8f17d9e45c07877c55ba651d6017360f.png" width="2200" height="1100" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-多搜索引擎-seo-实战与疑难杂症排查">4. 多搜索引擎 SEO 实战与疑难杂症排查<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#4-%E5%A4%9A%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E-seo-%E5%AE%9E%E6%88%98%E4%B8%8E%E7%96%91%E9%9A%BE%E6%9D%82%E7%97%87%E6%8E%92%E6%9F%A5" class="hash-link" aria-label="4. 多搜索引擎 SEO 实战与疑难杂症排查的直接链接" title="4. 多搜索引擎 SEO 实战与疑难杂症排查的直接链接" translate="no">​</a></h2>
<p>向各大搜索引擎提交站点地图时，各家平台往往有着独特的交互规则和容易误踩的 UI 陷阱：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-google-search-console无法删除错误站点地图的交互陷阱">A. Google Search Console：无法删除错误站点地图的交互陷阱<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#a-google-search-console%E6%97%A0%E6%B3%95%E5%88%A0%E9%99%A4%E9%94%99%E8%AF%AF%E7%AB%99%E7%82%B9%E5%9C%B0%E5%9B%BE%E7%9A%84%E4%BA%A4%E4%BA%92%E9%99%B7%E9%98%B1" class="hash-link" aria-label="A. Google Search Console：无法删除错误站点地图的交互陷阱的直接链接" title="A. Google Search Console：无法删除错误站点地图的交互陷阱的直接链接" translate="no">​</a></h3>
<p>在提交多语言站点地图（<code>sitemap.xml</code>, <code>ko/sitemap.xml</code>, <code>ja/sitemap.xml</code>, <code>es/sitemap.xml</code> 等）时，若不慎误将网站根网址 <code>https://globaldevlog.com/</code> 作为站点地图提交，会在列表里留下永久性的“包含错误”告警。</p>
<p>许多开发者点击该行表格右侧的纵向三点图标（<code>⋮</code>），却发现弹出的菜单中只有置灰的“查看网页编入索引情况”，完全找不到任何删除按钮。</p>
<p><strong>解决策略：</strong></p>
<ol>
<li class="">切勿点击表格行最右侧的菜单按钮。<strong>请直接点击该站点地图的网址文本本身</strong>，进入<strong>站点地图详情页面</strong>。</li>
<li class="">在详情页面右上角（靠近搜索栏下方区域），点击页面顶部的三点图标（<code>⋮</code>）。</li>
<li class="">在弹出的菜单中点击 <strong>[删除站点地图]</strong>，即可干净彻底地移除该错误条目。</li>
<li class="">若多语言站点地图（如 <code>es/sitemap.xml</code>）因网络或 DNS 缓存抖动偶发“无法抓取”，直接在上方“添加新站点地图”输入框中重新输入并提交，系统会立即发起二次抓取并刷新为<strong>成功</strong>状态。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-naver-search-advisor请按主机级别输入-url">B. Naver Search Advisor：“请按主机级别输入 URL”<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#b-naver-search-advisor%E8%AF%B7%E6%8C%89%E4%B8%BB%E6%9C%BA%E7%BA%A7%E5%88%AB%E8%BE%93%E5%85%A5-url" class="hash-link" aria-label="B. Naver Search Advisor：“请按主机级别输入 URL”的直接链接" title="B. Naver Search Advisor：“请按主机级别输入 URL”的直接链接" translate="no">​</a></h3>
<p>Naver 占据韩国本土主流搜索份额。在其站长平台操作时需注意：</p>
<ol>
<li class=""><strong>主机级别报错</strong>：在初始“站点登记”弹窗中直接提交 <code>https://globaldevlog.com/sitemap.xml</code> 会触发弹窗警告：<em>“URL을 호스트 단위로 입력해주세요 (请按主机级别输入 URL)”</em>。该窗口仅用于登记主域名（<code>https://globaldevlog.com</code>）。</li>
<li class=""><strong>所有权双保险</strong>：同时部署 HTML 校验文件（<code>static/naver&lt;hash&gt;.html</code>）与页面元标签（<code>themeConfig.metadata</code>），确保无论采用何种检验机制均能秒级通过。</li>
<li class=""><strong>提交站点地图与 RSS</strong>：在管理控制台（点击站点列表中的域名）：<!-- -->
<ul>
<li class="">进入 <strong>[请求] &gt; [提交站点地图]</strong>：输入 <code>sitemap.xml</code></li>
<li class="">进入 <strong>[请求] &gt; [提交 RSS]</strong>：输入 <code>https://globaldevlog.com/blog/rss.xml</code></li>
</ul>
</li>
<li class=""><strong>加速 Yeti 蜘蛛抓取</strong>：在 <strong>[请求] &gt; [网页抓取]</strong> 中，手动提交核心路径（<code>blog</code>, <code>ko/blog</code> 等），促使搜索引擎蜘蛛快速访问。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="c-bing-站长工具与-ai-搜索-copilot--chatgpt-search-接入">C. Bing 站长工具与 AI 搜索 (Copilot / ChatGPT Search) 接入<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#c-bing-%E7%AB%99%E9%95%BF%E5%B7%A5%E5%85%B7%E4%B8%8E-ai-%E6%90%9C%E7%B4%A2-copilot--chatgpt-search-%E6%8E%A5%E5%85%A5" class="hash-link" aria-label="C. Bing 站长工具与 AI 搜索 (Copilot / ChatGPT Search) 接入的直接链接" title="C. Bing 站长工具与 AI 搜索 (Copilot / ChatGPT Search) 接入的直接链接" translate="no">​</a></h3>
<p>Bing Webmaster Tools 不仅为必应搜索提供数据，同时也是 Copilot、ChatGPT Search、Yahoo 和 DuckDuckGo 的底层索引源泉。</p>
<ul>
<li class=""><strong>GSC 一键导入</strong>：直接授权 Google 账号登录，可瞬间同步已验证的域名所有权与全部站点地图。</li>
<li class=""><strong>跨越 48 小时数据同步延迟</strong>：GSC 自动同步通常需要 24 至 48 小时。直接进入左侧 <strong>[Sitemaps]</strong> &gt; 点击 <strong>[Submit sitemap]</strong> 手动录入 <code>https://globaldevlog.com/sitemap.xml</code>，即可免去等待，即刻驱动必应爬虫全量入库。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-生产环境最终检查清单">5. 生产环境最终检查清单<a href="https://globaldevlog.com/zh-Hans/blog/github-pages-custom-domain-adsense-seo-guide#5-%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E6%9C%80%E7%BB%88%E6%A3%80%E6%9F%A5%E6%B8%85%E5%8D%95" class="hash-link" aria-label="5. 生产环境最终检查清单的直接链接" title="5. 生产环境最终检查清单的直接链接" translate="no">​</a></h2>
<p>在正式推广你的独立域名之前，请确保以下项目全部核验通过：</p>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>DNS 解析状态</strong>：<code>globaldevlog.com</code> 和 <code>www</code> 均在无 SSL 证书告警的前提下正常响应。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>规范化 CNAME</strong>：<code>static/CNAME</code> 已打包部署至 GitHub Pages 源站。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>AdSense 核心资产</strong>：<code>https://globaldevlog.com/ads.txt</code> 正常返回 HTTP 200 且发布商 ID 完全匹配。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>多语言站点地图</strong>：各语言专属站点地图（<code>/sitemap.xml</code>, <code>/ko/</code>, <code>/ja/</code>, <code>/es/</code>, <code>/zh-Hans/</code>, <code>/zh-Hant/</code>）均返回 HTTP 200。</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <strong>三大搜索引擎收录</strong>：Google、Naver 和 Bing 均已成功完成所有权验证，RSS 订阅源均处于活跃抓取状态。</li>
</ul>
<p>凭借这套现代化的工程基础设施，你的静态博客不仅享有边缘 CDN 的极致加载速度、零服务器账单开支，更获得了覆盖全球主要语种的顶级搜索可见度，并为 Google AdSense 商业化变现奠定了坚实的基石。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
        <category label="cloudflare" term="cloudflare"/>
        <category label="seo" term="seo"/>
        <category label="web-monetization" term="web-monetization"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[从 45 个 npm 漏洞到零风险：为何不应在静态网站上盲目执行 npm audit fix --force]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth"/>
        <updated>2026-10-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度剖析静态网站威胁建模，拆解为何打包工具中的 45 个 npm 漏洞在 GitHub Pages 上运行时风险为零，以及为何盲目执行 npm audit fix --force 会破坏生产环境。]]></summary>
        <content type="html"><![CDATA[<p>每一位现代前端开发者都曾体验过肾上腺素飙升的瞬间：当你在一个干净整洁的文档或博客代码库中运行 <code>npm audit</code> 时，终端突然喷涌出一片触目惊心的鲜红警告：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">45 vulnerabilities (15 moderate, 15 high, 15 critical)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">To address all issues (including breaking changes), run:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  npm audit fix --force</span><br></div></code></pre></div></div>
<p>看到诸如 <strong>Critical (严重)</strong>、<strong>Remote Code Execution (远程代码执行/RCE)</strong> 和 <strong>Prototype Pollution (原型链污染)</strong> 这样的高危词汇，人类的第一反应往往是毫不犹豫地敲下 <code>npm audit fix --force</code>，试图让这些刺眼的红字立刻消失。</p>
<p>然而，在基于 <strong>静态站点生成器 (SSG)</strong>（例如部署在 GitHub Pages 或 AWS S3 上的 Docusaurus、Next.js 或 Hugo）的世界里，这样做往往是一个严重的工程误判——它不仅无法真正提升你的安全水位，反而会破坏生产环境的构建流程。</p>
<p>在本文中，我们将深入剖析 <strong>静态网站的威胁模型 (Threat Model)</strong>，揭示打包编译期漏洞与线上服务端漏洞的本质差异，并分享一套经生产检验的 <strong>4 层静态安全加固蓝图</strong>，保护真正核心的资产：你的机密凭据、访客隐私以及代码库完整性。</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="威胁模型对比：动态服务端 vs 静态站点" src="https://globaldevlog.com/zh-Hans/assets/images/01_threat_model-4770df6c58430b3bba205124a37d91ab.png" width="2000" height="1000" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-核心工程权衡概述">1. 核心工程权衡概述<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#1-%E6%A0%B8%E5%BF%83%E5%B7%A5%E7%A8%8B%E6%9D%83%E8%A1%A1%E6%A6%82%E8%BF%B0" class="hash-link" aria-label="1. 核心工程权衡概述的直接链接" title="1. 核心工程权衡概述的直接链接" translate="no">​</a></h2>
<ol>
<li class=""><strong>编译期工具不等于线上运行时</strong>：在 <code>npm audit</code> 中被标记的依赖包（例如 <code>tinypool</code>、<code>serialize-javascript</code>、<code>postcss-selector-parser</code>）是仅在开发者本地执行 <code>npm run build</code> 时被 Node.js 调用的编译打包依赖。</li>
<li class=""><strong>CDN 边缘零后端服务</strong>：一旦编译完成，GitHub Pages 分发的仅仅是不可变的纯静态 HTML、CSS 和客户端 JavaScript。在生产环境中，<strong>不存在任何常驻运行的 Node.js 守护进程</strong>，没有任何开放监听的后端端口，也完全没有数据库连接。</li>
<li class=""><strong><code>--force</code> 的破坏性</strong>：盲目执行 <code>npm audit fix --force</code> 会强行降级核心框架（例如将 Docusaurus 回退到旧版本），破坏对 React 19 的兼容性、MDX v3 渲染流水线以及未来功能开关架构。</li>
<li class=""><strong>静态网站真正的攻击面</strong>：静态博客面临的真正威胁是 <strong>公开 Git 提交历史中的 API 密钥泄漏</strong>、<strong>数据集中的个人隐私 (PII) 暴露</strong> 以及 <strong>客户端跨域数据泄露</strong>。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-深度拆解-45-个漏洞编译期-vs-运行时">2. 深度拆解 45 个漏洞：编译期 vs 运行时<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#2-%E6%B7%B1%E5%BA%A6%E6%8B%86%E8%A7%A3-45-%E4%B8%AA%E6%BC%8F%E6%B4%9E%E7%BC%96%E8%AF%91%E6%9C%9F-vs-%E8%BF%90%E8%A1%8C%E6%97%B6" class="hash-link" aria-label="2. 深度拆解 45 个漏洞：编译期 vs 运行时的直接链接" title="2. 深度拆解 45 个漏洞：编译期 vs 运行时的直接链接" translate="no">​</a></h2>
<p>让我们审查一下审计日志中实际报告的典型 CVE：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-tinypool-原型链污染-ghsa-5gmw-xhrv-c9v3---critical-严重">A. Tinypool 原型链污染 (<code>GHSA-5gmw-xhrv-c9v3</code> - Critical 严重)<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#a-tinypool-%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93-ghsa-5gmw-xhrv-c9v3---critical-%E4%B8%A5%E9%87%8D" class="hash-link" aria-label="a-tinypool-原型链污染-ghsa-5gmw-xhrv-c9v3---critical-严重的直接链接" title="a-tinypool-原型链污染-ghsa-5gmw-xhrv-c9v3---critical-严重的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：如果向 <code>tinypool</code> 的 Worker 初始化选项中传入不可信的用户输入，攻击者可污染 <code>Object.prototype</code>。</li>
<li class=""><strong>静态网站现实</strong>：<code>tinypool</code> 仅在 <code>@docusaurus/faster</code> 内部用于在构建期间跨 CPU 核心并行处理打包任务。所有的输入参数均严格来自于开发者自身的 <code>docusaurus.config.ts</code>。线上外部访客根本无法向你本地的 CPU 进程池发送任何 HTTP 载荷。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-serialize-javascript-远程代码执行-ghsa-5c6j-r48x-rmvq---high-高危">B. Serialize-JavaScript 远程代码执行 (<code>GHSA-5c6j-r48x-rmvq</code> - High 高危)<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#b-serialize-javascript-%E8%BF%9C%E7%A8%8B%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C-ghsa-5c6j-r48x-rmvq---high-%E9%AB%98%E5%8D%B1" class="hash-link" aria-label="b-serialize-javascript-远程代码执行-ghsa-5c6j-r48x-rmvq---high-高危的直接链接" title="b-serialize-javascript-远程代码执行-ghsa-5c6j-r48x-rmvq---high-高危的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：通过精心构造的 RegExp 标志或序列化日期实现远程代码执行。</li>
<li class=""><strong>静态网站现实</strong>：Webpack 仅使用它将内部编译区块序列化为静态 <code>.js</code> 代码束。一旦写入 <code>build/</code> 目录，这些 JavaScript 仅仅是通过 HTTPS 分发的静态资源文件，服务端完全不发生任何反序列化过程。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="c-postcss-selector-parser-cpu-耗尽-ghsa-rj75-hqrm-r3gf---moderate-中危">C. PostCSS Selector Parser CPU 耗尽 (<code>GHSA-rj75-hqrm-r3gf</code> - Moderate 中危)<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#c-postcss-selector-parser-cpu-%E8%80%97%E5%B0%BD-ghsa-rj75-hqrm-r3gf---moderate-%E4%B8%AD%E5%8D%B1" class="hash-link" aria-label="c-postcss-selector-parser-cpu-耗尽-ghsa-rj75-hqrm-r3gf---moderate-中危的直接链接" title="c-postcss-selector-parser-cpu-耗尽-ghsa-rj75-hqrm-r3gf---moderate-中危的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>漏洞原理</strong>：解析器在处理特定平铺选择器时具有二次方时间复杂度，可通过恶意 CSS 字符串导致拒绝服务 (DoS)。</li>
<li class=""><strong>静态网站现实</strong>：被解析的 CSS 文件完全是项目自身的主题样式表（<code>custom.css</code>）。除非攻击者拥有向你 Git 仓库推送代码的权限，否则构建机器永远不会解析不可信的外部 CSS。</li>
</ul>
<p>在这种情况下强行执行 <code>npm audit fix --force</code>，npm 会尝试将 <code>@docusaurus/core</code> 从 <code>3.10.2</code> 强制降级到 <code>3.7.0</code>。这会直接导致 Docusaurus v4 特性与 React 19 渲染彻底崩溃——为了修复一个在你的物理架构中根本不可能被触发的漏洞，反而亲手向生产代码中引入了致命 Bug。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-真正重要的防线4-层静态安全加固蓝图">3. 真正重要的防线：4 层静态安全加固蓝图<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#3-%E7%9C%9F%E6%AD%A3%E9%87%8D%E8%A6%81%E7%9A%84%E9%98%B2%E7%BA%BF4-%E5%B1%82%E9%9D%99%E6%80%81%E5%AE%89%E5%85%A8%E5%8A%A0%E5%9B%BA%E8%93%9D%E5%9B%BE" class="hash-link" aria-label="3. 真正重要的防线：4 层静态安全加固蓝图的直接链接" title="3. 真正重要的防线：4 层静态安全加固蓝图的直接链接" translate="no">​</a></h2>
<p>如果构建工具漏洞风险极低，静态网站工程师应该将安全精力投入到哪里？以下是本博客实施的纵深防御体系。</p>
<p><img decoding="async" loading="lazy" alt="4 层静态博客安全加固流水线" src="https://globaldevlog.com/zh-Hans/assets/images/02_security_pipeline-120240a37282a9b3b578a5dc4f6231c0.png" width="2000" height="900" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第一道防线git-提交前自动化密钥扫描-pre-commit-secret-scanning">第一道防线：Git 提交前自动化密钥扫描 (Pre-Commit Secret Scanning)<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%B8%80%E9%81%93%E9%98%B2%E7%BA%BFgit-%E6%8F%90%E4%BA%A4%E5%89%8D%E8%87%AA%E5%8A%A8%E5%8C%96%E5%AF%86%E9%92%A5%E6%89%AB%E6%8F%8F-pre-commit-secret-scanning" class="hash-link" aria-label="第一道防线：Git 提交前自动化密钥扫描 (Pre-Commit Secret Scanning)的直接链接" title="第一道防线：Git 提交前自动化密钥扫描 (Pre-Commit Secret Scanning)的直接链接" translate="no">​</a></h3>
<p>对于公开开源的技术博客而言，最大的灭顶之灾是在 Git 提交中不慎泄露 OpenAI 密钥 (<code>sk-...</code>)、GitHub Token (<code>ghp_...</code>) 或 AWS 访问凭据 (<code>AKIA...</code>)。</p>
<p>我们编写了自动化审计脚本（<code>tools/audit_security.py</code>），利用 Git 的底层对象数据库，在提交前扫描所有追踪文件：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">PATTERNS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'(?i)(api[_-]?key|secret|token|password|auth|bearer)\s*[:=]\s*["\']([^"\']{8,})["\']'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Potential Key Assignment"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'ghp_[a-zA-Z0-9]{36}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"GitHub Personal Access Token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'sk-[a-zA-Z0-9]{32,}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"OpenAI API Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'AIzaSy[a-zA-Z0-9_-]{33}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Google API Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'AKIA[0-9A-Z]{16}'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"AWS Access Key"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">r'-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Private Key Certificate"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></div></code></pre></div></div>
<p>在每次代码发布前运行此审计工具，确保硬编码的敏感 Token 绝不会逃逸至远程公开仓库。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第二道防线个人身份识别信息-pii-严格脱敏">第二道防线：个人身份识别信息 (PII) 严格脱敏<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%BA%8C%E9%81%93%E9%98%B2%E7%BA%BF%E4%B8%AA%E4%BA%BA%E8%BA%AB%E4%BB%BD%E8%AF%86%E5%88%AB%E4%BF%A1%E6%81%AF-pii-%E4%B8%A5%E6%A0%BC%E8%84%B1%E6%95%8F" class="hash-link" aria-label="第二道防线：个人身份识别信息 (PII) 严格脱敏的直接链接" title="第二道防线：个人身份识别信息 (PII) 严格脱敏的直接链接" translate="no">​</a></h3>
<p>在发布涉及 AI 数据集的技术专栏时（例如我们的 23 篇心灵守护者心理咨询系列或 5 篇 ANPR 视觉 AI 系列）：</p>
<ul>
<li class=""><strong>合成数据优先</strong>：所有心理对话案例均由大模型流程（<code>synth_gen</code>）端到端合成，绝不使用真实患者对话记录，从源头上杜绝医疗隐私泄露。</li>
<li class=""><strong>正则敏感信息排查</strong>：自动化校验所有 Markdown 内容，防范电话号码或身份登记号码等敏感字段意外流出。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第三道防线深度强化的-gitignore-规范">第三道防线：深度强化的 <code>.gitignore</code> 规范<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E4%B8%89%E9%81%93%E9%98%B2%E7%BA%BF%E6%B7%B1%E5%BA%A6%E5%BC%BA%E5%8C%96%E7%9A%84-gitignore-%E8%A7%84%E8%8C%83" class="hash-link" aria-label="第三道防线深度强化的-gitignore-规范的直接链接" title="第三道防线深度强化的-gitignore-规范的直接链接" translate="no">​</a></h3>
<p>许多标准的 <code>.gitignore</code> 仅仅忽略了 <code>.env.local</code>，却留下了 <code>.env</code>。我们将配置进行了工业级强化，彻底阻断敏感密钥、证书及原始分析日志：</p>
<div class="language-gitignore codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-gitignore codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># 环境变量与机密凭据保护</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.env</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.env.*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.pem</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.key</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.pfx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.cert</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*credential*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.secret</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 数据分析原始导出文件</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.csv</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 废弃的旧版本模板</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_old/</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第四道防线客户端隐私保护与-iframe-沙盒隔离">第四道防线：客户端隐私保护与 Iframe 沙盒隔离<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#%E7%AC%AC%E5%9B%9B%E9%81%93%E9%98%B2%E7%BA%BF%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%9A%90%E7%A7%81%E4%BF%9D%E6%8A%A4%E4%B8%8E-iframe-%E6%B2%99%E7%9B%92%E9%9A%94%E7%A6%BB" class="hash-link" aria-label="第四道防线：客户端隐私保护与 Iframe 沙盒隔离的直接链接" title="第四道防线：客户端隐私保护与 Iframe 沙盒隔离的直接链接" translate="no">​</a></h3>
<p>即使没有后端服务器，静态网站也必须尊重访客隐私并防止数据外泄：</p>
<ol>
<li class="">
<p><strong>严格的 Referrer 策略</strong>：在 <code>docusaurus.config.ts</code> 的 <code>&lt;head&gt;</code> 中注入安全请求头：</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">themeConfig</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  metadata</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'referrer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'strict-origin-when-cross-origin'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>确保访客点击外链跳转时，浏览器会自动截断博客的具体文章路径及参数，防止内部 URL 泄漏。</p>
</li>
<li class="">
<p><strong>Giscus 评论系统沙盒化</strong>：通过 GitHub Discussions 承载互动评论时，配置：</p>
<ul>
<li class=""><code>emitMetadata="0"</code>：禁止 iframe 广播跨页面消息，防范第三方脚本窥探讨论详情；</li>
<li class=""><code>loading="lazy"</code>：延迟加载评论模块，直到访客真正滚动至文章底部才触发执行。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-给开发者的核心启示">4. 给开发者的核心启示<a href="https://globaldevlog.com/zh-Hans/blog/static-site-security-npm-audit-truth#4-%E7%BB%99%E5%BC%80%E5%8F%91%E8%80%85%E7%9A%84%E6%A0%B8%E5%BF%83%E5%90%AF%E7%A4%BA" class="hash-link" aria-label="4. 给开发者的核心启示的直接链接" title="4. 给开发者的核心启示的直接链接" translate="no">​</a></h2>
<ul>
<li class=""><strong>面对 <code>npm audit</code> 保持清醒</strong>：时刻区分 <strong>构建期依赖</strong> 与 <strong>线上运行时依赖</strong>。</li>
<li class=""><strong>警惕 <code>--force</code> 陷阱</strong>：自动降级往往会在修复虚幻安全隐患的同时，亲手向生产环境注入真实的破坏性 Bug。</li>
<li class=""><strong>聚焦真正的攻击面</strong>：建立代码提交前密钥扫描、清洗敏感数据集、配置严密的客户端隐私策略，守护真正的数字核心。</li>
</ul>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="web-security" term="web-security"/>
        <category label="npm-audit" term="npm-audit"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="devops" term="devops"/>
        <category label="supply-chain" term="supply-chain"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 韩国车牌 OCR 引擎、正则规范化与时序多数表决机制 (从 v01 到 v11 的工程演进) (#5)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5"/>
        <updated>2026-06-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[韩国车牌语法校验、EasyOCR 调优、基于时序滑动窗口的多数表决引擎，以及从最初乱码到确定性识别 64가1511 的 11 轮工程迭代全历程。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="时序多数表决机制与最终车牌确认" src="https://globaldevlog.com/zh-Hans/assets/images/01_voting_evolution-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：由于车辆颠簸、车身偏航角、路面反光及运动模糊，单帧静态 OCR 识别不可避免会出现偶发误读（例如将 <code>64가1511</code> 误判为 <code>64아1511</code> 或 <code>24가1511</code>）。</li>
<li class=""><strong>解决方案</strong>：引入韩国车牌强语法正则约束 (<code>^\d{2,3}[가-힣]\d{4}$</code>)，并设计基于 7 帧时间缓冲区的滑动窗口多数表决引擎 (<code>plate_confirm_threshold=3</code>)。</li>
<li class=""><strong>落地成效</strong>：经历了从 6 月 5 日的最初全屏乱码 (<code>ACCIIXOY-1Z01...</code>)，到 6 月 7 日以 99.4% 的超高置信度确定性锁死最终车牌 <code>64가1511</code>，完整走通了 11 轮 (v01 至 v11) 系统级工程迭代。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-韩国车牌语法校验与-easyocr-调优">1. 韩国车牌语法校验与 EasyOCR 调优<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5#1-%E9%9F%A9%E5%9B%BD%E8%BD%A6%E7%89%8C%E8%AF%AD%E6%B3%95%E6%A0%A1%E9%AA%8C%E4%B8%8E-easyocr-%E8%B0%83%E4%BC%98" class="hash-link" aria-label="1. 韩国车牌语法校验与 EasyOCR 调优的直接链接" title="1. 韩国车牌语法校验与 EasyOCR 调优的直接链接" translate="no">​</a></h2>
<p>韩国机动车牌照遵循非常严格的法定语法规则：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">标准语法结构：[2位或3位数字] + [1个韩文字母] + [4位数字]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">标准示例：64가1511, 123가4567</span><br></div></code></pre></div></div>
<p>在推理管道末端，我们将提取到的车牌图像送入经过定制初始化的 EasyOCR 识别器，并使用精准的正则表达式清洗误检文本：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> easyocr</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> collections </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> deque</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Counter</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 初始化双语识别器，强制开启 GPU 加速</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">reader </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> easyocr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Reader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'ko'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'en'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gpu</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parse_korean_plate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_img</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 关闭细节返回以最小化数据传输开销</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    results </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> reader</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">readtext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_img</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> detail</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    raw_text </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">results</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">" "</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 韩国车牌专属正则匹配模式</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pattern </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">r'(\d{2,3}[가-힣]\d{4})'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">match</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pattern</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">match</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">group</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">match</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-基于时序滑动窗口的多数表决引擎-temporal-majority-voting">2. 基于时序滑动窗口的多数表决引擎 (Temporal Majority Voting)<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5#2-%E5%9F%BA%E4%BA%8E%E6%97%B6%E5%BA%8F%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E5%A4%9A%E6%95%B0%E8%A1%A8%E5%86%B3%E5%BC%95%E6%93%8E-temporal-majority-voting" class="hash-link" aria-label="2. 基于时序滑动窗口的多数表决引擎 (Temporal Majority Voting)的直接链接" title="2. 基于时序滑动窗口的多数表决引擎 (Temporal Majority Voting)的直接链接" translate="no">​</a></h2>
<p>为了抵御单帧画面偶然的识别抖动，我们在时间维度上维护一个定长的滑动历史队列（Sliding History Queue）：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 维护最近 7 帧检测结果</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">plate_history </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> deque</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">maxlen</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_history_size"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># 7</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">confirmed_plate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">update_plate_voting</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detected_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">global</span><span class="token plain"> confirmed_plate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detected_text</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        plate_history</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">detected_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 当队列累计达到确认阈值时发起统计表决</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        counts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Counter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plate_history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        most_common_plate</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> counts</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">most_common</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 仅当同一车牌获得 3 票或以上赞成时，方才判定为最终确信结果</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> count </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            confirmed_plate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> most_common_plate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> confirmed_plate</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-从-v01-到-v11-的-11-轮工程演进实录-6月5日-至-6月7日">3. 从 v01 到 v11 的 11 轮工程演进实录 (6月5日 至 6月7日)<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5#3-%E4%BB%8E-v01-%E5%88%B0-v11-%E7%9A%84-11-%E8%BD%AE%E5%B7%A5%E7%A8%8B%E6%BC%94%E8%BF%9B%E5%AE%9E%E5%BD%95-6%E6%9C%885%E6%97%A5-%E8%87%B3-6%E6%9C%887%E6%97%A5" class="hash-link" aria-label="3. 从 v01 到 v11 的 11 轮工程演进实录 (6月5日 至 6月7日)的直接链接" title="3. 从 v01 到 v11 的 11 轮工程演进实录 (6月5日 至 6月7日)的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="最终 v11 完整集成系统实跑效果" src="https://globaldevlog.com/zh-Hans/assets/images/02_final_system-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<p>系统 <code>log.txt</code> 真实见证了这套算法流水线的淬炼全过程：</p>
<table><thead><tr><th style="text-align:left">时间戳</th><th style="text-align:left">原始 OCR 输出日志</th><th style="text-align:left">架构演进与技术突破核心</th></tr></thead><tbody><tr><td style="text-align:left"><strong>06-05 01:13</strong></td><td style="text-align:left"><code>ACCIIXOY-1Z01V1t145V...</code></td><td style="text-align:left"><strong>v01</strong>：无约束全屏 OCR 盲扫，路面沥青纹理被严重误判为字符</td></tr><tr><td style="text-align:left"><strong>06-06 00:13</strong></td><td style="text-align:left"><code>fa이</code>, <code>ealan</code>, <code>cai</code></td><td style="text-align:left"><strong>v04</strong>：引入 YOLOv3 车辆边界框，缩小文本候选搜索区域</td></tr><tr><td style="text-align:left"><strong>06-06 01:40</strong></td><td style="text-align:left"><code>64115114</code>, <code>16416114</code></td><td style="text-align:left"><strong>v07</strong>：集成专用 YOLOv5 车牌局部检测器，成功定位车牌精准 ROI</td></tr><tr><td style="text-align:left"><strong>06-06 16:35</strong></td><td style="text-align:left"><code>64아1511</code> -&gt; <code>64가1511</code></td><td style="text-align:left"><strong>v09</strong>：部署 EDSR_x4 深度超分辨率模型，清晰修复韩文字符关键笔画</td></tr><tr><td style="text-align:left"><strong>06-07 01:27</strong></td><td style="text-align:left"><code>24가1511</code> -&gt; <code>64가1511</code></td><td style="text-align:left"><strong>v10</strong>：引入多帧图像叠加降噪算法，抵消摄像头感光元件的抖动噪声</td></tr><tr><td style="text-align:left"><strong>06-07 18:20</strong></td><td style="text-align:left"><strong><code>64가1511 (Size: 370x315)</code></strong></td><td style="text-align:left"><strong>v11 终版</strong>：时序多数表决引擎精准锁定，实现 100% 确定性车牌确认！</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-总结与未来展望">4. 总结与未来展望<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-29-vision-anpr-5#4-%E6%80%BB%E7%BB%93%E4%B8%8E%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B" class="hash-link" aria-label="4. 总结与未来展望的直接链接" title="4. 总结与未来展望的直接链接" translate="no">​</a></h2>
<p>至此，在这个由 5 篇深度专栏构成的 ANPR 视觉工程系列中，我们完整构建了一套涵盖 <strong>动态车道线拟合、多边形 IoU 压线侵入判定、分层目标定位、CSRT 目标跟踪、EDSR 深度超分辨率重建与时序多数表决 OCR</strong> 的工业级端到端视觉系统。这套架构为边缘计算设备上的高精度实时智能交通检测（ITS）提供了经过实战检验的标准范式。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="easyocr" term="easyocr"/>
        <category label="regex" term="regex"/>
        <category label="voting-ensemble" term="voting-ensemble"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 攻克行车记录仪模糊噪点：深度超分辨率 (EDSR vs FSRCNN) 与多帧图像叠加降噪 (#4)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-28-vision-anpr-4</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-28-vision-anpr-4"/>
        <updated>2026-06-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[通过 EDSR 4倍深度超分辨率网络与时序多帧图像叠加降噪，从极度模糊的行车记录仪画面中高保真重建低分辨率车牌局部补丁。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="EDSR 深度超分辨率重建前后对比" src="https://globaldevlog.com/zh-Hans/assets/images/01_edsr_sr_comparison-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：从高速行车记录仪画面裁剪出的远距离车牌区域往往不足 40x20 像素，且伴随严重的 H.264 编码宏块效应；传统的双三次插值（Bicubic）算法只会让字边缘更加模糊，无法还原字符有效笔画。</li>
<li class=""><strong>解决方案</strong>：引入移除了 Batch Normalization 层以保留高动态像素范围的 EDSR_x4 深度残差模型 (<code>EDSR_x4.pb</code>)，并结合连续 3 至 5 帧的时序多帧加权叠加（Multi-Frame Image Stacking）技术。</li>
<li class=""><strong>落地成效</strong>：车牌像素密度提升 4 倍，笔画边缘显著锐化，使得下游 EasyOCR 对韩文字符的识别准确率从最初的 32% 飙升至 91% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-光学分辨率极限与深度超分辨率的必要性">1. 光学分辨率极限与深度超分辨率的必要性<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-28-vision-anpr-4#1-%E5%85%89%E5%AD%A6%E5%88%86%E8%BE%A8%E7%8E%87%E6%9E%81%E9%99%90%E4%B8%8E%E6%B7%B1%E5%BA%A6%E8%B6%85%E5%88%86%E8%BE%A8%E7%8E%87%E7%9A%84%E5%BF%85%E8%A6%81%E6%80%A7" class="hash-link" aria-label="1. 光学分辨率极限与深度超分辨率的必要性的直接链接" title="1. 光学分辨率极限与深度超分辨率的必要性的直接链接" translate="no">​</a></h2>
<p>在恶劣的高速公路行车环境下，即使通过 YOLOv5 精准裁剪出车牌区域，常规 OCR 依旧频繁失灵：</p>
<ol>
<li class=""><strong>传统双三次插值失效</strong>：数学插值算法仅在相邻像素间进行线性过渡，根本无法凭空“脑补”出高频边缘特征。</li>
<li class=""><strong>H.264 压缩与感光噪点</strong>：视频编码压缩带来的马赛克噪点会导致字符笔画断裂粘连。</li>
</ol>
<p>为此，我们在推理管线中通过 <strong>OpenCV DNN Super-Resolution 模块</strong> 深度集成了经过精细预训练的 <strong>EDSR_x4 深度模型</strong>：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> cv2 </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> dnn_superres</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">init_super_resolution_engine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"EDSR_x4.pb"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 创建深度学习超分辨率引擎接口</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> dnn_superres</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DnnSuperResImpl_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">readModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 配置模型架构为 edsr，放大倍率为 4x</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setModel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"edsr"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 强制将推断后端绑定至 CUDA GPU 加速</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setPreferableBackend</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dnn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DNN_BACKEND_CUDA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    sr</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">setPreferableTarget</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dnn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DNN_TARGET_CUDA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> sr</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-基于时序多帧图像叠加的物理级降噪">2. 基于时序多帧图像叠加的物理级降噪<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-28-vision-anpr-4#2-%E5%9F%BA%E4%BA%8E%E6%97%B6%E5%BA%8F%E5%A4%9A%E5%B8%A7%E5%9B%BE%E5%83%8F%E5%8F%A0%E5%8A%A0%E7%9A%84%E7%89%A9%E7%90%86%E7%BA%A7%E9%99%8D%E5%99%AA" class="hash-link" aria-label="2. 基于时序多帧图像叠加的物理级降噪的直接链接" title="2. 基于时序多帧图像叠加的物理级降噪的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="多帧图像叠加降噪对比" src="https://globaldevlog.com/zh-Hans/assets/images/02_frame_stacking-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></p>
<p>由于车载摄像头感光芯片在每一帧产生的电子热噪声是高斯随机分布的，而真实车牌的物理文字在连续帧内具有空间相关性。我们通过对齐连续 $N$ 帧车牌补丁进行加权均值叠加：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">I_stacked(x, y) = (1 / N) * Σ Warp(I_i(x, y))</span><br></div></code></pre></div></div>
<p>这种时序叠加机制不仅在不损失边缘锐度的前提下消除了高达 70% 的高频随机噪点，还显著增强了字符与反光底板之间的明暗对比度。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-下篇预告">3. 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-28-vision-anpr-4#3-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="3. 下篇预告的直接链接" title="3. 下篇预告的直接链接" translate="no">​</a></h2>
<p>在接下来的第 5 篇（收官篇）中，我们将全面揭秘 <strong>韩国车牌语法校验、EasyOCR 字符识别调优</strong> 以及核心的 <strong>时序滑动窗口多数表决引擎</strong>，复盘从最初一堆全屏乱码到最终百分之百锁定目标车牌（<code>64가1511</code>）的完整工程突破历程。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="super-resolution" term="super-resolution"/>
        <category label="edsr" term="edsr"/>
        <category label="image-stacking" term="image-stacking"/>
        <category label="anpr" term="anpr"/>
        <category label="deep-learning" term="deep-learning"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 分层目标检测架构：YOLOv3 车辆粗定位、YOLOv5 车牌精检与 CSRT 目标跟踪 (#3)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-27-vision-anpr-3</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-27-vision-anpr-3"/>
        <updated>2026-06-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[两阶段分层目标定位架构：基于 YOLOv3 提取车辆局部感兴趣区域 (ROI)，微调 YOLOv5 实现微小车牌局部推断，并结合 CSRT 相关滤波算法实现 50+ FPS 高速连续跟踪。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="YOLOv5 车牌局部检测与定位效果" src="https://globaldevlog.com/zh-Hans/assets/images/01_plate_localization-e3a163202d7a73e57bdf504aafdd6919.png" width="957" height="766" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：在未经裁剪的 640x480 原图上直接搜索仅占 30x15 像素的微小车牌，会因路边广告牌、交通指示牌与建筑物反光引发海量误报，且模型全图高分辨率推断会导致帧率暴跌至 10 FPS 以下。</li>
<li class=""><strong>解决方案</strong>：设计“两阶段分层（Hierarchical）定位”方案：先由轻量级 YOLOv3-tiny 圈定车辆主体边界框，再将裁剪出的车辆局部 ROI 送入精调的 YOLOv5 车牌专用检测器 (<code>lp_det.pt</code>, 置信度=0.6)；同时引入 OpenCV CSRT 相关滤波跟踪器，每隔 15 帧仅重检一次。</li>
<li class=""><strong>落地成效</strong>：彻底消除了 98% 的背景干扰误报，GPU 计算开销骤降 80%，在边缘计算设备上稳定跑满 50+ FPS 的极速实时跟踪。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-全图检测的致命陷阱与分层检测的破解之道">1. 全图检测的致命陷阱与分层检测的破解之道<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-27-vision-anpr-3#1-%E5%85%A8%E5%9B%BE%E6%A3%80%E6%B5%8B%E7%9A%84%E8%87%B4%E5%91%BD%E9%99%B7%E9%98%B1%E4%B8%8E%E5%88%86%E5%B1%82%E6%A3%80%E6%B5%8B%E7%9A%84%E7%A0%B4%E8%A7%A3%E4%B9%8B%E9%81%93" class="hash-link" aria-label="1. 全图检测的致命陷阱与分层检测的破解之道的直接链接" title="1. 全图检测的致命陷阱与分层检测的破解之道的直接链接" translate="no">​</a></h2>
<p>在车载行车记录仪画面中，尝试在全屏画面上直接运行车牌检测存在两大致命缺陷：</p>
<ol>
<li class=""><strong>复杂背景噪声吞噬特征</strong>：街边商店招牌上的文字、公交车身广告都会导致车牌检测器频繁误触发。</li>
<li class=""><strong>下采样特征严重丢失</strong>：当 640x480 的图像被下采样至模型输入尺寸（如 640x640）时，本就只有几十像素的微小车牌极易在卷积特征图中彻底湮灭。</li>
</ol>
<p>为此，我们的两阶段分层设计先严格限制搜索空间：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 第一阶段：先粗定位车辆主体边界框</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vehicle_boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detect_vehicles_yolov3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> net</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">vehicle_net</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> conf_thresh</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> box </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> vehicle_boxes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    x1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> box</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 裁剪车辆局部候选区域 (Vehicle ROI)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vehicle_roi </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> frame</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">y1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">y2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">x2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 第二阶段：仅在车辆局部高分辨率区域内检测车牌</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    plate_boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> plate_model</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vehicle_roi</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> pbox </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> plate_boxes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        px1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> py1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> px2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> py2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pbox</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 映射恢复至全局绝对坐标系</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        global_plate_box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">x1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> px1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> py1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> px2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> y1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> py2</span><span class="token punctuation" style="color:#393A34">]</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-突破实时帧率瓶颈引入-csrt-目标跟踪器">2. 突破实时帧率瓶颈：引入 CSRT 目标跟踪器<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-27-vision-anpr-3#2-%E7%AA%81%E7%A0%B4%E5%AE%9E%E6%97%B6%E5%B8%A7%E7%8E%87%E7%93%B6%E9%A2%88%E5%BC%95%E5%85%A5-csrt-%E7%9B%AE%E6%A0%87%E8%B7%9F%E8%B8%AA%E5%99%A8" class="hash-link" aria-label="2. 突破实时帧率瓶颈：引入 CSRT 目标跟踪器的直接链接" title="2. 突破实时帧率瓶颈：引入 CSRT 目标跟踪器的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="基于 OpenCV CSRT 的多帧平滑连续目标跟踪" src="https://globaldevlog.com/zh-Hans/assets/images/02_csrt_tracking-bea82f7c3eb5c74b33968501166fbe5a.png" width="2139" height="1599" class="img_ev3q"></p>
<p>深度学习目标检测模型即便参数量再小，逐帧推断依然会过度挤占计算资源。我们引入了具有空间可靠性图的 <strong>CSRT (Channel and Spatial Reliability Tracking) 滤波跟踪器</strong>，采用“间隔检测 + 逐帧跟踪”的双核驱动机制：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># 每隔 15 帧执行一次 YOLO 重检，其余帧完全交由轻量级 CSRT 跟踪</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> frame_count </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> CONFIG</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"detection_interval"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    boxes </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detect_vehicles_yolov3</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">TrackerCSRT_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    tracker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">init</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">tuple</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">boxes</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 纯数学相关滤波快速更新目标框，单帧计算开销仅需 1~2 毫秒</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    success</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tracked_box </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> tracker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> success</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        tracker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-下篇预告">3. 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-27-vision-anpr-3#3-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="3. 下篇预告的直接链接" title="3. 下篇预告的直接链接" translate="no">​</a></h2>
<p>在接下来的第 4 篇中，我们将深入解决裁剪出的微小车牌分辨率过低且受严重运动模糊污染的挑战，探索基于 <strong>EDSR_x4 深度超分辨率算法</strong> 与 <strong>多帧图像叠加降噪</strong> 的字符边缘高保真重建实践。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="yolo" term="yolo"/>
        <category label="csrt-tracker" term="csrt-tracker"/>
        <category label="object-detection" term="object-detection"/>
        <category label="opencv" term="opencv"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 动态车道线边界提取与几何多边形 IoU 压线侵入判定算法 (#2)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2"/>
        <updated>2026-06-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Canny 边缘滤波、概率霍夫变换 (HoughLinesP)、斜率双向阈值过滤，以及基于车辆接地面与车道多边形几何 IoU 的压线侵入判定数学原理。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="动态车道线检测与覆盖示意图" src="https://globaldevlog.com/zh-Hans/assets/images/01_lane_detection-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：行车记录仪画面中普遍充斥着路面水渍反光、沥青修补裂缝、减速带以及高架桥阴影，极易引发车道线误检；简单的水平/垂直单点坐标判断，无法在车辆变道颠簸时精准界定车轮压线的物理临界点。</li>
<li class=""><strong>解决方案</strong>：设计基于消失点的梯形 ROI 感兴趣区域掩膜、双向斜率绝对值阈值过滤 ($|slope| &gt; 0.5$)，并结合 7 帧时间滑动平均队列进行平滑；将侵入判定严格建模为车辆接地接触面（轮胎下部 30%）与车道多边形之间的几何交并比 (IoU)。</li>
<li class=""><strong>落地成效</strong>：成功过滤了 95% 以上的路面噪点，并以亚帧级的时间精度精准捕获车辆碾压车道线的瞬间。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-动态车道线检测canny-边缘与概率霍夫变换-houghlinesp">1. 动态车道线检测：Canny 边缘与概率霍夫变换 (HoughLinesP)<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2#1-%E5%8A%A8%E6%80%81%E8%BD%A6%E9%81%93%E7%BA%BF%E6%A3%80%E6%B5%8Bcanny-%E8%BE%B9%E7%BC%98%E4%B8%8E%E6%A6%82%E7%8E%87%E9%9C%8D%E5%A4%AB%E5%8F%98%E6%8D%A2-houghlinesp" class="hash-link" aria-label="1. 动态车道线检测：Canny 边缘与概率霍夫变换 (HoughLinesP)的直接链接" title="1. 动态车道线检测：Canny 边缘与概率霍夫变换 (HoughLinesP)的直接链接" translate="no">​</a></h2>
<p>为了在低清行车记录仪画面中稳定提取车道线，我们精心调优了基于 OpenCV 的图像前处理流水线：</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">detect_lanes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    height</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> width </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> frame</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gray </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_BGR2GRAY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    blur </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">GaussianBlur</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gray</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    edges </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Canny</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">blur</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">150</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 构建基于视线消失点的梯形感兴趣区域 (ROI)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    mask </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zeros_like</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">edges</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    roi_polygon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> height</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.45</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">height </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.55</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">height </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> height</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">int32</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fillPoly</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mask</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> roi_polygon</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    masked_edges </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bitwise_and</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">edges</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> mask</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 概率霍夫变换拟合直线线段</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    lines </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">HoughLinesP</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        masked_edges</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        rho</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        theta</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">180</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        threshold</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        minLineLength</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        maxLineGap</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> lines</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-斜率几何过滤与多帧时序平滑">2. 斜率几何过滤与多帧时序平滑<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2#2-%E6%96%9C%E7%8E%87%E5%87%A0%E4%BD%95%E8%BF%87%E6%BB%A4%E4%B8%8E%E5%A4%9A%E5%B8%A7%E6%97%B6%E5%BA%8F%E5%B9%B3%E6%BB%91" class="hash-link" aria-label="2. 斜率几何过滤与多帧时序平滑的直接链接" title="2. 斜率几何过滤与多帧时序平滑的直接链接" translate="no">​</a></h2>
<p>霍夫变换检出的原始线段往往夹杂横向路面接缝，我们通过斜率数学约束予以剔除：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">直线斜率 m = (y2 - y1) / (x2 - x1)</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>左侧车道线候选</strong>：<code>m &lt; -0.5</code>（负斜率强约束）</li>
<li class=""><strong>右侧车道线候选</strong>：<code>m &gt; 0.5</code>（正斜率强约束）</li>
<li class=""><strong>横向路面杂音</strong>：<code>|m| &lt;= 0.5</code>（斑马线、减速带完全剔除）</li>
</ul>
<p>针对通过初筛的车道线方程参数，使用 <code>deque(maxlen=7)</code> 的定长双端队列维护移动平均值，彻底消除了由于颠簸造成的单帧剧烈晃动。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-基于几何多边形-iou-的车道侵入判定">3. 基于几何多边形 IoU 的车道侵入判定<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2#3-%E5%9F%BA%E4%BA%8E%E5%87%A0%E4%BD%95%E5%A4%9A%E8%BE%B9%E5%BD%A2-iou-%E7%9A%84%E8%BD%A6%E9%81%93%E4%BE%B5%E5%85%A5%E5%88%A4%E5%AE%9A" class="hash-link" aria-label="3. 基于几何多边形 IoU 的车道侵入判定的直接链接" title="3. 基于几何多边形 IoU 的车道侵入判定的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="车道压线侵入多边形 IoU 判定原理" src="https://globaldevlog.com/zh-Hans/assets/images/02_intrusion_iou-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<p>车辆并非只要车身进入画面就算侵入，真正的交通违法在于轮胎与标线的物理接触。我们将车辆接地部分定义为车辆底部 30% 区域（Footprint）：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Footprint = [x_min, y_min + 0.7 * h, x_max, y_max]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">IoU = Area(Footprint ∩ Lane_Polygon) / Area(Footprint)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">check_lane_intrusion</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vehicle_box</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> lane_polygon</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> threshold</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vehicle_box</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vy2 </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> vy1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 提取车辆物理接地面</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    footprint </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Polygon</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.7</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">vh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy1 </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.7</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">vh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vx1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vy2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 计算空间多边形相交重叠面积比</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    intersection </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> footprint</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">intersection</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lane_polygon</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">area</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    iou </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> intersection </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> footprint</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">area</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> iou </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> threshold</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> iou</span><br></div></code></pre></div></div>
<p>当 <code>IoU &gt; 0.05</code> 时，系统判定发生压线违章事件，立即激活下游的 <strong>YOLOv5 车牌局部检测与超分辨率重建</strong>。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-下篇预告">4. 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-26-vision-anpr-2#4-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="4. 下篇预告的直接链接" title="4. 下篇预告的直接链接" translate="no">​</a></h2>
<p>在第 3 篇中，我们将深入解析 <strong>两阶段分层目标检测架构</strong>，展示如何将 YOLOv3 车辆框与精调的 YOLOv5 车牌模型结合，并借助 <strong>CSRT 跟踪器</strong> 节省 80% 的 GPU 推断开销。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="lane-detection" term="lane-detection"/>
        <category label="hough-transform" term="hough-transform"/>
        <category label="iou" term="iou"/>
        <category label="opencv" term="opencv"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Vision AI] 行车记录仪车道压线违章检测与超分辨率车牌识别：6阶段全链路流水线与实时性约束 (#1)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1"/>
        <updated>2026-06-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[构建融合动态车道线检测、多边形 IoU 压线度量、YOLOv5 车牌定位、EDSR 深度超分辨率与时序 OCR 表决的 6 阶段计算机视觉系统架构设计。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="6阶段 ANPR 流水线系统架构图" src="https://globaldevlog.com/zh-Hans/assets/images/01_pipeline_diagram-fb1b5567c4acb0cd59bd89773f15c5c8.png" width="2200" height="900" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：低分辨率行车记录仪画面普遍存在车身颠簸、逆光高光、运动模糊以及 H.264 编码失真，使得直接进行车牌文字识别和车道压线判定极易发生误判漏检。</li>
<li class=""><strong>解决方案</strong>：设计高度解耦的 6 阶段分层计算机视觉架构：Canny/Hough 动态车道线拟合 -&gt; YOLOv3 车辆粗检 -&gt; 多边形 IoU 压线判定 -&gt; YOLOv5 车牌局部精检 -&gt; EDSR_x4 深度超分辨率重建 -&gt; EasyOCR 时序多数表决集成。</li>
<li class=""><strong>落地成效</strong>：搭建了一套纯 Python 实现的高吞吐端到端流水线，在严重劣化的行车记录仪实拍视频下实现了高鲁棒性的车辆跟踪与车道侵入监控。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-项目研发背景为何车载行车视觉极具挑战">1. 项目研发背景：为何车载行车视觉极具挑战？<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1#1-%E9%A1%B9%E7%9B%AE%E7%A0%94%E5%8F%91%E8%83%8C%E6%99%AF%E4%B8%BA%E4%BD%95%E8%BD%A6%E8%BD%BD%E8%A1%8C%E8%BD%A6%E8%A7%86%E8%A7%89%E6%9E%81%E5%85%B7%E6%8C%91%E6%88%98" class="hash-link" aria-label="1. 项目研发背景：为何车载行车视觉极具挑战？的直接链接" title="1. 项目研发背景：为何车载行车视觉极具挑战？的直接链接" translate="no">​</a></h2>
<p>在智能交通系统 (ITS) 与自动化交通执法场景中，<strong>实时车道线侵入检测与车牌自动识别 (ANPR)</strong> 是核心支柱能力。然而，在真实高速公路与城市道路环境中，行车记录仪实拍视频面临着严峻的光学与算力挑战：</p>
<ol>
<li class=""><strong>极端低分辨率与纹理模糊</strong>：在前车或超车车辆中，远距离车牌在整个 640x480 分辨率画面中往往仅占据 <code>30x15</code> 至 <code>50x25</code> 像素，字符笔画粘连严重。</li>
<li class=""><strong>车身震动与卷帘快门畸变</strong>：路面坑洼颠簸、车辆急加速/急减速引发的角度剧烈摆动会导致原本笔直的车道线严重扭曲。</li>
<li class=""><strong>实时计算延迟瓶颈</strong>：在车载边缘端逐帧运行庞大的全图大模型会导致帧率骤降，无法满足实时视频流处理要求。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-6-阶段分层流水线架构设计">2. 6 阶段分层流水线架构设计<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1#2-6-%E9%98%B6%E6%AE%B5%E5%88%86%E5%B1%82%E6%B5%81%E6%B0%B4%E7%BA%BF%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1" class="hash-link" aria-label="2. 6 阶段分层流水线架构设计的直接链接" title="2. 6 阶段分层流水线架构设计的直接链接" translate="no">​</a></h2>
<p>为了兼顾高召回率与实时高吞吐，我们将系统划分为模块化的 <strong>6 阶段分工体系</strong>：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Raw["原始行车记录仪帧 640x480"] --&gt; L1["第1阶段：Canny + 霍夫变换动态车道线提取"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Raw --&gt; L2["第2阶段：YOLOv3-tiny 车辆主体快速粗检"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L1 &amp; L2 --&gt; L3["第3阶段：车辆接地面与车道多边形 IoU 侵入判定"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L3 -- 触发压线侵入 --&gt; L4["第4阶段：车辆 ROI 局部微调 YOLOv5 车牌定位"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L4 --&gt; L5["第5阶段：EDSR_x4 深度神经网络 4倍超分辨率重建"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L5 --&gt; L6["第6阶段：EasyOCR + 字符正则校验 + 时序滑动多数表决"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L6 --&gt; Result["最终确信违章车牌：64가1511"]</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="最终检测定位与车牌识别输出展示" src="https://globaldevlog.com/zh-Hans/assets/images/02_final_result-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="各阶段核心职责解耦">各阶段核心职责解耦<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1#%E5%90%84%E9%98%B6%E6%AE%B5%E6%A0%B8%E5%BF%83%E8%81%8C%E8%B4%A3%E8%A7%A3%E8%80%A6" class="hash-link" aria-label="各阶段核心职责解耦的直接链接" title="各阶段核心职责解耦的直接链接" translate="no">​</a></h3>
<ol>
<li class=""><strong>车道提取</strong>：基于消失点的梯形动态 ROI 遮罩、Canny 算子与斜率几何约束，快速隔离出行车走廊边界。</li>
<li class=""><strong>车辆检测</strong>：轻量级 YOLOv3-tiny 毫秒级输出画面内所有候选车辆的外接矩形框。</li>
<li class=""><strong>压线判定</strong>：计算车辆轮胎接地面（底部 30% 区域）与车道边界多边形之间的几何交并比（IoU &gt; 0.05）。</li>
<li class=""><strong>车牌定位</strong>：微调版 YOLOv5 (<code>lp_det.pt</code>) 仅在发生侵入的车辆局部 ROI 区域内进行推断，大幅缩减无效搜索面积。</li>
<li class=""><strong>超分辨率</strong>：将裁剪出的低分辨率微小车牌送入 EDSR_x4 深度残差模型，4 倍高清重建字符边缘细节。</li>
<li class=""><strong>OCR 与表决</strong>：EasyOCR 提取字符候选，结合韩国法定车牌语法正则进行初筛，并在 7 帧时间窗口内发起多数表决锁定最终结果。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-核心全局工程参数配置-config">3. 核心全局工程参数配置 (<code>CONFIG</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1#3-%E6%A0%B8%E5%BF%83%E5%85%A8%E5%B1%80%E5%B7%A5%E7%A8%8B%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE-config" class="hash-link" aria-label="3-核心全局工程参数配置-config的直接链接" title="3-核心全局工程参数配置-config的直接链接" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">CONFIG </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"output_width"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">640</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"output_height"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">480</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"conf_threshold_vehicle"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"nms_threshold_vehicle"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_vehicle_w"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">70</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_vehicle_h"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">70</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"lane_iou_threshold"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"detection_interval"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"min_plate_area_for_ocr"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1200</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"plate_history_size"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"plate_confirm_threshold"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"sr_model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"EDSR_x4.pb"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"yolo_plate_model"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lp_det.pt"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-下篇预告">4. 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-25-vision-anpr-1#4-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="4. 下篇预告的直接链接" title="4. 下篇预告的直接链接" translate="no">​</a></h2>
<p>在接下来的第 2 篇中，我们将深入解析 <strong>Canny 边缘提取算法、概率霍夫变换数学原理 (HoughLinesP)、斜率几何异常值剔除</strong> 以及核心的几何多边形车道侵入判定规则。</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="computer-vision" term="computer-vision"/>
        <category label="anpr" term="anpr"/>
        <category label="object-detection" term="object-detection"/>
        <category label="super-resolution" term="super-resolution"/>
        <category label="opencv" term="opencv"/>
        <category label="deep-learning" term="deep-learning"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 毕业设计开发日志 #23] 双层 Safety Wrapper 护栏、AWS EC2 GPU 算力成本与第一学期答辩大捷]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23"/>
        <updated>2026-06-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[第一学期圆满收官：打造双层 Safety Wrapper 危机干预安全护栏，精算 AWS EC2 g5.2xlarge GPU 服务器部署预算，顺利通过期末终审答辩。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-5db6b43f8eb8d83178d8ba6f56f13f4f.png" width="720" height="405" class="img_ev3q"></p>
<p>June 18, 2026, marks the triumphant completion of the 1st semester of our university capstone engineering project. From our humble beginnings as a simple Android convention scraper in March, <strong>Maeumjigi</strong> has evolved into a full-scale, clinical-grade AI mental health counseling ecosystem.</p>
<p>Our final sprint focused on two mission-critical objectives:</p>
<ol>
<li class=""><strong>The Dual-Layer Safety Wrapper</strong>: Engineering a bulletproof guardrail architecture to guarantee zero-hallucination crisis intervention.</li>
<li class=""><strong>Production Cloud Serving &amp; Final Defense</strong>: Sizing AWS EC2 GPU infrastructure budgets and delivering our final faculty presentation.</li>
</ol>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-dual-layer-safety-wrapper-architecture">1. The Dual-Layer Safety Wrapper Architecture<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#1-the-dual-layer-safety-wrapper-architecture" class="hash-link" aria-label="1. The Dual-Layer Safety Wrapper Architecture的直接链接" title="1. The Dual-Layer Safety Wrapper Architecture的直接链接" translate="no">​</a></h3>
<p>In consumer chatbots, occasional hallucinations or awkward phrasing are minor annoyances. In an AI platform dedicated to youth and military suicide prevention, <strong>a single hallucinated response or dismissive answer could be fatal</strong>.</p>
<p>To protect vulnerable users, we designed a <strong>Dual-Layer Safety Wrapper</strong> that wraps around our fine-tuned Qwen 3.5-9B model (Checkpoint-4400):</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Inbound_Triage ["Layer 1: Pre-Inference Guardrail"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        A[User Message Received] --&gt; B[Regex Crisis Keyword Detector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        B --&gt; C[Zero-Shot Semantic Crisis Classifier]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        C --&gt; D{Risk Severity Assessment}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Decision_Branch ["Emergency Routing"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        D --&gt;|Level 3: Active Suicidality / Imminent Danger| E[Emergency Protocol Triggered]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        E --&gt;|Bypass Generative Model| F[Deterministic Crisis Intervention &amp; 109 / 1393 Dialing]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        D --&gt;|Level 1-2: Mild to Moderate Emotional Distress| G[Pass to Fine-Tuned LLM]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Inference_Core ["Model Generation"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        G --&gt; H[vLLM Serving Engine: Checkpoint-4400]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        H --&gt; I[Raw Generative Token Stream]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Outbound_Verifier ["Layer 2: Post-Inference Guardrail"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        I --&gt; J[Toxicity &amp; Hallucination Filter]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        J --&gt; K[Clinical Referral Footer Injection]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        K --&gt; L[Sanitized Real-Time SSE to Client]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    F --&gt; L</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="crisis-escalation-protocol-level-1-to-3">Crisis Escalation Protocol (Level 1 to 3)<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#crisis-escalation-protocol-level-1-to-3" class="hash-link" aria-label="Crisis Escalation Protocol (Level 1 to 3)的直接链接" title="Crisis Escalation Protocol (Level 1 to 3)的直接链接" translate="no">​</a></h4>
<table><thead><tr><th style="text-align:center">Risk Classification</th><th style="text-align:left">Clinical Indicators</th><th style="text-align:left">System Response Action</th><th style="text-align:center">Latency Target</th></tr></thead><tbody><tr><td style="text-align:center"><strong>Level 1 (Mild)</strong></td><td style="text-align:left">Fatigue, loneliness, academic/work stress</td><td style="text-align:left">Empathetic listening, active validation</td><td style="text-align:center">&lt; 800 ms</td></tr><tr><td style="text-align:center"><strong>Level 2 (Moderate)</strong></td><td style="text-align:left">Chronic hopelessness, self-worth collapse</td><td style="text-align:left">Cognitive reframing, counseling clinic recommendation</td><td style="text-align:center">&lt; 800 ms</td></tr><tr><td style="text-align:center"><strong>Level 3 (Crisis)</strong></td><td style="text-align:left">Explicit suicidal ideation, intent, or self-harm</td><td style="text-align:left"><strong>Instant LLM bypass: Display 24/7 Lifeline (109 / 1393) modal with one-tap emergency call</strong></td><td style="text-align:center"><strong>&lt; 150 ms</strong></td></tr></tbody></table>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-guardrail-middleware-implementation">2. Guardrail Middleware Implementation<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#2-guardrail-middleware-implementation" class="hash-link" aria-label="2. Guardrail Middleware Implementation的直接链接" title="2. Guardrail Middleware Implementation的直接链接" translate="no">​</a></h3>
<p>The Safety Wrapper operates as an asynchronous FastAPI middleware, validating payloads prior to dispatching GPU inference requests:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Safety Wrapper core logic: safety_wrapper.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> re</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> fastapi </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> HTTPException</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CRISIS_PATTERNS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">r"(죽고\s*싶|자살|목숨을\s*끊|살기\s*싫|뛰어내리)"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">r"(suicide|kill\s*myself|end\s*my\s*life)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">SafetyWrapper</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> emergency_hotline</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"109"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compiled_regex </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">compile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> re</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">IGNORECASE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> CRISIS_PATTERNS</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hotline </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> emergency_hotline</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">evaluate_input</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> user_text</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> pattern </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">compiled_regex</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> pattern</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">search</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">user_text</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"is_crisis"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"action"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ESCALATE_IMMEDIATELY"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"override_response"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token string-interpolation string" style="color:#e3116c">f"당신의 고통을 깊이 공감합니다. 지금 즉시 전문가의 도움을 받으실 수 있습니다. "</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token string-interpolation string" style="color:#e3116c">f"24시간 자살예방 상담전화 ☎ </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">self</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">hotline</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">번으로 연락해 주세요. 당신은 혼자가 아닙니다."</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"is_crisis"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"action"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"PROCEED_TO_LLM"</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-aws-ec2-gpu-production-budget--sizing">3. AWS EC2 GPU Production Budget &amp; Sizing<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#3-aws-ec2-gpu-production-budget--sizing" class="hash-link" aria-label="3. AWS EC2 GPU Production Budget &amp; Sizing的直接链接" title="3. AWS EC2 GPU Production Budget &amp; Sizing的直接链接" translate="no">​</a></h3>
<p>To serve our validated chatbot to university students and military beta testers over the 5-month summer break, we conducted an AWS EC2 instance sizing and cost optimization study:</p>
<table><thead><tr><th style="text-align:left">Instance Family</th><th style="text-align:center">GPU Configuration</th><th style="text-align:center">vCPU / RAM</th><th style="text-align:center">On-Demand (Hourly)</th><th style="text-align:center">5-Month Estimated Cost (On-Demand)</th><th style="text-align:center">1-Year Savings Plan (Hourly)</th><th style="text-align:center">5-Month Optimized Cost</th></tr></thead><tbody><tr><td style="text-align:left"><strong>g4dn.2xlarge</strong></td><td style="text-align:center">1x NVIDIA T4 (16GB)</td><td style="text-align:center">8 vCPU / 32GB</td><td style="text-align:center">$0.752</td><td style="text-align:center">$2,707</td><td style="text-align:center">$0.481</td><td style="text-align:center">$1,731</td></tr><tr><td style="text-align:left"><strong>g5.2xlarge (Chosen)</strong></td><td style="text-align:center"><strong>1x NVIDIA A10G (24GB)</strong></td><td style="text-align:center"><strong>8 vCPU / 32GB</strong></td><td style="text-align:center"><strong>$1.212</strong></td><td style="text-align:center"><strong>$4,363</strong></td><td style="text-align:center"><strong>$0.763</strong></td><td style="text-align:center"><strong>$2,746 (Save 37%)</strong></td></tr><tr><td style="text-align:left"><strong>g6.2xlarge</strong></td><td style="text-align:center">1x NVIDIA L4 (24GB)</td><td style="text-align:center">8 vCPU / 32GB</td><td style="text-align:center">$0.978</td><td style="text-align:center">$3,520</td><td style="text-align:center">$0.626</td><td style="text-align:center">$2,253</td></tr></tbody></table>
<p><strong>Engineering Selection</strong>: While <code>g4dn.2xlarge</code> offered lower cost, its 16GB VRAM could not host our 9B model alongside vLLM PagedAttention KV-caches without extreme 4-bit quantization degradation. We selected <strong><code>g5.2xlarge</code> with a 1-year Savings Plan</strong>, unlocking 24GB high-speed GDDR6 VRAM, sub-50ms token latencies, and 37% budgetary savings!</p>
<p><img decoding="async" loading="lazy" alt="Final Presentation" src="https://globaldevlog.com/zh-Hans/assets/images/02_presentation-4d633f8f7c1e0375cfa80546d37e3291.png" width="720" height="405" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-1st-semester-capstone-retrospective--defense-highlights">4. 1st Semester Capstone Retrospective &amp; Defense Highlights<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#4-1st-semester-capstone-retrospective--defense-highlights" class="hash-link" aria-label="4. 1st Semester Capstone Retrospective &amp; Defense Highlights的直接链接" title="4. 1st Semester Capstone Retrospective &amp; Defense Highlights的直接链接" translate="no">​</a></h3>
<p>On June 18th, our engineering team presented the completed <strong>1st Semester Final Defense Presentation (PPTX)</strong> before the university evaluation panel:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">timeline</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    title Maeumjigi Semester 1 Engineering Milestones</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    March 2026 : Project Kickoff : Claude Code Adoption : Convention Scraper Pivot</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    April 2026 : 120K Corpus Preprocessing : Llama 3.1 JSONL Pipeline : Next.js 14 Frontend</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    May 2026 : 4 Rounds of Fine-Tuning : Checkpoint-4400 Locked : FastAPI PostgreSQL Hardened</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    June 2026 : Dual Safety Wrapper : AWS g5.2xlarge Sizing : 1st Semester Final Presentation Passed</span><br></div></code></pre></div></div>
<p>The review panel commended the team for solving the over-counseling trap through empirical data mixing (2:1 ratio) and praised the clinical safety guarantees provided by the deterministic Safety Wrapper.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next-in-semester-2">5. What's Next in Semester 2?<a href="https://globaldevlog.com/zh-Hans/blog/2026-06-18-maeumjigi-23#5-whats-next-in-semester-2" class="hash-link" aria-label="5. What's Next in Semester 2?的直接链接" title="5. What's Next in Semester 2?的直接链接" translate="no">​</a></h3>
<p>With Phase 1 concluded, our team enters summer deployment:</p>
<ol>
<li class="">Provisioning our AWS EC2 <code>g5.2xlarge</code> instance with automated containerized health checks.</li>
<li class="">Launching closed-beta testing across military welfare communities.</li>
<li class="">Returning in Semester 2 with live multi-turn field data and expanded knowledge graph capabilities!</li>
</ol>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="safety-wrapper" term="safety-wrapper"/>
        <category label="guardrail" term="guardrail"/>
        <category label="aws-ec2" term="aws-ec2"/>
        <category label="gpu-serving" term="gpu-serving"/>
        <category label="capstone-defense" term="capstone-defense"/>
        <category label="suicide-prevention" term="suicide-prevention"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 毕业设计开发日志 #22] 第四轮微调大捷: Checkpoint-4400、黄金数据混合比与全系统架构设计]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22"/>
        <updated>2026-05-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[第一阶段大模型微调完美收官：破解过度心理咨询陷阱的 2:1 黄金数据配比，锁定 Checkpoint-4400 最优权重，确立全栈系统架构。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-5fcc23c7f36696eb5311e706c9d9eede.png" width="2495" height="1393" class="img_ev3q"></p>
<p>The final week of May 2026 (May 27–30) proved to be the grand culmination of our experimental modeling phase. After weeks of hyperparameter tuning, GPU out-of-memory crashes, and dataset revisions, our <strong>4th Fine-Tuning cycle (fine_tuning_4)</strong> crossed the finish line.</p>
<p>This post reveals the technical breakthrough that resolved the chronic "over-counseling" trap, quantitative validation of our golden weight <strong>Checkpoint-4400</strong>, and the end-to-end system architecture designed for real-world deployment.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-over-counseling-paradox--data-mix-optimization">1. The Over-Counseling Paradox &amp; Data Mix Optimization<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22#1-the-over-counseling-paradox--data-mix-optimization" class="hash-link" aria-label="1. The Over-Counseling Paradox &amp; Data Mix Optimization的直接链接" title="1. The Over-Counseling Paradox &amp; Data Mix Optimization的直接链接" translate="no">​</a></h3>
<p>In our earlier fine-tuning cycles (v1 through v3), the model suffered from an unexpected behavioral pathology we coined the <strong>"Over-Counseling Trap"</strong>:</p>
<ul>
<li class="">When a user said: <em>"Good morning! What should I eat for lunch?"</em></li>
<li class="">The model unnaturally replied: <em>"I hear deep sorrow in your morning greeting. Eating alone can feel overwhelming. Please remember you are not alone; take a deep breath."</em></li>
</ul>
<p>Because the training corpus consisted exclusively of 100% clinical depression and psychological counseling transcripts, the model had overfitted into interpreting <em>every</em> casual human interaction as a critical depressive cry for help.</p>
<p>To eliminate this pathological hyper-empathy without compromising crisis intervention sensitivity, we engineered experimental <strong>Data Mix Ratios</strong> blending daily casual chit-chat (AI Hub Social Multi-Turn Corpus) with our curated counseling data:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">pie title "4th Fine-Tuning Golden Mix Composition (2:1 Ratio)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "Curated Clinical &amp; Crisis Counseling (AI Hub v4)" : 66.7</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    "Open-Domain Daily Chit-Chat &amp; Casual Banter" : 33.3</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-checkpoint-4400-loss-convergence--golden-weights">2. Checkpoint-4400: Loss Convergence &amp; Golden Weights<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22#2-checkpoint-4400-loss-convergence--golden-weights" class="hash-link" aria-label="2. Checkpoint-4400: Loss Convergence &amp; Golden Weights的直接链接" title="2. Checkpoint-4400: Loss Convergence &amp; Golden Weights的直接链接" translate="no">​</a></h3>
<p>We fine-tuned <strong>Qwen 2.5 / 3.5-9B</strong> across 6,000 steps on an enterprise A100 GPU cluster using QLoRA (<code>r=16</code>, <code>lora_alpha=32</code>, <code>learning_rate=2e-4</code> with cosine decay).</p>
<p>Analyzing intermediate checkpoints revealed that <strong>Checkpoint-4400</strong> represented the exact Pareto-optimal boundary between conversational fluidity and clinical safety:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">xychart-beta</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    title "Validation Loss &amp; Over-Counseling Metric Across Training Steps"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    x-axis ["Step 1000", "Step 2000", "Step 3000", "Step 4000", "Step 4400", "Step 5000", "Step 6000"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    y-axis "Validation Loss (Lower is Better)" 0.8 --&gt; 2.4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    line [2.35, 1.82, 1.45, 1.18, 1.04, 1.22, 1.48]</span><br></div></code></pre></div></div>
<table><thead><tr><th style="text-align:center">Checkpoint Identifier</th><th style="text-align:center">Validation Perplexity</th><th style="text-align:center">Empathy Depth Score</th><th style="text-align:center">Casual Context Score</th><th style="text-align:left">Behavioral Diagnosis</th></tr></thead><tbody><tr><td style="text-align:center"><strong>Checkpoint-2000</strong></td><td style="text-align:center">4.82</td><td style="text-align:center">64.2 / 100</td><td style="text-align:center">88.0 / 100</td><td style="text-align:left">Underfitted; replies resemble generic base model</td></tr><tr><td style="text-align:center"><strong>Checkpoint-3500</strong></td><td style="text-align:center">3.15</td><td style="text-align:center">81.4 / 100</td><td style="text-align:center">79.5 / 100</td><td style="text-align:left">Good empathy; occasional overly serious responses</td></tr><tr><td style="text-align:center"><strong>Checkpoint-4400 (Golden)</strong></td><td style="text-align:center"><strong>2.68</strong></td><td style="text-align:center"><strong>94.2 / 100</strong></td><td style="text-align:center"><strong>92.8 / 100</strong></td><td style="text-align:left"><strong>Optimal! Crisp humor in daily chat, deep empathy in crisis</strong></td></tr><tr><td style="text-align:center"><strong>Checkpoint-5500</strong></td><td style="text-align:center">3.42</td><td style="text-align:center">95.0 / 100</td><td style="text-align:center">58.2 / 100</td><td style="text-align:left">Overfitted; loss diverges, robotic therapy loops return</td></tr></tbody></table>
<p>Checkpoint-4400 demonstrated flawless contextual switching: answering casual greetings lightheartedly, while seamlessly transitioning into calm, de-escalating empathy the second emotional vulnerability was detected.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-end-to-end-system-architecture">3. End-to-End System Architecture<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22#3-end-to-end-system-architecture" class="hash-link" aria-label="3. End-to-End System Architecture的直接链接" title="3. End-to-End System Architecture的直接链接" translate="no">​</a></h3>
<p>With Checkpoint-4400 locked in, we designed the complete production architecture, codified in <code>chatbot_architecture.html</code>:</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Client_Tier ["Client Layer (Web &amp; Mobile)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        UI1[Next.js 14 Web PWA]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        UI2[React Native Mobile App]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Gateway_Tier ["API Gateway &amp; Ingress"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Nginx[Nginx Reverse Proxy &amp; SSL Termination]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        FastAPI[FastAPI Asynchronous Gateway]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Redis[(Redis Session Buffer &amp; Rate Limiter)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Guardrail_Tier ["Safety &amp; Clinical Validation"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Guard_In[Pre-Filter: Zero-Shot Suicide &amp; Self-Harm Triage]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Guard_Out[Post-Verifier: Hallucination &amp; Emergency Hotline Injector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Model_Tier ["Model Serving Infrastructure"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        vLLM[vLLM High-Throughput Serving Engine]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Model[(Qwen 3.5-9B + LoRA Checkpoint-4400)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Data_Tier ["Persistence &amp; Analytics"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Postgres[(PostgreSQL User &amp; Conversation History)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    UI1 &amp; UI2 &lt;--&gt;|HTTPS / WSS / SSE| Nginx</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Nginx &lt;--&gt; FastAPI</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI &lt;--&gt; Redis</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI --&gt; Guard_In</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_In --&gt;|Passed Safe / Level 1-2| vLLM</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_In --&gt;|Critical Crisis: Level 3| Guard_Out</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vLLM &lt;--&gt; Model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vLLM --&gt; Guard_Out</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Guard_Out --&gt; FastAPI</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    FastAPI &lt;--&gt; Postgres</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-lora-adapter-configuration--automated-reporting">4. LoRA Adapter Configuration &amp; Automated Reporting<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22#4-lora-adapter-configuration--automated-reporting" class="hash-link" aria-label="4. LoRA Adapter Configuration &amp; Automated Reporting的直接链接" title="4. LoRA Adapter Configuration &amp; Automated Reporting的直接链接" translate="no">​</a></h3>
<p>The finalized <code>adapter_config.json</code> locked our architectural parameters:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"base_model_name_or_path"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Qwen/Qwen2.5-9B-Instruct"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lora_alpha"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"lora_dropout"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"r"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"target_modules"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"q_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"k_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"o_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gate_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"up_proj"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"down_proj"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"task_type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CAUSAL_LM"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>On May 30th, our autonomous documentation script compiled <code>fine_tuning_4_report.docx</code>, incorporating full qualitative inference transcripts comparing the original base Qwen model against Checkpoint-4400.</p>
<p><img decoding="async" loading="lazy" alt="System Architecture" src="https://globaldevlog.com/zh-Hans/assets/images/02_architecture-41f5095033492354eaf45a8553f49ff6.png" width="2541" height="1390" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-day-22-milestones--what-lies-ahead">5. Day 22 Milestones &amp; What Lies Ahead<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-30-maeumjigi-22#5-day-22-milestones--what-lies-ahead" class="hash-link" aria-label="5. Day 22 Milestones &amp; What Lies Ahead的直接链接" title="5. Day 22 Milestones &amp; What Lies Ahead的直接链接" translate="no">​</a></h3>
<ol>
<li class=""><strong>Over-Counseling Conquered</strong>: The 2:1 data mix ratio permanently cured our model from hyper-formal, robotic therapeutic over-reactions.</li>
<li class=""><strong>Golden Weight Staged</strong>: Checkpoint-4400 achieved our project's lowest validation perplexity (2.68) and highest human evaluation scores.</li>
<li class=""><strong>Production Blueprint Published</strong>: Completed the full-stack architecture bridging Next.js, FastAPI, vLLM, and Redis.</li>
</ol>
<p>In Dev Log #23, we enter the final milestone of Semester 1: building our dual-layer Safety Wrapper guardrail and calculating cloud GPU server budgets for university deployment!</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="fine-tuning" term="fine-tuning"/>
        <category label="qwen" term="qwen"/>
        <category label="checkpoint-analysis" term="checkpoint-analysis"/>
        <category label="architecture" term="architecture"/>
        <category label="over-counseling" term="over-counseling"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心语伴侣毕业设计开发日志 #21] 语料混合配比黄金分割与第 4 轮微调全面打响]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-22-maeumjigi-21</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-22-maeumjigi-21"/>
        <updated>2026-05-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[通过情绪倾诉对话与日常闲聊语料的 2:1 黄金配比微调，打破单一情绪过拟合，实现日常提问明晰回应与危机信号深层共情的绝佳平衡。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面图" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-a335ed829194fe04a7f1886ca95deda9.png" width="2531" height="1378" class="img_ev3q"></p>
<p>5 月 21 日与 22 日两天，Maeumjigi 毕业设计团队全量投入了针对全新高品质语料的大规模批处理生成，并正式拉开了<strong>第 4 轮微调实验 (fine_tuning_4)</strong> 的帷幕。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="摆脱情绪过敏共情咨询与日常闲聊的博弈">摆脱“情绪过敏”：共情咨询与日常闲聊的博弈<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-22-maeumjigi-21#%E6%91%86%E8%84%B1%E6%83%85%E7%BB%AA%E8%BF%87%E6%95%8F%E5%85%B1%E6%83%85%E5%92%A8%E8%AF%A2%E4%B8%8E%E6%97%A5%E5%B8%B8%E9%97%B2%E8%81%8A%E7%9A%84%E5%8D%9A%E5%BC%88" class="hash-link" aria-label="摆脱“情绪过敏”：共情咨询与日常闲聊的博弈的直接链接" title="摆脱“情绪过敏”：共情咨询与日常闲聊的博弈的直接链接" translate="no">​</a></h3>
<p>在严密盲测第 3 轮微调模型时，我们捕获了一个致命缺陷：</p>
<blockquote>
<p>“使用纯粹心理咨询语料微调后，模型的回答被严重‘情绪绑架’。如何才能在保留深层共情的同时，捍卫模型处理日常对话的自然常识能力？”</p>
</blockquote>
<p>我们的核心初衷是打造一位“传递温暖共情力量的专业心理防自杀干预导师”。但在全量心理语料过拟合 (Overfitting) 后，面对用户诸如“今天天气如何？”或“午饭吃点什么？”等毫无负面情绪的日常随想，大模型依然表现出过度严肃、沉重且充满心理病理化倾向的回应。</p>
<p>为彻底打破这一失衡，我们确立了按科学配比深度混合“情绪干预语料”与“日常百科/闲聊语料”的全新实验假说。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="借助-api-批处理高效合成-10000-条-v6-情绪增强语料">借助 API 批处理高效合成 10,000 条 v6 情绪增强语料<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-22-maeumjigi-21#%E5%80%9F%E5%8A%A9-api-%E6%89%B9%E5%A4%84%E7%90%86%E9%AB%98%E6%95%88%E5%90%88%E6%88%90-10000-%E6%9D%A1-v6-%E6%83%85%E7%BB%AA%E5%A2%9E%E5%BC%BA%E8%AF%AD%E6%96%99" class="hash-link" aria-label="借助 API 批处理高效合成 10,000 条 v6 情绪增强语料的直接链接" title="借助 API 批处理高效合成 10,000 条 v6 情绪增强语料的直接链接" translate="no">​</a></h3>
<blockquote>
<p>“参数约束：统一定长 512 Tokens / 单条平均有效长度 500 Tokens / 覆盖 20 种典型心理咨询人设 / 持久化目标：v6.jsonl”</p>
</blockquote>
<p>为了锻造最顶级的训练集，我们调用 Anthropic API (Haiku) Batch 批处理模式，一口气合成了整整 10,000 条长程心理对话数据 (<code>Emotion_train_dataset_v6.jsonl</code>)。中间虽历经异步批处理无进度回传的紧张焦虑，但借助补全校验脚本 (<code>fill_v6.py</code>) 成功补齐了所有样本。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="探寻黄金分割率第-4-轮多比例矩阵微调">探寻黄金分割率：第 4 轮多比例矩阵微调<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-22-maeumjigi-21#%E6%8E%A2%E5%AF%BB%E9%BB%84%E9%87%91%E5%88%86%E5%89%B2%E7%8E%87%E7%AC%AC-4-%E8%BD%AE%E5%A4%9A%E6%AF%94%E4%BE%8B%E7%9F%A9%E9%98%B5%E5%BE%AE%E8%B0%83" class="hash-link" aria-label="探寻黄金分割率：第 4 轮多比例矩阵微调的直接链接" title="探寻黄金分割率：第 4 轮多比例矩阵微调的直接链接" translate="no">​</a></h3>
<blockquote>
<p>“1 万条 Emotion_train_dataset_v6 已就绪。请分别按 7:3、2:1 以及 5:5 比例混合日常与情绪语料。”
“本次 fine_tuning_4 摆脱单纯调整 LoRA Alpha，转向以语料混合比例 (mixed_2_1, mixed_5_5, mixed_7_3) 作为核心自变量展开横向对抗实验。”</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="比例实验设计" src="https://globaldevlog.com/zh-Hans/assets/images/02_ratio-aa49284ad0e0859f6efd82a31aa871ce.png" width="2507" height="1395" class="img_ev3q"></p>
<p>第 4 轮微调自此化身为一场验证“日常常识”与“专业共情”能否达成最完美动态平衡的技术竞技场。三组异构数据集被注入 Qwen3.5-9B 底座，新一轮 GPU 矩阵训练轰鸣打响！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="开发日志" term="开发日志"/>
        <category label="微调" term="微调"/>
        <category label="数据集" term="数据集"/>
        <category label="提示词" term="提示词"/>
        <category label="毕业设计" term="毕业设计"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 机器人] Sim-to-Real：通过 Arduino Uno、HC-06 蓝牙与 ArUco 视觉追踪实现硬件迁移 (#5)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5"/>
        <updated>2026-05-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[将 Webots 仿真中验证的 648 状态 Q-Learning 策略移植到实体双轮差速 Arduino Uno 机器人上，结合天花板俯视摄像头单应性去畸变与 ArUco 视觉位姿追踪，实现 Sim-to-Real 硬件自主避障实战。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="ArUco 标记视觉追踪画面" src="https://globaldevlog.com/zh-Hans/assets/images/01_aruco_vision-b5bd404f0b35979c465be925a4fac943.png" width="1280" height="720" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (TL;DR)</strong></p>
<ul>
<li class=""><strong>问题 (Problem)</strong>：微控制器 (Arduino Uno ATmega328P) 仅有 2KB SRAM 的极端算力瓶颈、车轮打滑 (Wheel Slip) 与超声波传感噪声，以及 Webots 仿真物理与物理硬件之间典型的<strong>虚实鸿沟 (Reality Gap)</strong>。</li>
<li class=""><strong>解决方案 (Solution)</strong>：采用<strong>分布式 I/O 架构 (HC-06 蓝牙, 115,200 baud)</strong>，上位机 PC 负责 OpenCV 视觉处理与 Q-Table 决策，Arduino 专注电机底层 PWM 驱动；配合俯视摄像头棋盘格去畸变与 <strong>ArUco 标记单应性矩阵 (<code>vision.py</code>)</strong>，实现亚厘米级 (x, y, θ) 实时姿态追踪。</li>
<li class=""><strong>实验结果 (Result)</strong>：仿真训练的 648 状态 Q 表格零样本迁移至物理 1.2m × 1.2m 障碍场地中，在无需真实微调的情况下达成了 <strong>86.7% 的目标到达成功率 (26/30 次试验)</strong>，成功跨越虚实鸿沟。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-sim-to-real-的核心挑战跨越虚实鸿沟-reality-gap">1. Sim-to-Real 的核心挑战：跨越虚实鸿沟 (Reality Gap)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#1-sim-to-real-%E7%9A%84%E6%A0%B8%E5%BF%83%E6%8C%91%E6%88%98%E8%B7%A8%E8%B6%8A%E8%99%9A%E5%AE%9E%E9%B8%BF%E6%B2%9F-reality-gap" class="hash-link" aria-label="1. Sim-to-Real 的核心挑战：跨越虚实鸿沟 (Reality Gap)的直接链接" title="1. Sim-to-Real 的核心挑战：跨越虚实鸿沟 (Reality Gap)的直接链接" translate="no">​</a></h2>
<p>在 Webots 仿真环境中，地面摩擦系数恒定，车轮电机转速确定，超声波测距也无杂散反射。然而在真实物理世界中，不可预测的物理扰动无处不在：</p>
<ol>
<li class=""><strong>算力与内存的绝对瓶颈</strong>：实体小车主控 <strong>Arduino Uno (ATmega328P)</strong> 仅具备 32KB Flash 与 2KB SRAM。在片上运行深度神经网络或复杂的浮点矩阵运算在物理上是不可行的。</li>
<li class=""><strong>传感器噪声与反射盲区</strong>：廉价超声波测距模块在面对亚克力墙壁的大角度倾角时，声波极易发生镜面散射，导致读数归零或突变至最大测量极限。</li>
<li class=""><strong>车轮打滑与累积里程计漂移 (Odometry Drift)</strong>：地面摩擦微小差异与左右直流减速电机的齿轮箱公差，导致即使发出笔直前进指令，小车也会逐渐偏向一侧。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-分布式控制架构上位机-pc大脑-arduino肌肉">2. 分布式控制架构：上位机 PC（大脑）↔ Arduino（肌肉）<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#2-%E5%88%86%E5%B8%83%E5%BC%8F%E6%8E%A7%E5%88%B6%E6%9E%B6%E6%9E%84%E4%B8%8A%E4%BD%8D%E6%9C%BA-pc%E5%A4%A7%E8%84%91-arduino%E8%82%8C%E8%82%89" class="hash-link" aria-label="2. 分布式控制架构：上位机 PC（大脑）↔ Arduino（肌肉）的直接链接" title="2. 分布式控制架构：上位机 PC（大脑）↔ Arduino（肌肉）的直接链接" translate="no">​</a></h2>
<p>为了打破微控制器的算力限制，我们确立了**“PC 充当大脑，Arduino 充当肌肉”**的分布式控制策略：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph CeilingCam["天花板俯视视觉系统 (Ceiling Vision)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Cam["全高清网络摄像头 (30 FPS)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Aruco["ArUco 标记检测 (DICT_4X4_50)&lt;br&gt;单应性矩阵变换 (homography.npy)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Cam --&gt; Aruco</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph PC["上位机 PC (Python 算法引擎)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Pose["机器人平面位姿估计 (x, y, θ)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["648 状态离散化模块"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        QL["已训练 Q 表格 (ε=0)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Host["HC-06 蓝牙串口通信 (115200 baud)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Aruco --&gt; Pose --&gt; Disc --&gt; QL --&gt; BT_Host</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Robot["实体差速移动机器人 (Real Robot)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Slave["HC-06 蓝牙从机模块"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        MCU["Arduino Uno (下位机固件)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Motors["L298N 电机驱动板 + 直流减速电机"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Host &lt;--&gt;|"动作字节指令 [0..3]&lt;br&gt;确认回执/状态"| BT_Slave --&gt; MCU --&gt; Motors</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>上位机 PC 视觉流水线</strong>：安装在天花板上的网络摄像头俯视监控整座竞技场，以 30 FPS 的帧率实时检测小车顶部的 ArUco 二维标记，计算平面绝对姿态 <code>(x, y, θ)</code>，映射进 648 个离散状态区间，并从 Q 表格中读取最优动作。</li>
<li class=""><strong>HC-06 蓝牙通信优化</strong>：为了将蓝牙串口 (SPP) 的往返时延 (RTT) 降至最低，波特率由默认的 9,600 提升至 <strong>115,200 bps</strong>，维持 30~50Hz 的闭环控制响应。</li>
<li class=""><strong>Arduino 下位机固件 (<code>arduino_firmware.ino</code>)</strong>：接收到单字节指令（<code>0: 前进</code>, <code>1: 后退</code>, <code>2: 左转</code>, <code>3: 右转</code>）后，即刻控制 L298N 驱动芯片输出精准的脉冲宽度调制 (PWM) 驱动车轮。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-俯视摄像头单应性校正与-aruco-视觉位姿追踪">3. 俯视摄像头单应性校正与 ArUco 视觉位姿追踪<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#3-%E4%BF%AF%E8%A7%86%E6%91%84%E5%83%8F%E5%A4%B4%E5%8D%95%E5%BA%94%E6%80%A7%E6%A0%A1%E6%AD%A3%E4%B8%8E-aruco-%E8%A7%86%E8%A7%89%E4%BD%8D%E5%A7%BF%E8%BF%BD%E8%B8%AA" class="hash-link" aria-label="3. 俯视摄像头单应性校正与 ArUco 视觉位姿追踪的直接链接" title="3. 俯视摄像头单应性校正与 ArUco 视觉位姿追踪的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="硬件系统搭建与场地标定" src="https://globaldevlog.com/zh-Hans/assets/images/02_hardware_setup-fddea6cef90bd71036b2aff5eef38f33.png" width="1707" height="1067" class="img_ev3q"></p>
<p>为了在物理场地上实现毫米级精度的位姿解算与障碍测距，我们搭建了<strong>基于 OpenCV 的俯视计算机视觉流水线</strong>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-视角校正与单应性变换-calib_autopy">📐 视角校正与单应性变换 (<code>calib_auto.py</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#-%E8%A7%86%E8%A7%92%E6%A0%A1%E6%AD%A3%E4%B8%8E%E5%8D%95%E5%BA%94%E6%80%A7%E5%8F%98%E6%8D%A2-calib_autopy" class="hash-link" aria-label="-视角校正与单应性变换-calib_autopy的直接链接" title="-视角校正与单应性变换-calib_autopy的直接链接" translate="no">​</a></h3>
<ol>
<li class=""><strong>棋盘格去畸变标定</strong>：针对广角镜头的桶形畸变 (Barrel Distortion)，利用标准 9×6 棋盘格提取相机内参矩阵 ($K$) 与畸变系数 ($D$) 进行反畸变映射。</li>
<li class=""><strong>透视单应性变换 (Homography)</strong>：为了将倾斜的摄像头视角投影为正向俯视的 1.2m × 1.2m 鸟瞰视角 (Bird's-eye View)，通过场地 4 个角标坐标拟合出 $3 \times 3$ 单应性矩阵 (<code>homography.npy</code>)。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-aruco-标记位姿解算实现-visionpy">💻 ArUco 标记位姿解算实现 (<code>vision.py</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#-aruco-%E6%A0%87%E8%AE%B0%E4%BD%8D%E5%A7%BF%E8%A7%A3%E7%AE%97%E5%AE%9E%E7%8E%B0-visionpy" class="hash-link" aria-label="-aruco-标记位姿解算实现-visionpy的直接链接" title="-aruco-标记位姿解算实现-visionpy的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 加载 ArUco 字典与检测参数</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">aruco_dict </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">getPredefinedDictionary</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DICT_4X4_50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">parameters </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">DetectorParameters</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">detector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">aruco</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ArucoDetector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">aruco_dict</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> parameters</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_robot_pose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> homography_matrix</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""从透视矫正图像中解算机器人的平面坐标 (x, y) 与航向角 (theta)"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    warped </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">warpPerspective</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frame</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> homography_matrix</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">600</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">600</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    corners</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> detector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">detectMarkers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">warped</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> ids </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        c </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> corners</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        center_x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        center_y </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 利用标记前侧边向量解算航向角 (theta)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        front_x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        front_y </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        theta </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">arctan2</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">front_y </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> center_y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> front_x </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> center_x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> center_x</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> center_y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theta</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-硬件实测避坑排查与真实部署战报">4. 硬件实测避坑排查与真实部署战报<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#4-%E7%A1%AC%E4%BB%B6%E5%AE%9E%E6%B5%8B%E9%81%BF%E5%9D%91%E6%8E%92%E6%9F%A5%E4%B8%8E%E7%9C%9F%E5%AE%9E%E9%83%A8%E7%BD%B2%E6%88%98%E6%8A%A5" class="hash-link" aria-label="4. 硬件实测避坑排查与真实部署战报的直接链接" title="4. 硬件实测避坑排查与真实部署战报的直接链接" translate="no">​</a></h2>
<p>在实体小车联调测试期间，面对突发的硬件边缘工况，我们实施了针对性的工程应对方案：</p>
<table><thead><tr><th style="text-align:left">故障现象 (Troubleshooting)</th><th style="text-align:left">根本原因剖析</th><th style="text-align:left">针对性工程对策</th></tr></thead><tbody><tr><td style="text-align:left"><strong>前进指令发出后小车明显偏航</strong></td><td style="text-align:left">左右直流减速电机的内阻公差与齿轮摩擦不均</td><td style="text-align:left">编写标定脚本 (<code>ble_vision_turn_calib.py</code>)，动态引入左右轮独立 PWM 微调偏移量</td></tr><tr><td style="text-align:left"><strong>蓝牙偶尔掉线导致小车失控冲撞</strong></td><td style="text-align:left">射频干扰引发短暂丢包，单片机维持前序动作</td><td style="text-align:left">在固件中引入 200ms 通信看门狗 (Watchdog)，超时无数据立即全停刹车</td></tr><tr><td style="text-align:left"><strong>日光灯反光引起标记识别偶发性丢失</strong></td><td style="text-align:left">亚克力反光膜在强光照射下产生高光死区</td><td style="text-align:left">优化自适应二值化 (Adaptive Thresholding) 参数，并引入一维卡尔曼滤波进行平滑插值</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-真实物理场地最终基准测试">🏆 真实物理场地最终基准测试<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#-%E7%9C%9F%E5%AE%9E%E7%89%A9%E7%90%86%E5%9C%BA%E5%9C%B0%E6%9C%80%E7%BB%88%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95" class="hash-link" aria-label="🏆 真实物理场地最终基准测试的直接链接" title="🏆 真实物理场地最终基准测试的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>自主导航到达成功率</strong>：在物理 1.2m × 1.2m 障碍竞技场中展开 30 轮独立盲测，最终录得 <strong>86.7% 的成功率 (26/30 次成功抵达)</strong>。</li>
<li class="">仅仅依托事先离散化的 <strong>2,592 个 Q 表格参数（体积仅约 2.5 KB）</strong>，小车未经过任何真实环境梯度微调便成功克服了物理摩擦与传感器噪声。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-webots-强化学习系列全篇结语">5. Webots 强化学习系列全篇结语<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-21-rl-qlearning-5#5-webots-%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97%E5%85%A8%E7%AF%87%E7%BB%93%E8%AF%AD" class="hash-link" aria-label="5. Webots 强化学习系列全篇结语的直接链接" title="5. Webots 强化学习系列全篇结语的直接链接" translate="no">​</a></h2>
<p>至此，历经 5 篇深度记录的 <strong>Webots 强化学习机器人开发日志</strong> 圆满收官：</p>
<ol>
<li class=""><strong>#1</strong>：648 状态空间离散化与 Webots TCP Socket IPC 通信设计</li>
<li class=""><strong>#2</strong>：表格型 Q-Learning 指数衰减探索与多目标稠密奖励整形</li>
<li class=""><strong>#3</strong>：攻克 12 墙障碍地图中的局部最优与 Headless 自动化仿真</li>
<li class=""><strong>#4</strong>：<code>evaluate_qlearning</code> 零探索确定性评测及与 DQN、SARSA 的三方基准测试</li>
<li class=""><strong>#5</strong>：基于 Arduino Uno、蓝牙通信与 ArUco 视觉位姿追踪的 Sim-to-Real 硬件落地</li>
</ol>
<p>从虚拟仿真物理建模、算法核心推导，到大批量多随机种子自动化测试，最终成功落地于低成本嵌入式实体机器人，本系列完整呈现了机器人强化学习研发的全生命周期。感谢大家的关注与支持，后续敬请期待我们全新的前沿开发实录！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="强化学习" term="强化学习"/>
        <category label="Sim-to-Real" term="Sim-to-Real"/>
        <category label="Arduino" term="Arduino"/>
        <category label="机器人学" term="机器人学"/>
        <category label="计算机视觉" term="计算机视觉"/>
        <category label="ArUco" term="ArUco"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心语伴侣毕业设计开发日志 #20] 第三轮微调收官与全栈功能 (DB 持久化、社区看板) 深度整合]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-20-maeumjigi-20</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-maeumjigi-20"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[通过精细校准日常闲聊与危机干预比例完成第 3 轮微调，打通对话持久化数据库与前端社区分页及情绪日记筛选组件。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面图" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-ac5e0126cfa930b87e044dbc1b82cc03.png" width="2519" height="1373" class="img_ev3q"></p>
<p>5 月 19 日至 20 日的两天时间里，第三轮微调 (<code>fine_tuning_3</code>) 的评估自动化与面向生产级 App 服务的全栈功能深度打磨实现了全面齐头并进。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="第三轮微调收尾自动化提取最优-checkpoint-与并行推理">第三轮微调收尾：自动化提取最优 Checkpoint 与并行推理<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-maeumjigi-20#%E7%AC%AC%E4%B8%89%E8%BD%AE%E5%BE%AE%E8%B0%83%E6%94%B6%E5%B0%BE%E8%87%AA%E5%8A%A8%E5%8C%96%E6%8F%90%E5%8F%96%E6%9C%80%E4%BC%98-checkpoint-%E4%B8%8E%E5%B9%B6%E8%A1%8C%E6%8E%A8%E7%90%86" class="hash-link" aria-label="第三轮微调收尾：自动化提取最优 Checkpoint 与并行推理的直接链接" title="第三轮微调收尾：自动化提取最优 Checkpoint 与并行推理的直接链接" translate="no">​</a></h3>
<p>汲取前序微调经验，旨在兼顾知识图谱确定性规则与自然共情引导的第三轮微调实验顺利完成训练：</p>
<blockquote>
<p>“自动遍历 fine_tuning_3 目录下所有实验模型，精准筛选 Eval Loss 表现最优的最佳检查点 (Best Checkpoint)，替换至对应 adapter 目录并汇报指标。”
“编写全新的 run_test_20_30.sh 自动化脚本，在后台针对 fine_tuning_3 展开多卡并行推理与自动评测。”</p>
</blockquote>
<p>告别以往繁琐的人工跨文件夹复制，AI 编写的高可用脚本自主提炼最优权重并配置至推理服务，将 4 组别名候选模型一次性挂在 GPU 服务器后台展开全自动化基准盲测。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="全栈贯通大模型对话-db-持久化与社区互动功能落地">全栈贯通：大模型对话 DB 持久化与社区互动功能落地<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-maeumjigi-20#%E5%85%A8%E6%A0%88%E8%B4%AF%E9%80%9A%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%AF%B9%E8%AF%9D-db-%E6%8C%81%E4%B9%85%E5%8C%96%E4%B8%8E%E7%A4%BE%E5%8C%BA%E4%BA%92%E5%8A%A8%E5%8A%9F%E8%83%BD%E8%90%BD%E5%9C%B0" class="hash-link" aria-label="全栈贯通：大模型对话 DB 持久化与社区互动功能落地的直接链接" title="全栈贯通：大模型对话 DB 持久化与社区互动功能落地的直接链接" translate="no">​</a></h3>
<p>在服务器集群高速运算的同时，本地全栈联调也迎来了实质性突破：</p>
<blockquote>
<p>“深度梳理 seed_data.sql 初始化凭据。默认测试用户初始密码究竟是 Test1234! 吗？当前脚本目标写入的目标 DB 架构为何？”</p>
</blockquote>
<p>为了彻底歼灭此前导致 <code>401 Unauthorized</code> 鉴权死锁的根本诱因，我们逆向审查了数据种子的哈希散列格式，成功修复了认证链路。</p>
<p>5 月 20 日下午，我们提交了一个里程碑意义的 Git Commit：
<code>feat: 챗봇 DB 저장, 게시판 페이지네이션, 감정일기 필터 추가 (支持大模型对话持久化、社区看板分页及情绪日记多维筛选)</code></p>
<p><img decoding="async" loading="lazy" alt="移动端功能截图" src="https://globaldevlog.com/zh-Hans/assets/images/02_app_feature-26d976bed8544fa566bd187f86d90617.png" width="2522" height="1384" class="img_ev3q"></p>
<p>至此，微调大语言模型的交互上下文终于正式打通数据库实现了安全落盘存储！社区看板分页浏览组件以及记录每日心情起伏的日记多维过滤面板也全部在移动端高质量渲染上线。</p>
<p>无形而庞大的前沿大模型，终于被稳稳装入了精美的移动应用容器之中！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="开发日志" term="开发日志"/>
        <category label="微调" term="微调"/>
        <category label="后端" term="后端"/>
        <category label="前端" term="前端"/>
        <category label="数据库" term="数据库"/>
        <category label="毕业设计" term="毕业设计"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 机器人] evaluate_qlearning：确定性导航定量评估与 DQN·SARSA 对比基准 (#4)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析针对已训练 Q-Table 权重的零探索率 (ε=0) 确定性评估流水线 (evaluate_qlearning.py)、路径效率、近距离避碰 (Near-miss) 指标，以及与 DQN、SARSA 模型的全面横向基准测试。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="成功率与步数基准测试" src="https://globaldevlog.com/zh-Hans/assets/images/01_eval_success_steps-1434c5fba2793b6877d335bf289ef59b.png" width="1170" height="650" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (TL;DR)</strong></p>
<ul>
<li class=""><strong>目的 (Objective)</strong>：在完全关闭训练探索噪声 (ε=0) 的纯确定性策略 (Greedy Policy) 下，验证策略在真实部署环境中的自律导航可靠性与路径质量。</li>
<li class=""><strong>评估方法 (Evaluation)</strong>：通过 <code>evaluate_qlearning.py</code> 脚本，在 5 组独立测试随机种子 (Eval Seeds 0~4) 上执行共计 30 轮评估，严密监测成功率、理论最短路径比率 (Path Efficiency) 及接近障碍险情 (Near-miss) 频次。</li>
<li class=""><strong>实验结果 (Result)</strong>：表格型 Q-Learning 在 12 墙障碍地图中斩获 93.3% 的确定性到达成功率，且单步推理延迟仅为 0.02ms，在执行效率与轻量化上显著优于沉重的深度神经网络 DQN 与保守的在线学习 SARSA。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-定量评估流水线设计-evaluate_qlearningpy">1. 定量评估流水线设计 (<code>evaluate_qlearning.py</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#1-%E5%AE%9A%E9%87%8F%E8%AF%84%E4%BC%B0%E6%B5%81%E6%B0%B4%E7%BA%BF%E8%AE%BE%E8%AE%A1-evaluate_qlearningpy" class="hash-link" aria-label="1-定量评估流水线设计-evaluate_qlearningpy的直接链接" title="1-定量评估流水线设计-evaluate_qlearningpy的直接链接" translate="no">​</a></h2>
<p>训练过程中记录的奖励曲线混杂了 ε-贪婪探索的随机噪声，无法真实反映即将在实体机器人上执行的<strong>确定性贪婪策略 (Deterministic Greedy Policy)</strong> 的真实水准。</p>
<p>为此，我们构建了专门的评估引擎，加载训练保存好的权重文件 (<code>models/&lt;scenario&gt;/seed_&lt;seed&gt;/qlearning_qtable.npy</code>)，并将探索率严格固定为 $\epsilon = 0$：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Model["已训练 Q 表格&lt;br&gt;(qlearning_qtable.npy)"] --&gt; Agent["贪婪智能体&lt;br&gt;(ε = 0 固定)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    EvalSeeds["5 组评估随机种子&lt;br&gt;(EVAL_SEEDS 0..4)"] --&gt; Webots["Webots 仿真器&lt;br&gt;(30 次独立测试)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Agent &lt;--&gt; Webots</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Webots --&gt; CSV["轨迹与指标记录&lt;br&gt;(eval_log.csv / traces)"]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-核心评估循环代码">💻 核心评估循环代码<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#-%E6%A0%B8%E5%BF%83%E8%AF%84%E4%BC%B0%E5%BE%AA%E7%8E%AF%E4%BB%A3%E7%A0%81" class="hash-link" aria-label="💻 核心评估循环代码的直接链接" title="💻 核心评估循环代码的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># evaluate_qlearning.py 核心逻辑摘录</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">agent </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> QLearningAgent</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">agent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">load</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 加载已训练的 Q-Table</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> ep </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_episodes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    env_seed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> EVAL_SEEDS</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">ep </span><span class="token operator" style="color:#393A34">%</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">EVAL_SEEDS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    raw</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> info </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">reset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">env_seed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> return_info</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> t </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> max_steps </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        a </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> agent</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">act</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deterministic</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 纯粹贪婪决策 argmax Q(s, a)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        raw_next</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> info </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> env</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">step</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">a</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_next</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># 记录轨迹坐标与近距擦碰险情 (dist &lt;= 0.15m)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        record_metrics</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">break</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-核心评估指标与度量准则">2. 核心评估指标与度量准则<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#2-%E6%A0%B8%E5%BF%83%E8%AF%84%E4%BC%B0%E6%8C%87%E6%A0%87%E4%B8%8E%E5%BA%A6%E9%87%8F%E5%87%86%E5%88%99" class="hash-link" aria-label="2. 核心评估指标与度量准则的直接链接" title="2. 核心评估指标与度量准则的直接链接" translate="no">​</a></h2>
<p>除了基础的“是否到达目标”之外，我们从<strong>平滑度与安全性</strong>等多维度进行了深入度量：</p>
<ol>
<li class=""><strong>到达成功率 (Success Rate)</strong>：在 150 步以内无碰撞且顺利进入目标半径 (0.10m) 范围的回合比例。</li>
<li class=""><strong>路径效率 (Path Efficiency)</strong>：理论最短几何距离 (L_shortest) 与机器人实际行驶累积距离 (L_actual) 的百分比比值：
<strong>Path Efficiency (%) = (Shortest Path / Actual Path) × 100%</strong></li>
<li class=""><strong>近距危险频次 (Near-Miss Count)</strong>：机器人超声波测距与墙体距离低于 0.15m 的危险步数。</li>
<li class=""><strong>振荡与打转指标 (Oscillation &amp; Spin)</strong>：连续左右摆头抖动与原地无效打转的步数统计。</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-三大算法横向基准测试q-learning-vs-sarsa-vs-dqn">3. 三大算法横向基准测试：Q-Learning vs. SARSA vs. DQN<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#3-%E4%B8%89%E5%A4%A7%E7%AE%97%E6%B3%95%E6%A8%AA%E5%90%91%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95q-learning-vs-sarsa-vs-dqn" class="hash-link" aria-label="3. 三大算法横向基准测试：Q-Learning vs. SARSA vs. DQN的直接链接" title="3. 三大算法横向基准测试：Q-Learning vs. SARSA vs. DQN的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="行驶质量与对比基准" src="https://globaldevlog.com/zh-Hans/assets/images/02_eval_quality-42dd1fc5e31c5e63d48c26ece24e8936.png" width="1625" height="585" class="img_ev3q"></p>
<p>在完全一致的 Webots 12 墙障碍竞技场中，对三种经典强化学习算法展开了对齐测试，揭示了各自的技术权衡：</p>
<table><thead><tr><th style="text-align:left">评估指标</th><th style="text-align:center">Tabular Q-Learning (本项目)</th><th style="text-align:center">SARSA (在线策略 On-policy)</th><th style="text-align:center">Deep Q-Network (DQN)</th></tr></thead><tbody><tr><td style="text-align:left"><strong>学习范式</strong></td><td style="text-align:center">离策略时序差分 TD(0)</td><td style="text-align:center">在线策略时序差分 TD(0)</td><td style="text-align:center">神经网络深度强化学习 (Deep RL)</td></tr><tr><td style="text-align:left"><strong>参数体积</strong></td><td style="text-align:center"><strong>2,592 个浮点数 (~2.5 KB)</strong></td><td style="text-align:center">2,592 个浮点数 (~2.5 KB)</td><td style="text-align:center">约 150,000 个权重参数 (~600 KB)</td></tr><tr><td style="text-align:left"><strong>确定性成功率 (<code>obstacle12</code>)</strong></td><td style="text-align:center"><strong>93.3%</strong></td><td style="text-align:center">86.7%</td><td style="text-align:center">90.0%</td></tr><tr><td style="text-align:left"><strong>单回合平均步数</strong></td><td style="text-align:center"><strong>41.2 步</strong></td><td style="text-align:center">48.5 步</td><td style="text-align:center">44.7 步</td></tr><tr><td style="text-align:left"><strong>单步推理延迟 (Latency)</strong></td><td style="text-align:center"><strong>0.02 ms</strong> (NumPy 数组寻址)</td><td style="text-align:center">0.02 ms</td><td style="text-align:center">2.15 ms (PyTorch 前向传播)</td></tr><tr><td style="text-align:left"><strong>近距擦碰危险 (Near-miss)</strong></td><td style="text-align:center">较低 (游刃有余的绕行)</td><td style="text-align:center">极低 (过度避碰，极端保守)</td><td style="text-align:center">中等</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-算法行为深度剖析">🔍 算法行为深度剖析<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#-%E7%AE%97%E6%B3%95%E8%A1%8C%E4%B8%BA%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90" class="hash-link" aria-label="🔍 算法行为深度剖析的直接链接" title="🔍 算法行为深度剖析的直接链接" translate="no">​</a></h3>
<ul>
<li class=""><strong>Q-Learning 的优势</strong>：得益于 <code>max_a' Q(s', a')</code> 的离策略特性，Q-Learning 在反向传播时剔除了探索阶段遭遇的随机碰撞惩罚，能够精确收敛至<strong>最短可行迂回路径</strong>。</li>
<li class=""><strong>SARSA 的局限性</strong>：由于根据实际采样的下一步动作更新价值，SARSA 过度畏惧探索期的碰撞，导致策略<strong>大幅度绕开墙体，走出过于冗长、效率低下的迂回航线</strong>。</li>
<li class=""><strong>DQN 的权衡</strong>：虽然能精细表达连续状态，但训练稳定性高度依赖经验回放池与目标网络，且 2.15ms 的推理开销与 GPU 依赖对于 Arduino 等超低功耗微控制器带来了不可承受的负担。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-定量评估结论">4. 定量评估结论<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#4-%E5%AE%9A%E9%87%8F%E8%AF%84%E4%BC%B0%E7%BB%93%E8%AE%BA" class="hash-link" aria-label="4. 定量评估结论的直接链接" title="4. 定量评估结论的直接链接" translate="no">​</a></h2>
<p>定量基准测试表明，压缩至 648 个状态区间的<strong>表格型 Q-Learning 仅凭 2.5 KB 的极小内存开销</strong>，不仅在成功率 (93.3%) 上超越了复杂的深度神经网络 (DQN)，更在实时推理吞吐与路径经济性上表现优异。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-20-rl-qlearning-4#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接链接" title="5. 下一步研究方向的直接链接" translate="no">​</a></h2>
<p>至此，在仿真环境中的全套定量评估已圆满收官。强化学习最具挑战性的最终门槛在于<strong>向现实物理世界迁移 (Sim-to-Real)</strong>。</p>
<p>在最后的第 5 篇中，我们将深入硬件实战：将训练好的 Q-Table 部署至 <strong>2 轮差速 Arduino Uno 小车，通过 HC-06 蓝牙通信联动，并结合俯视摄像头棋盘格去畸变标定与 ArUco 视觉标记追踪</strong>，揭秘软硬件联调与 Sim-to-Real 的关键避坑指南！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="强化学习" term="强化学习"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="性能评估" term="性能评估"/>
        <category label="基准测试" term="基准测试"/>
        <category label="DQN" term="DQN"/>
        <category label="SARSA" term="SARSA"/>
        <category label="机器人学" term="机器人学"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 机器人] 克服 12 面墙体障碍与 Local Minima 陷阱：多随机种子 Headless 仿真 (#3)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3"/>
        <updated>2026-05-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度剖析在 12 面墙体内置障碍环境 (obstacle12) 及高难度地图中面临的局部最优 (Local Minima) 瓶颈，以及基于 Webots Headless 无渲染加速流水线实现的多随机种子并行强化学习训练实战。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="obstacle12 仿真环境" src="https://globaldevlog.com/zh-Hans/assets/images/01_obstacle12_arena-bc2bc496b7ff2cdfe32dd9630f39bd68.png" width="1438" height="966" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (TL;DR)</strong></p>
<ul>
<li class=""><strong>问题 (Problem)</strong>：当目标位于墙体正后方时，由于距离增量奖励 (+0.1 × Δdist) 固有的贪婪特性，机器人无法主动绕开障碍，而是反复正面撞墙，深陷严重的<strong>局部最优 (Local Minima)</strong> 死锁。</li>
<li class=""><strong>解决方案 (Solution)</strong>：将 3 方向超声波摇头扫描（前/左/右）状态感知与长远视界贝尔曼最优价值反向传播 (γ = 0.99) 相结合，并构建基于 PowerShell 的 <strong>Webots Headless 无渲染加速流水线 (<code>headless_run.ps1</code>)</strong>，对种子 0、1、2 进行自动化重复实验。</li>
<li class=""><strong>实验结果 (Result)</strong>：在 12 面墙体障碍环境下，智能体成功习得稳健的侧向绕行策略 (Detour Policy)，训练 600 个回合后目标到达成功率稳定维持在 90% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-障碍环境面临的严峻挑战local-minima局部最优陷阱">1. 障碍环境面临的严峻挑战：Local Minima（局部最优）陷阱<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#1-%E9%9A%9C%E7%A2%8D%E7%8E%AF%E5%A2%83%E9%9D%A2%E4%B8%B4%E7%9A%84%E4%B8%A5%E5%B3%BB%E6%8C%91%E6%88%98local-minima%E5%B1%80%E9%83%A8%E6%9C%80%E4%BC%98%E9%99%B7%E9%98%B1" class="hash-link" aria-label="1. 障碍环境面临的严峻挑战：Local Minima（局部最优）陷阱的直接链接" title="1. 障碍环境面临的严峻挑战：Local Minima（局部最优）陷阱的直接链接" translate="no">​</a></h2>
<p>在无障碍的开阔竞技场 (<code>no_obstacle</code>) 中，智能体只需调整朝向直面目标即可。然而，当在室内场地引入 <strong>12 面内置隔断墙 (<code>obstacle12</code>)</strong> 时，奖励函数的局限性暴露出了致命瓶颈：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start["起点 (Robot)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall["墙体障碍 (Wall)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Goal["目标位置 (Goal)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start --&gt;|"直线前进（贪婪距离缩短奖励）"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall -.-&gt;|"碰撞并陷入停滞 (Local Minima)"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start ==&gt;|"侧向绕行策略 (Detour Path)&lt;br&gt;初期承受距离变远扣分"| Goal</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>直观困境</strong>：当目标位于墙壁正后方时，朝向目标前行将直接诱发墙面碰撞。</li>
<li class=""><strong>奖励冲突与死锁</strong>：要成功绕过障碍，智能体必须暂时背离目标行驶（导致即时距离增量奖励为负）。在缺乏充分探索的情况下，贪婪策略拒绝接受即时惩罚，导致机器人<strong>在墙前频繁打转、不断发生轻微刮碰或耗尽 150 步超时上限</strong>。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-破局之道摇头扫描感知与时序差分价值反向传播">2. 破局之道：摇头扫描感知与时序差分价值反向传播<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#2-%E7%A0%B4%E5%B1%80%E4%B9%8B%E9%81%93%E6%91%87%E5%A4%B4%E6%89%AB%E6%8F%8F%E6%84%9F%E7%9F%A5%E4%B8%8E%E6%97%B6%E5%BA%8F%E5%B7%AE%E5%88%86%E4%BB%B7%E5%80%BC%E5%8F%8D%E5%90%91%E4%BC%A0%E6%92%AD" class="hash-link" aria-label="2. 破局之道：摇头扫描感知与时序差分价值反向传播的直接链接" title="2. 破局之道：摇头扫描感知与时序差分价值反向传播的直接链接" translate="no">​</a></h2>
<p>攻克该局部最优陷阱的关键在于将 <strong>648 状态空间下的 3 方向测距感知（前方、左侧、右侧）</strong> 与 <strong>高折扣因子 (γ = 0.99)</strong> 深度结合：</p>
<ol>
<li class=""><strong>多维度状态感知</strong>：<!-- -->
<ul>
<li class="">当机器人处于前方障碍危险 (<code>front_distance = Danger</code>) 时盲目前行，将承受强烈的碰撞惩罚 (-1.0)。</li>
<li class="">若利用舵机摇头探测到侧方开阔通道 (<code>left_distance = Safe</code> 或 <code>right_distance = Safe</code>) 并执行转向，则可规避撞击。</li>
</ul>
</li>
<li class=""><strong>长远时序差分信用分配 (Temporal Difference Credit Assignment)</strong>：<!-- -->
<ul>
<li class="">尽管侧向绕行初期距离目标变远，但只要最终成功绕过墙壁并命中目标，就能获得丰厚的正向终止奖励 (+1.0)。</li>
<li class="">在 γ = 0.99 的折扣传播下，终止奖励沿绕行状态序列逆向渗透进 Q(s, a) 表格，智能体逐步学会：<strong>“在墙壁面前，暂时偏离目标的迂回路线在长远预期中能带来更高累积价值”</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-webots-headless-高性能自动化仿真流水线">3. Webots Headless 高性能自动化仿真流水线<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#3-webots-headless-%E9%AB%98%E6%80%A7%E8%83%BD%E8%87%AA%E5%8A%A8%E5%8C%96%E4%BB%BF%E7%9C%9F%E6%B5%81%E6%B0%B4%E7%BA%BF" class="hash-link" aria-label="3. Webots Headless 高性能自动化仿真流水线的直接链接" title="3. Webots Headless 高性能自动化仿真流水线的直接链接" translate="no">​</a></h2>
<p>在复杂的障碍地图中，为了验证强化学习算法的鲁棒性，严谨的<strong>多随机种子 (Seeds 0, 1, 2) 交叉验证</strong>必不可少。若每次都在 Webots 桌面图形界面下手动跑完 3 轮各 1,500 个回合，将耗费数十小时。</p>
<p>我们编写了 <strong><code>headless_run.ps1</code> PowerShell 脚本</strong>，在后台以无渲染高速模式 (<code>--mode=fast --no-rendering</code>) 启动 Webots，实现全自动无人值守训练与收敛绘图：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-自动化无渲染流水线-headless_runps1">💻 自动化无渲染流水线 (<code>headless_run.ps1</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#-%E8%87%AA%E5%8A%A8%E5%8C%96%E6%97%A0%E6%B8%B2%E6%9F%93%E6%B5%81%E6%B0%B4%E7%BA%BF-headless_runps1" class="hash-link" aria-label="-自动化无渲染流水线-headless_runps1的直接链接" title="-自动化无渲染流水线-headless_runps1的直接链接" translate="no">​</a></h3>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># 以高速 Headless 模式后台启动 Webots</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">$webotsProc = Start-Process -FilePath $WebotsExe `</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    -ArgumentList @("--mode=fast", "--no-rendering", "--stdout", "--stderr", $WorldFile) `</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    -PassThru</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 监听套接字并启动强化学习训练</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python train_qlearning.py --scenario obstacle12 --seed 0 --episodes 1500</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># 训练完成后强制清理进程并生成训练收敛曲线</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Stop-Process -Id $webotsProc.Id -Force</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">python -m qlearning.plot_curves --log-dir logs/obstacle12/seed_0</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-实证评估跨环境仿真学习曲线对比">4. 实证评估：跨环境仿真学习曲线对比<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#4-%E5%AE%9E%E8%AF%81%E8%AF%84%E4%BC%B0%E8%B7%A8%E7%8E%AF%E5%A2%83%E4%BB%BF%E7%9C%9F%E5%AD%A6%E4%B9%A0%E6%9B%B2%E7%BA%BF%E5%AF%B9%E6%AF%94" class="hash-link" aria-label="4. 实证评估：跨环境仿真学习曲线对比的直接链接" title="4. 实证评估：跨环境仿真学习曲线对比的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="多环境学习曲线对比" src="https://globaldevlog.com/zh-Hans/assets/images/02_comparison_curves-0d241e07d5b445acc1866d00247179b7.png" width="1625" height="585" class="img_ev3q"></p>
<p>上图展示了开阔平原 (<code>no_obstacle</code>) 与 12 墙障碍场地 (<code>obstacle12</code>) 在 3 组独立随机种子下的完整学习曲线对比。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-核心基准测试指标">📊 核心基准测试指标<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#-%E6%A0%B8%E5%BF%83%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95%E6%8C%87%E6%A0%87" class="hash-link" aria-label="📊 核心基准测试指标的直接链接" title="📊 核心基准测试指标的直接链接" translate="no">​</a></h3>
<table><thead><tr><th style="text-align:left">评估指标</th><th style="text-align:center">开阔平原 (<code>no_obstacle</code>)</th><th style="text-align:center">12 面墙障碍场地 (<code>obstacle12</code>)</th></tr></thead><tbody><tr><td style="text-align:left"><strong>初期收敛拐点</strong></td><td style="text-align:center">约 300 ~ 400 回合</td><td style="text-align:center">约 600 ~ 800 回合</td></tr><tr><td style="text-align:left"><strong>最终成功率 (100 回合移动平均)</strong></td><td style="text-align:center"><strong>98.5% ~ 99.2%</strong></td><td style="text-align:center"><strong>91.4% ~ 94.8%</strong></td></tr><tr><td style="text-align:left"><strong>单回合平均消耗步数</strong></td><td style="text-align:center">15 ~ 22 步</td><td style="text-align:center">38 ~ 52 步 (包含迂回路径)</td></tr><tr><td style="text-align:left"><strong>随机种子间方差</strong></td><td style="text-align:center">极低（策略高度收敛稳定）</td><td style="text-align:center">中等（受初期随机探索路径影响存在差异）</td></tr></tbody></table>
<ul>
<li class=""><strong>探索周期拉长</strong>：在 <code>obstacle12</code> 场景下，智能体摸索出可用绕行走廊前碰撞极其频繁，平均回报在负数区间 (-0.6 ~ -0.4) 持续到了第 400 回合。</li>
<li class=""><strong>绕行策略确立</strong>：在 800 回合之后，所有随机种子的成功率均跃升至 90% 以上，证明机器人已牢固掌握在复杂迷宫夹缝中灵活穿行且无碰撞的高级避障策略。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-19-rl-qlearning-3#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接链接" title="5. 下一步研究方向的直接链接" translate="no">​</a></h2>
<p>我们成功证明了 648 状态离散表格型 Q-Learning 能够应对迷宫级别的复杂障碍绕行。</p>
<p>在第 4 篇中，我们将加载训练完成的权重文件 (<code>models/*.npy</code>)，在关闭探索策略的情况下利用 <strong><code>evaluate_qlearning.py</code> 进行确定性测试，并与 DQN 及 SARSA 等经典算法开展横向性能对比分析</strong>！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="强化学习" term="强化学习"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="Webots" term="Webots"/>
        <category label="避障" term="避障"/>
        <category label="局部最优" term="局部最优"/>
        <category label="Headless" term="Headless"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 机器人] 表格型 Q-Learning 实现与奖励函数(Reward Shaping)调优 (#2)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2"/>
        <updated>2026-05-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[深度解析 Webots 移动机器人的表格型 Q-Learning (Tabular Q-Learning) TD(0) 更新公式、指数衰减 ε-贪婪探索策略，以及克服稀疏奖励 (Sparse Reward) 的稠密奖励整形 (Dense Reward Shaping) 调优实战。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="训练奖励曲线" src="https://globaldevlog.com/zh-Hans/assets/images/01_reward_curve-dafe6b9a6b58ab351aa0d2717b504f0a.png" width="800" height="400" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (TL;DR)</strong></p>
<ul>
<li class=""><strong>问题 (Problem)</strong>：在仅在目标到达 (+1.0) 和墙体碰撞 (-1.0) 时给予反馈的稀疏奖励 (Sparse Reward) 环境下，智能体往往需要经历数千个回合的原地打转或盲目徘徊，才能偶然撞见目标，探索严重滞后。</li>
<li class=""><strong>解决方案 (Solution)</strong>：设计了基于势能的欧氏距离增量奖励 (+0.1 × Δdist) 与单步 (-0.01)、转向 (-0.01)、倒车 (-0.05) 惩罚相结合的多目标<strong>稠密奖励整形 (Dense Reward Shaping)</strong> 机制，并配合指数衰减的 ε-贪婪 (Epsilon-Greedy, 1.0 → 0.05) 探索策略。</li>
<li class=""><strong>实验结果 (Result)</strong>：在无障碍 (<code>no_obstacle</code>) 场景下，移动平均奖励在 500 个回合以内稳步转正并进入高位区间 (0.7+)，最终导航成功率达到 98% 以上。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-表格型-q-learning-更新公式与智能体架构">1. 表格型 Q-Learning 更新公式与智能体架构<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#1-%E8%A1%A8%E6%A0%BC%E5%9E%8B-q-learning-%E6%9B%B4%E6%96%B0%E5%85%AC%E5%BC%8F%E4%B8%8E%E6%99%BA%E8%83%BD%E4%BD%93%E6%9E%B6%E6%9E%84" class="hash-link" aria-label="1. 表格型 Q-Learning 更新公式与智能体架构的直接链接" title="1. 表格型 Q-Learning 更新公式与智能体架构的直接链接" translate="no">​</a></h2>
<p>表格型 Q-Learning (Tabular Q-Learning) 是一种建立在贝尔曼最优方程 (Bellman Optimality Equation) 之上的<strong>离策略时序差分 (Off-policy Temporal Difference, TD(0))</strong> 算法。</p>
<p>智能体维护一个形状为 $(648, 4)$ 的二维 NumPy 数组作为动作价值表 $Q(s, a)$。利用从 Webots 套接字服务器流式传输的状态转移元组 $(s, a, r, s', done)$，智能体根据以下公式实时更新 $Q$ 值：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-q-learning-td0-核心更新公式">📐 Q-Learning TD(0) 核心更新公式<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#-q-learning-td0-%E6%A0%B8%E5%BF%83%E6%9B%B4%E6%96%B0%E5%85%AC%E5%BC%8F" class="hash-link" aria-label="📐 Q-Learning TD(0) 核心更新公式的直接链接" title="📐 Q-Learning TD(0) 核心更新公式的直接链接" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Target = r + γ * max_a' Q(s', a') * (1 - done)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">TD Error = Target - Q(s, a)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Q(s, a) &lt;- Q(s, a) + α * TD Error</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>学习率 (Learning Rate, α = 0.1)</strong>：平衡吸收全新经验与保留历史估计值之间的权重。</li>
<li class=""><strong>折扣因子 (Discount Factor, γ = 0.99)</strong>：量化未来长期累积回报的现值，对多步导航至关重要。</li>
<li class=""><strong>终止状态掩码 (<code>1 - done</code>)</strong>：在到达目标或发生碰撞导致回合结束时，将未来的预期价值掩码为 0，精准提取终止即时奖励。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-智能体核心实现-qlearning_agentpy">💻 智能体核心实现 (<code>qlearning_agent.py</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#-%E6%99%BA%E8%83%BD%E4%BD%93%E6%A0%B8%E5%BF%83%E5%AE%9E%E7%8E%B0-qlearning_agentpy" class="hash-link" aria-label="-智能体核心实现-qlearning_agentpy的直接链接" title="-智能体核心实现-qlearning_agentpy的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">QLearningAgent</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_states</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">648</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_actions</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> alpha</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> gamma</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.99</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 eps_start</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps_min</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.05</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> eps_decay</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0.995</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> seed</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">default_rng</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seed</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">zeros</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_states</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">float64</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">alpha </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">alpha</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gamma </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gamma</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_start</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_min </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_min</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_decay </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eps_decay</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">n_actions </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">act</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deterministic</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">False</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""ε-贪婪行为策略：探索与利用的动态权衡"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> deterministic</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">rng</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">integers</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">n_actions</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">argmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">update</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> s_next</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> done</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bool</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""TD(0) 离策略 Q 表格更新"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        target </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> done </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">gamma </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s_next</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        td_error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> target </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Q</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">alpha </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> td_error</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">td_error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">decay_epsilon</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token triple-quoted-string string" style="color:#e3116c">"""每个回合结束时执行的指数衰减"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_min</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">epsilon </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">eps_decay</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-指数衰减-ε-贪婪探索策略">2. 指数衰减 ε-贪婪探索策略<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#2-%E6%8C%87%E6%95%B0%E8%A1%B0%E5%87%8F-%CE%B5-%E8%B4%AA%E5%A9%AA%E6%8E%A2%E7%B4%A2%E7%AD%96%E7%95%A5" class="hash-link" aria-label="2. 指数衰减 ε-贪婪探索策略的直接链接" title="2. 指数衰减 ε-贪婪探索策略的直接链接" translate="no">​</a></h2>
<p>在训练初期，智能体对场地环境一无所知，必须依赖 <strong>ε = 1.0 (100% 全随机探索)</strong> 来触碰边界并发现目标。</p>
<p>然而，若不对探索率进行收敛，即便智能体已经摸索出最优路径，仍会因高频随机动作而不断撞墙。为此，我们在每个回合结束时应用逐回合指数衰减公式：</p>
<p><strong>ε_(ep+1) = max(ε_min, ε_ep × 0.995)</strong></p>
<ul>
<li class=""><strong>第 1 回合</strong>：ε = 1.0（全方位随机探索）</li>
<li class=""><strong>第 300 回合</strong>：ε ≈ 0.22（探索显著收敛，重心向利用高 Q 值经验倾斜）</li>
<li class=""><strong>第 600 回合</strong>：ε = 0.05（降至设定的最低探索下限 ε_min，保持 95% 确定性最优利用）</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-克服稀疏导航奖励的稠密奖励整形机制">3. 克服稀疏导航奖励的稠密奖励整形机制<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#3-%E5%85%8B%E6%9C%8D%E7%A8%80%E7%96%8F%E5%AF%BC%E8%88%AA%E5%A5%96%E5%8A%B1%E7%9A%84%E7%A8%A0%E5%AF%86%E5%A5%96%E5%8A%B1%E6%95%B4%E5%BD%A2%E6%9C%BA%E5%88%B6" class="hash-link" aria-label="3. 克服稀疏导航奖励的稠密奖励整形机制的直接链接" title="3. 克服稀疏导航奖励的稠密奖励整形机制的直接链接" translate="no">​</a></h2>
<p>移动机器人导航面临的核心挑战是<strong>极端稀疏的奖励反馈 (Sparse Reward Feedback)</strong>。在 1.2m × 1.2m 的竞技场中，随机游走几乎无法获得任何有效反馈，直到偶然撞入 0.10m 的目标容差范围。</p>
<p>为了有效引导策略收敛，我们在 Webots 控制器 (<code>RL_controller.py</code>) 内部设计了多层<strong>稠密奖励整形 (Dense Reward Shaping)</strong> 体系：</p>
<table><thead><tr><th style="text-align:left">奖励组成项</th><th style="text-align:center">数值设定</th><th style="text-align:left">设计意图与物理效果</th></tr></thead><tbody><tr><td style="text-align:left"><strong>到达目标 (<code>REWARD_GOAL</code>)</strong></td><td style="text-align:center"><strong>+1.0</strong></td><td style="text-align:left">成功抵达目标位置时的正向终止奖励</td></tr><tr><td style="text-align:left"><strong>碰撞墙壁/障碍 (<code>REWARD_COLLISION</code>)</strong></td><td style="text-align:center"><strong>-1.0</strong></td><td style="text-align:left">发生物理接触时的严厉惩罚与回合截断</td></tr><tr><td style="text-align:left"><strong>距离增量奖励 (<code>REWARD_DELTA_SCALE</code>)</strong></td><td style="text-align:center"><strong>+0.1 × Δdist</strong></td><td style="text-align:left">与欧氏距离缩短量成正比的即时势能引导反馈</td></tr><tr><td style="text-align:left"><strong>单步时间惩罚 (<code>REWARD_STEP</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">防止停滞不前，鼓励智能体探索最短用时路径</td></tr><tr><td style="text-align:left"><strong>转向惩罚 (<code>REWARD_TURN</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">抑制无意义的原地高频摆头与打转行为</td></tr><tr><td style="text-align:left"><strong>倒车惩罚 (<code>REWARD_BACKWARD</code>)</strong></td><td style="text-align:center"><strong>-0.05</strong></td><td style="text-align:left">惩罚效率低下的后退动作，强化直面前方目标</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-距离增量-distance-delta-的数学原理">🔍 距离增量 (Distance Delta) 的数学原理<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#-%E8%B7%9D%E7%A6%BB%E5%A2%9E%E9%87%8F-distance-delta-%E7%9A%84%E6%95%B0%E5%AD%A6%E5%8E%9F%E7%90%86" class="hash-link" aria-label="🔍 距离增量 (Distance Delta) 的数学原理的直接链接" title="🔍 距离增量 (Distance Delta) 的数学原理的直接链接" translate="no">​</a></h3>
<p><strong>Δdist = dist_prev - dist_curr</strong></p>
<ul>
<li class="">朝目标方向前进一步 (0.05m)，立即获得 +0.1 × 0.05 = +0.005 的即时正向激励。</li>
<li class="">相反，若背离目标行驶，则会承受即时扣分。这使得智能体在最初 100 个回合内就能迅速学会将车头航向对准目标中心。</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-实证评估奖励曲线与成功率收敛">4. 实证评估：奖励曲线与成功率收敛<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#4-%E5%AE%9E%E8%AF%81%E8%AF%84%E4%BC%B0%E5%A5%96%E5%8A%B1%E6%9B%B2%E7%BA%BF%E4%B8%8E%E6%88%90%E5%8A%9F%E7%8E%87%E6%94%B6%E6%95%9B" class="hash-link" aria-label="4. 实证评估：奖励曲线与成功率收敛的直接链接" title="4. 实证评估：奖励曲线与成功率收敛的直接链接" translate="no">​</a></h2>
<p>在 <code>no_obstacle</code>（无障碍场地）中进行 1,500 个回合的完整训练，展现了与理论分析高度吻合的优异收敛特性。</p>
<p><img decoding="async" loading="lazy" alt="成功率曲线" src="https://globaldevlog.com/zh-Hans/assets/images/02_success_curve-a47390e774a7a40ace8869f29884f2bd.png" width="800" height="400" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-收敛阶段里程碑">📈 收敛阶段里程碑<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#-%E6%94%B6%E6%95%9B%E9%98%B6%E6%AE%B5%E9%87%8C%E7%A8%8B%E7%A2%91" class="hash-link" aria-label="📈 收敛阶段里程碑的直接链接" title="📈 收敛阶段里程碑的直接链接" translate="no">​</a></h3>
<ol>
<li class=""><strong>第 0 ~ 200 回合（初期探索阶段）</strong>：<!-- -->
<ul>
<li class="">频繁发生撞墙以及达到 150 步超时上限，移动平均奖励徘徊在负数区间 (-0.4 ~ -0.2)。</li>
</ul>
</li>
<li class=""><strong>第 200 ~ 500 回合（快速攀升阶段）</strong>：<!-- -->
<ul>
<li class="">随着距离增量势能奖励在 Q 表格中有效反向传播，策略在 <code>goal_heading</code> 居中时建立起强烈的直行倾向。</li>
<li class="">成功率从 20% 迅猛攀升至 <strong>80% 以上</strong>。</li>
</ul>
</li>
<li class=""><strong>第 500 ~ 1,500 回合（渐近收敛稳定阶段）</strong>：<!-- -->
<ul>
<li class="">ε 稳定在 0.05 下限，单回合平均消耗步数从 150 步剧降至 <strong>15 ~ 22 步</strong>。</li>
<li class="">100 回合移动平均成功率稳定收敛在 <strong>98.5% 以上</strong>。</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下一步研究方向">5. 下一步研究方向<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-18-rl-qlearning-2#5-%E4%B8%8B%E4%B8%80%E6%AD%A5%E7%A0%94%E7%A9%B6%E6%96%B9%E5%90%91" class="hash-link" aria-label="5. 下一步研究方向的直接链接" title="5. 下一步研究方向的直接链接" translate="no">​</a></h2>
<p>我们已经验证，在 648 个离散状态空间下，表格型 Q-Learning 能够完美征服开阔平原导航。</p>
<p>然而，<strong>真实物理世界充满着复杂的墙体与死胡同障碍</strong>。在第 3 篇中，我们将直面 <strong><code>obstacle12</code> 与 <code>obstacle_hard</code> 迷宫地图</strong>，深入剖析智能体如何跳出局部最优陷阱，并利用多种子并行训练构建高鲁棒性导航策略！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="强化学习" term="强化学习"/>
        <category label="Q-Learning" term="Q-Learning"/>
        <category label="奖励函数" term="奖励函数"/>
        <category label="Webots" term="Webots"/>
        <category label="机器人学" term="机器人学"/>
        <category label="机器学习" term="机器学习"/>
        <category label="Python" term="Python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 毕业设计开发日志 #19] 自动化评测报告生成与 FastAPI PostgreSQL 401 鉴权故障排查]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[攻克全栈联调瓶颈：利用 Claude Code 全自动生成大模型评测报告，彻底排查并解决 PostgreSQL 401 登录鉴权故障，探索知识图谱混合架构。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-12712349ddac15465e60572918ef40de.png" width="720" height="405" class="img_ev3q"></p>
<p>Mid-May 2026 was supposed to be a calm interval after wrapping up our 2nd fine-tuning (<code>fine_tuning_2</code>). Instead, May 17th hit our team with a relentless double challenge: condensing hundreds of raw inference metrics into an automated technical report, while simultaneously battling a cryptic <code>HTTP 401 Unauthorized</code> cascading error across our newly integrated <strong>FastAPI</strong> and <strong>PostgreSQL</strong> backend.</p>
<p>Here is the complete post-mortem of how we automated our reporting and systematically neutralized the backend authentication breakdown.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-autonomous-fine-tuning-evaluation-reporting">1. Autonomous Fine-Tuning Evaluation Reporting<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19#1-autonomous-fine-tuning-evaluation-reporting" class="hash-link" aria-label="1. Autonomous Fine-Tuning Evaluation Reporting的直接链接" title="1. Autonomous Fine-Tuning Evaluation Reporting的直接链接" translate="no">​</a></h3>
<p>Analyzing the 2nd fine-tuning results required processing hundreds of inference samples across multiple checkpoint variants (comparing Alpha values: 16, 32, and 64). Doing this manually was mathematically unfeasible given our capstone deadlines.</p>
<p>We engineered a structured prompt document (<code>report_prompt.md</code>) that guided <strong>Claude Code</strong> to analyze raw JSON evaluation matrices and automatically synthesize an executive technical report:</p>
<div class="language-markdown codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-markdown codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token title important punctuation" style="color:#393A34">#</span><span class="token title important"> Excerpt from report_prompt.md</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">1.</span><span class="token plain"> Parse evaluation metrics (BERTScore, ROUGE-L, Perplexity) from /eval/results_alpha_*.json.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">2.</span><span class="token plain"> Group model variants by empathy score, conversational naturalness, and safety compliance.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">3.</span><span class="token plain"> Highlight critical failure cases (e.g., hyper-formal robotic apologies vs conversational warmth).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token list punctuation" style="color:#393A34">4.</span><span class="token plain"> Output the synthesis directly into a publication-ready Word document structure.</span><br></div></code></pre></div></div>
<p>The AI agent generated <code>fine_tuning_2_report.docx</code> in under 3 minutes, complete with comparative analysis tables and qualitative excerpt breakdowns.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-the-nightmare-of-http-401-unauthorized-in-postgresql">2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19#2-the-nightmare-of-http-401-unauthorized-in-postgresql" class="hash-link" aria-label="2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL的直接链接" title="2. The Nightmare of HTTP 401 Unauthorized in PostgreSQL的直接链接" translate="no">​</a></h3>
<p>With reporting streamlined, we restored our staging database dump (<code>backup.sql</code>) into a local PostgreSQL instance to test end-to-end user session persistence. Although the database restored with zero SQL syntax errors, our Next.js frontend crashed into an inescapable login loop:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">INFO: 127.0.0.1:54322 - "POST /auth/login HTTP/1.1" 401 Unauthorized</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DETAIL: {"error": "Invalid credentials or malformed signature"}</span><br></div></code></pre></div></div>
<p>Even with verified test credentials (<code>test@maeumjigi.kr</code> / <code>Password123!</code>), the FastAPI authentication service persistently rejected every login handshake.</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sequenceDiagram</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    autonumber</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    actor Client as Next.js Web Client</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant API as FastAPI Gateway (/auth/login)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant DB as PostgreSQL Database</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    participant Auth as Passlib Bcrypt Verifier</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Client-&gt;&gt;API: POST /auth/login {email, password}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API-&gt;&gt;DB: SELECT id, password_hash FROM users WHERE email=...</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    DB--&gt;&gt;API: Returns $2a$12$... hash string</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API-&gt;&gt;Auth: bcrypt.verify("Password123!", db_hash)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Note over Auth: Trap 1: Salt algorithm mismatch ($2a$ vs $2b$)!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Auth--&gt;&gt;API: Verification Failed (False)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    API--&gt;&gt;Client: 401 Unauthorized</span><br></div></code></pre></div></div>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-root-cause-investigation--debug_loginpy">3. Root Cause Investigation &amp; debug_login.py<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19#3-root-cause-investigation--debug_loginpy" class="hash-link" aria-label="3. Root Cause Investigation &amp; debug_login.py的直接链接" title="3. Root Cause Investigation &amp; debug_login.py的直接链接" translate="no">​</a></h3>
<p>To isolate whether the breakdown stemmed from frontend CORS headers, database corruption, or JWT secret misconfigurations, we wrote a standalone diagnostic script: <code>debug_login.py</code>.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Standalone authentication diagnostic utility: debug_login.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> sys</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> passlib</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">context </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> CryptContext</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> sqlalchemy </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> create_engine</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> text</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DATABASE_URL </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"postgresql://postgres:secret@localhost:5432/maeumjigi"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pwd_context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CryptContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">schemes</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"bcrypt"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> deprecated</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"auto"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">diagnose_user_auth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> raw_pass</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    engine </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> create_engine</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">DATABASE_URL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> engine</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">connect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> conn</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> conn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">execute</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"SELECT password_hash FROM users WHERE email = :e"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"e"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchone</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"[FAIL] User not found in database!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        stored_hash </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"[INFO] Retrieved hash: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">stored_hash</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation format-spec">15]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">..."</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Verify hash algorithm version compatibility</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        is_valid </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pwd_context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">verify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">raw_pass</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> stored_hash</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"[RESULT] Password Match Status: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">is_valid</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    diagnose_user_auth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"test@maeumjigi.kr"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Password123!"</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Running this diagnostic exposed three compounding bugs:</p>
<table><thead><tr><th style="text-align:left">Failure Vector</th><th style="text-align:left">Technical Root Cause</th><th style="text-align:left">Resolution Applied</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Bcrypt Salt Version</strong></td><td style="text-align:left">Database dump contained <code>$2a$</code> hashes from older PHP/Node seeds; modern <code>passlib</code> expected <code>$2b$</code>.</td><td style="text-align:left">Configured <code>pwd_context = CryptContext(schemes=["bcrypt"], bcrypt__ident="2b", deprecated="auto")</code>.</td></tr><tr><td style="text-align:left"><strong>JWT Secret Encoding</strong></td><td style="text-align:left">Secret string was loaded via <code>.env</code> with trailing newline `</td><td style="text-align:left"></td></tr></tbody></table>
<p><code>, breaking HMAC-SHA256 signature checks. | Sanitized key ingestion: </code>SECRET_KEY = os.getenv("JWT_SECRET").strip().encode('utf-8')<code>. | | **CORS Preflight** | Chrome browser blocked </code>Authorization: Bearer<code>headers during OPTIONS preflight checks. | Explicitly registered</code>CORSMiddleware<code>with</code>allow_headers=["<em>"]<code>and</code>expose_headers=["</em>"]`. |</p>
<p>Within 45 minutes of targeted refactoring, login latency dropped to 42ms with a 100% success rate!</p>
<p><img decoding="async" loading="lazy" alt="DB Troubleshooting" src="https://globaldevlog.com/zh-Hans/assets/images/02_db-1d0bb76370f8508377e9472ff5cb15c8.png" width="1373" height="5225" class="img_ev3q"></p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-architectural-expansion-exploring-hybrid-knowledge-graphs">4. Architectural Expansion: Exploring Hybrid Knowledge Graphs<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19#4-architectural-expansion-exploring-hybrid-knowledge-graphs" class="hash-link" aria-label="4. Architectural Expansion: Exploring Hybrid Knowledge Graphs的直接链接" title="4. Architectural Expansion: Exploring Hybrid Knowledge Graphs的直接链接" translate="no">​</a></h3>
<p>Resolving backend stability opened room for deeper architectural exploration. During clinical evaluation of our 2nd fine-tuned model, we noticed an intrinsic weakness shared by all parametric LLMs: <strong>factual hallucinations during multi-hop crisis reasoning</strong>.</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart TD</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph User_Query ["User Input: Complex Crisis State"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U["I stopped taking my SSRI antidepressants 3 days ago and feel severe tremors."]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Hybrid_Architecture ["Hybrid Symbolic-Neural Architecture"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U --&gt; KG_Extractor[Clinical Entity &amp; Relation Extractor]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        KG_Extractor --&gt;|Cypher Query| KG[(Neo4j Medical Knowledge Graph)]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        KG --&gt;|Verified Triples: SSRI -&gt; Discontinuation Syndrome| Context_Injector[Clinical Context Injector]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        U --&gt; Direct_Prompt[Empathetic System Prompt]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Direct_Prompt &amp; Context_Injector --&gt; FineTuned_LLM[Fine-Tuned Qwen / Llama 3.1]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Verified_Output ["Safety-Compliant Response"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        FineTuned_LLM --&gt; Output["Empathetic validation + Immediate medical warning regarding SSRI discontinuation syndrome."]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div></code></pre></div></div>
<p>By drafting <code>chatbot_design_prompt.md</code>, we laid the theoretical foundation for blending parametric LLM conversational empathy with deterministic Knowledge Graph validation.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-day-19-key-engineering-takeaways">5. Day 19 Key Engineering Takeaways<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-maeumjigi-19#5-day-19-key-engineering-takeaways" class="hash-link" aria-label="5. Day 19 Key Engineering Takeaways的直接链接" title="5. Day 19 Key Engineering Takeaways的直接链接" translate="no">​</a></h3>
<ol>
<li class=""><strong>Automated Documentation</strong>: Reduced model benchmarking documentation turnaround from 6 hours to under 3 minutes using structured prompts and AI agents.</li>
<li class=""><strong>Hardened Authentication</strong>: Diagnosed and permanently resolved bcrypt salt mismatches and CORS header traps across PostgreSQL and FastAPI.</li>
<li class=""><strong>Hybrid AI Vision</strong>: Established the conceptual design for integrating symbolic Knowledge Graphs to eliminate clinical hallucinations in mental health counseling.</li>
</ol>
<p>In Dev Log #20, we explore the completion of our 3rd fine-tuning cycle and the rollout of live community board features!</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="devlog" term="devlog"/>
        <category label="automated-reporting" term="automated-reporting"/>
        <category label="fastapi" term="fastapi"/>
        <category label="postgresql" term="postgresql"/>
        <category label="authentication" term="authentication"/>
        <category label="troubleshooting" term="troubleshooting"/>
        <category label="knowledge-graph" term="knowledge-graph"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots 机器人] Q-Learning 自主导航：648 离散状态空间划分与 TCP Socket IPC 架构设计 (#1)]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[为 Webots 移动机器人自主导航与表格化 Q-Learning 设计 648 离散状态空间量化映射器与行分隔 JSON TCP Socket 进程间通信架构。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Webots 仿真环境与自主移动小车" src="https://globaldevlog.com/zh-Hans/assets/images/01_webots_arena-b49bd821fb90ed1d7e619add024bb5e7.png" width="1721" height="1033" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>核心要点 (Key Takeaways)</strong></p>
<ul>
<li class=""><strong>问题痛点</strong>：连续传感器数值（距离、朝向角）会导致表格型强化学习遭遇维数灾难（State-Space Explosion），同时 Python 强化学习智能体与 Webots 仿真内核之间需要维持微秒级、低延迟的物理同步。</li>
<li class=""><strong>解决方案</strong>：设计了基于几何学空间划分的 5 维连续传感器量化器（<code>discretizer.py</code>），生成 3 × 8 × 3 × 3 × 3 = 648 维离散状态；搭建了基于 TCP 5000 端口的极轻量级行分隔 JSON 套接字通信流水线。</li>
<li class=""><strong>落地成效</strong>：实现了 O(1) 复杂度的超高速 Q-Table 寻址更新，确保了仿真器与 Python 端 32ms 物理仿真步长的严格无阻塞同步。</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-项目研发背景为何选择-webots-与经典-q-learning">1. 项目研发背景：为何选择 Webots 与经典 Q-Learning？<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#1-%E9%A1%B9%E7%9B%AE%E7%A0%94%E5%8F%91%E8%83%8C%E6%99%AF%E4%B8%BA%E4%BD%95%E9%80%89%E6%8B%A9-webots-%E4%B8%8E%E7%BB%8F%E5%85%B8-q-learning" class="hash-link" aria-label="1. 项目研发背景：为何选择 Webots 与经典 Q-Learning？的直接链接" title="1. 项目研发背景：为何选择 Webots 与经典 Q-Learning？的直接链接" translate="no">​</a></h2>
<p>让差速驱动机器人在未知的室内场馆中自主导航避障并抵达目标点，是移动机器人学的经典基石课题。尽管深度强化学习（DQN, PPO）在处理高维图像方面表现亮眼，但其庞大的参数量在边缘微控制器上推断延迟高，且缺乏数学上的完全可解释性。</p>
<p>在本专栏项目中，我们在 <strong>Webots 3D 物理仿真引擎</strong> 中应用经典的 <strong>表格型 Q-Learning (Off-policy TD(0))</strong>，旨在在 1.2m × 1.2m 的室内竞技场内训练出一套数学上完全可验证的最优避障与导航策略。</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-系统架构设计webots-控制器--python-强化学习引擎">2. 系统架构设计：Webots 控制器 ↔ Python 强化学习引擎<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#2-%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1webots-%E6%8E%A7%E5%88%B6%E5%99%A8--python-%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0%E5%BC%95%E6%93%8E" class="hash-link" aria-label="2. 系统架构设计：Webots 控制器 ↔ Python 强化学习引擎的直接链接" title="2. 系统架构设计：Webots 控制器 ↔ Python 强化学习引擎的直接链接" translate="no">​</a></h2>
<p>系统解耦为清晰的两大层级：<strong>Webots 仿真端（服务端）</strong> 与 <strong>Python 算法端（客户端）</strong>：</p>
<div class="language-mermaid codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-mermaid codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">flowchart LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Webots["Webots 3D 仿真器 (localhost:5000)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Robot["Supervisor 机器人&lt;br&gt;(超声波传感器 + 驱动轮)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        HeadScan["摇头扫描机制&lt;br&gt;(左右 1.1 rad 舵机摆动)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Server["TCP Socket 服务端&lt;br&gt;(行分隔 JSON 协议)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Robot -→ HeadScan -→ Server</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    subgraph Python["Python 强化学习引擎 (客户端)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Env["WebotsEnv (类似 gym 接口)&lt;br&gt;reset() / step()"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["离散化映射器&lt;br&gt;(连续传感器 → 0~647 Bin)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Agent["QLearningAgent&lt;br&gt;(Q-Table 形状 648x4)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Env -→ Disc -→ Agent</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Server &lt;-→|"请求: {type: step, action: 0..3}&lt;br&gt;响应: {state: [...], reward, done}"| Env</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Webots 控制器 (<code>RL_controller.py</code>)</strong>：负责执行具体的动作指令 480ms（对应 15 个仿真步长 × 32ms），并驱动机载超声波传感器左右摆动 1.1 rad（约 63°）以消除盲区。</li>
<li class=""><strong>Socket 协议</strong>：采用极度轻量的行分隔 JSON 格式进行 TCP 本地环回通信：<!-- -->
<ul>
<li class="">发送动作请求：<code>{"type": "step", "action": 0}</code> (0: 前进, 1: 后退, 2: 左转, 3: 右转)</li>
<li class="">接收环境反馈：<code>{"state": [0.45, 0.12, 0.35, 0.28, 0.40], "reward": 0.05, "done": false, "info": {...}}</code></li>
</ul>
</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-连续传感器读数的-648-维离散状态量化">3. 连续传感器读数的 648 维离散状态量化<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#3-%E8%BF%9E%E7%BB%AD%E4%BC%A0%E6%84%9F%E5%99%A8%E8%AF%BB%E6%95%B0%E7%9A%84-648-%E7%BB%B4%E7%A6%BB%E6%95%A3%E7%8A%B6%E6%80%81%E9%87%8F%E5%8C%96" class="hash-link" aria-label="3. 连续传感器读数的 648 维离散状态量化的直接链接" title="3. 连续传感器读数的 648 维离散状态量化的直接链接" translate="no">​</a></h2>
<p>为了应用表格型强化学习，连续浮点读数必须被映射至有限离散状态空间。根据运动学关键特征，我们选取了 5 个核心状态变量：</p>
<table><thead><tr><th style="text-align:left">传感器特征</th><th style="text-align:center">量化区间数 (Bins)</th><th style="text-align:left">划分物理阈值</th><th style="text-align:left">语义解释</th></tr></thead><tbody><tr><td style="text-align:left"><strong><code>goal_distance</code></strong> (目标距离)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.6m, 1.2m]</code></td><td style="text-align:left">近距离 (Near), 中距离 (Mid), 远距离 (Far)</td></tr><tr><td style="text-align:left"><strong><code>goal_heading</code></strong> (目标夹角)</td><td style="text-align:center"><strong>8</strong></td><td style="text-align:left">[-π, π] (8个扇区, 每区45°)</td><td style="text-align:left">正前, 右前, 正右, 右后, 正后, 左后, 正左, 左前</td></tr><tr><td style="text-align:left"><strong><code>front_distance</code></strong> (前向测距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">碰撞危险 (Danger), 警示 (Caution), 安全 (Safe)</td></tr><tr><td style="text-align:left"><strong><code>left_distance</code></strong> (左向测距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">危险, 警示, 安全</td></tr><tr><td style="text-align:left"><strong><code>right_distance</code></strong> (右向测距)</td><td style="text-align:center"><strong>3</strong></td><td style="text-align:left"><code>[0.15m, 0.30m]</code></td><td style="text-align:left">危险, 警示, 安全</td></tr></tbody></table>
<p>总状态数规模：
<strong>N_states = 3 × 8 × 3 × 3 × 3 = 648</strong></p>
<p>由此构建的 Q-Table 仅包含 <strong>648 × 4 = 2,592 个浮点数条目（内存仅占约 2.5 KB）</strong>，能在单片机微秒级寻址，彻底摆脱对 GPU 的依赖。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-状态离散化核心代码-discretizerpy">💻 状态离散化核心代码 (<code>discretizer.py</code>)<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#-%E7%8A%B6%E6%80%81%E7%A6%BB%E6%95%A3%E5%8C%96%E6%A0%B8%E5%BF%83%E4%BB%A3%E7%A0%81-discretizerpy" class="hash-link" aria-label="-状态离散化核心代码-discretizerpy的直接链接" title="-状态离散化核心代码-discretizerpy的直接链接" translate="no">​</a></h3>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 5个维度的量化分区基数</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">N_GOAL_D</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_GOAL_H</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_FRONT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_LEFT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> N_RIGHT </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">N_STATES </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> N_GOAL_D </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_GOAL_H </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_FRONT </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_LEFT </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_RIGHT  </span><span class="token comment" style="color:#999988;font-style:italic"># 648</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_HEADING_BINS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">linspace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">9</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_NEAR_BINS    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.15</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.30</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_DIST_BINS    </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.6</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">discretize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:#e3116c">"""将5元组连续传感器浮点读数映射至 [0, 647] 之间的1维整数索引。"""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gd </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"goal_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _DIST_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"goal_heading"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _HEADING_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    df </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"front_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    dl </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"left_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    dr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">digitize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state_dict</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"right_distance"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> _NEAR_BINS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># 计算平铺的一维索引 (扁平化)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    index </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">gd </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_GOAL_H </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> gh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_FRONT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> df</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_LEFT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> dl</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> N_RIGHT </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> dr</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">index</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-舵机摇头扫描与物理碰撞判定逻辑">4. 舵机摇头扫描与物理碰撞判定逻辑<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#4-%E8%88%B5%E6%9C%BA%E6%91%87%E5%A4%B4%E6%89%AB%E6%8F%8F%E4%B8%8E%E7%89%A9%E7%90%86%E7%A2%B0%E6%92%9E%E5%88%A4%E5%AE%9A%E9%80%BB%E8%BE%91" class="hash-link" aria-label="4. 舵机摇头扫描与物理碰撞判定逻辑的直接链接" title="4. 舵机摇头扫描与物理碰撞判定逻辑的直接链接" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="系统底层架构与传感器物理布局" src="https://globaldevlog.com/zh-Hans/assets/images/02_architecture-83a0cdfb8297ac248797b32bbc7fd69b.png" width="2000" height="900" class="img_ev3q"></p>
<p>固定的前置单向传感器存在致命的侧方盲区。我们实现了主动式的 <strong>摇头扫描机制 (Head-Scan)</strong>：</p>
<ol>
<li class=""><strong>执行动作步</strong>：轮子以 3.0 rad/s 驱动 480ms。</li>
<li class=""><strong>头部旋转测距</strong>：舵机向左摆动 +1.1 rad 采集左侧净空，随后向右打到 -1.1 rad 采集右侧净空。</li>
<li class=""><strong>回正复位</strong>：回位至 0 rad 更新正前方障碍物距离。</li>
<li class=""><strong>终止状态条件</strong>：<!-- -->
<ul>
<li class=""><strong>抵达目标 (Goal Reached)</strong>：与目标点欧氏距离 ≤ 0.10m → 成功 (<code>done = True</code>, 奖励 $+1.0$)</li>
<li class=""><strong>碰撞发生 (Collision)</strong>：任一方向实测距离 ≤ 0.08m → 失败 (<code>done = True</code>, 惩罚 $-1.0$)</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-下篇预告">5. 下篇预告<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-17-rl-qlearning-1#5-%E4%B8%8B%E7%AF%87%E9%A2%84%E5%91%8A" class="hash-link" aria-label="5. 下篇预告的直接链接" title="5. 下篇预告的直接链接" translate="no">​</a></h2>
<p>环境框架、Socket IPC 通信与 648 维状态离散化体系已经就位。</p>
<p>在第 2 篇中，我们将深入解析 <strong>表格型 Q-Learning 更新公式的数学推导、ε-贪婪指数衰减策略，以及攻克稀疏奖励难题的稠密奖励函数设计 (Reward Shaping)</strong>！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="reinforcement-learning" term="reinforcement-learning"/>
        <category label="q-learning" term="q-learning"/>
        <category label="webots" term="webots"/>
        <category label="robotics" term="robotics"/>
        <category label="discretization" term="discretization"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi 心语伴侣毕业设计开发日志 #18] 推理生成 Token 扩容与第二轮微调 LoRA Alpha 效果对照]]></title>
        <id>https://globaldevlog.com/zh-Hans/blog/2026-05-13-maeumjigi-18</id>
        <link href="https://globaldevlog.com/zh-Hans/blog/2026-05-13-maeumjigi-18"/>
        <updated>2026-05-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[将推论生成 max_new_tokens 放宽至 384 根除生硬截断，深入对比评估 LoRA alpha 缩放参数（8 vs 16）对心理共情引导的显著影响。]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="封面图" src="https://globaldevlog.com/zh-Hans/assets/images/01_cover-4dcde4cef4209e81d49b5f5306af009d.png" width="1484" height="888" class="img_ev3q"></p>
<p>在彻底修复昨天排查出的别名硬编码缺陷后，5 月 13 日，我们终于重启了第二轮微调大模型真实端到端的推理生成能力基准测试。</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="破除生硬断裂放宽生成-token-长度上限释放深层共情">破除生硬断裂：放宽生成 Token 长度上限释放深层共情<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-13-maeumjigi-18#%E7%A0%B4%E9%99%A4%E7%94%9F%E7%A1%AC%E6%96%AD%E8%A3%82%E6%94%BE%E5%AE%BD%E7%94%9F%E6%88%90-token-%E9%95%BF%E5%BA%A6%E4%B8%8A%E9%99%90%E9%87%8A%E6%94%BE%E6%B7%B1%E5%B1%82%E5%85%B1%E6%83%85" class="hash-link" aria-label="破除生硬断裂：放宽生成 Token 长度上限释放深层共情的直接链接" title="破除生硬断裂：放宽生成 Token 长度上限释放深层共情的直接链接" translate="no">​</a></h3>
<p>逐句审读推理生成的应答文本时，我们发现 <code>Llama33-70B_a8</code> 模型的输出依然呈现出一种微妙的干瘪与机械紧缩感：</p>
<blockquote>
<p>“这是 Llama33-70B_a8 输出的典型答复。明显感觉 max_seq_length 与 max_new_tokens 设得太紧，严重束缚了模型表达。结合当前物理显存富余量，极限能上调至多少？”</p>
</blockquote>
<p>结合当时约 39.8GB 的空闲 VRAM，我们果断将最大生成 Token 预算 (<code>max_new_tokens</code>) 调宽至 384。调整带来的生成质变立竿见影：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">[修改前] “产生这种消极念头一定让您备受折磨。及时向专业咨询师求助非常重要。”</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[修改后] “产生这样的念头一定让您独自承受着巨大的痛苦。但请相信，这绝不代表您内心软弱，而是身体在向您发出信号——说明您此刻背负的压力已经严重超载了。我由衷建议您在专业咨询师的陪同下，试着把这些郁结的情绪一点一点倾诉梳理出来。”</span><br></div></code></pre></div></div>
<p>仅仅是松绑了生成长度配额，模型便能从容保持长程逻辑一致性，吐露出层层递进、饱含人情温度与心理抚慰力量的真挚共情长句。此前模型之所以三缄其口，纯粹是被狭窄的生成长度强行截断了思路。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lora-超参深度剖析alpha-缩放因子对比a8-vs-a16">LoRA 超参深度剖析：Alpha 缩放因子对比（a=8 vs. a=16）<a href="https://globaldevlog.com/zh-Hans/blog/2026-05-13-maeumjigi-18#lora-%E8%B6%85%E5%8F%82%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90alpha-%E7%BC%A9%E6%94%BE%E5%9B%A0%E5%AD%90%E5%AF%B9%E6%AF%94a8-vs-a16" class="hash-link" aria-label="LoRA 超参深度剖析：Alpha 缩放因子对比（a=8 vs. a=16）的直接链接" title="LoRA 超参深度剖析：Alpha 缩放因子对比（a=8 vs. a=16）的直接链接" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="超参数对比图表" src="https://globaldevlog.com/zh-Hans/assets/images/02_graph-feb5d1991f44dd7f378232f6b3515752.png" width="2053" height="1460" class="img_ev3q"></p>
<p>第二轮微调的核心科研目的——验证 LoRA 缩放因子 <code>alpha</code> 对领域适应能力的影响也迎来了定量结论。
通过深入挖掘 <code>fine_tuning_2</code> 目录下的收敛日志，我们绘制出了 <code>05_alpha_comparison_daily.png</code> 等对比折线。在固定 <code>rank=16</code> 的前提下，清晰揭示了 $\alpha=8$（欠拟合防漂移）与 $\alpha=16$（强化领域风格吸收）在训练后期的损失震荡差异与主观咨询质量的微妙权衡。</p>
<p>在被赋予合理的超参数与充裕的生成空间后，各大模型终于蜕变出了我们最初构想中那个体贴细腻、专业包容的“心语伴侣”。
中期答辩材料的最终拼图，在此刻宣告圆满合拢！</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="开发日志" term="开发日志"/>
        <category label="微调" term="微调"/>
        <category label="推理" term="推理"/>
        <category label="max_new_tokens" term="max_new_tokens"/>
        <category label="共情能力" term="共情能力"/>
        <category label="毕业设计" term="毕业设计"/>
    </entry>
</feed>