<?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/blog</id>
    <title>Global Dev Blog Blog</title>
    <updated>2026-10-18T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://globaldevlog.com/blog"/>
    <subtitle>Global Dev Blog Blog</subtitle>
    <icon>https://globaldevlog.com/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[From 45 npm Vulnerabilities to Zero-Risk: Why You Shouldn't Blindly Run npm audit fix --force on Static Sites]]></title>
        <id>https://globaldevlog.com/blog/static-site-security-npm-audit-truth</id>
        <link href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth"/>
        <updated>2026-10-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A deep dive into static site threat modeling, dissecting why 45 npm vulnerabilities in build tools pose zero runtime risk on GitHub Pages, and why running npm audit fix --force breaks production.]]></summary>
        <content type="html"><![CDATA[<p>Every modern frontend developer has experienced this sudden spike of adrenaline: you run <code>npm audit</code> on your clean documentation or blog repository, and the terminal screams back in alarming red text:</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>Seeing words like <strong>Critical</strong>, <strong>Remote Code Execution (RCE)</strong>, and <strong>Prototype Pollution</strong> triggers an immediate instinct: type <code>npm audit fix --force</code> to make the scary red warnings go away.</p>
<p>However, in the world of <strong>Static Site Generators (SSG)</strong> like Docusaurus, Next.js, or Hugo deployed to GitHub Pages or AWS S3, doing so is often an engineering mistake that breaks production builds without improving your actual security posture.</p>
<p>In this article, we dissect the <strong>static site threat model</strong>, explain why bundler vulnerabilities differ fundamentally from server vulnerabilities, and share a production-tested <strong>4-Layer Security Hardening Blueprint</strong> to protect what actually matters: your secrets, client privacy, and repository integrity.</p>
<!-- -->
<hr>
<p><img decoding="async" loading="lazy" alt="Threat Model Comparison: Dynamic Server vs Static Site" src="https://globaldevlog.com/assets/images/01_threat_model-4770df6c58430b3bba205124a37d91ab.png" width="2000" height="1000" class="img_ev3q"></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-executive-summary-the-core-engineering-trade-off">1. Executive Summary: The Core Engineering Trade-off<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#1-executive-summary-the-core-engineering-trade-off" class="hash-link" aria-label="Direct link to 1. Executive Summary: The Core Engineering Trade-off" title="Direct link to 1. Executive Summary: The Core Engineering Trade-off" translate="no">​</a></h2>
<ol>
<li class=""><strong>Build-Time Tools != Production Runtime</strong>: Packages flagged in <code>npm audit</code> (such as <code>tinypool</code>, <code>serialize-javascript</code>, <code>postcss-selector-parser</code>) are node worker and bundler dependencies executed locally on your machine during <code>npm run build</code>.</li>
<li class=""><strong>Zero Live Server on CDN</strong>: Once compiled, GitHub Pages serves static, immutable HTML, CSS, and client-side JavaScript. There is <strong>no running Node.js daemon</strong>, no backend port listening, and no database connection in production.</li>
<li class=""><strong>The Danger of <code>--force</code></strong>: Running <code>npm audit fix --force</code> blindly downgrades core frameworks (e.g., rolling Docusaurus back to older releases), breaking React 19 compatibility, MDX v3 pipelines, and future flag architectures.</li>
<li class=""><strong>Where Real Static Risks Lie</strong>: The genuine security risks of static blogs are <strong>credential/API key leakage in public Git history</strong>, <strong>PII exposure in datasets</strong>, and <strong>client-side cross-origin leakage</strong>.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-anatomy-of-the-45-vulnerabilities-build-vs-runtime">2. Anatomy of the 45 Vulnerabilities: Build vs Runtime<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#2-anatomy-of-the-45-vulnerabilities-build-vs-runtime" class="hash-link" aria-label="Direct link to 2. Anatomy of the 45 Vulnerabilities: Build vs Runtime" title="Direct link to 2. Anatomy of the 45 Vulnerabilities: Build vs Runtime" translate="no">​</a></h2>
<p>Let's examine the actual CVEs reported in our audit log:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-tinypool-prototype-pollution-ghsa-5gmw-xhrv-c9v3---critical">A. Tinypool Prototype Pollution (<code>GHSA-5gmw-xhrv-c9v3</code> - Critical)<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#a-tinypool-prototype-pollution-ghsa-5gmw-xhrv-c9v3---critical" class="hash-link" aria-label="Direct link to a-tinypool-prototype-pollution-ghsa-5gmw-xhrv-c9v3---critical" title="Direct link to a-tinypool-prototype-pollution-ghsa-5gmw-xhrv-c9v3---critical" translate="no">​</a></h3>
<ul>
<li class=""><strong>Vulnerability</strong>: If untrusted user input is passed into the worker initialization options of <code>tinypool</code>, an attacker can pollute <code>Object.prototype</code>.</li>
<li class=""><strong>Reality in SSG</strong>: <code>tinypool</code> is used internally by <code>@docusaurus/faster</code> to parallelize bundling tasks across CPU cores during build time. The input parameters come solely from your own <code>docusaurus.config.ts</code>. No external visitor on your website can send HTTP payloads to your local CPU worker pool.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="b-serialize-javascript-rce-ghsa-5c6j-r48x-rmvq---high">B. Serialize-JavaScript RCE (<code>GHSA-5c6j-r48x-rmvq</code> - High)<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#b-serialize-javascript-rce-ghsa-5c6j-r48x-rmvq---high" class="hash-link" aria-label="Direct link to b-serialize-javascript-rce-ghsa-5c6j-r48x-rmvq---high" title="Direct link to b-serialize-javascript-rce-ghsa-5c6j-r48x-rmvq---high" translate="no">​</a></h3>
<ul>
<li class=""><strong>Vulnerability</strong>: Remote Code Execution via crafted RegExp flags or serialized dates.</li>
<li class=""><strong>Reality in SSG</strong>: Webpack uses this to serialize internal compilation chunks into static <code>.js</code> bundles. Once written to the <code>build/</code> directory, JavaScript files are passive assets served over HTTPS. No server-side deserialization takes place.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="c-postcss-selector-parser-cpu-exhaustion-ghsa-rj75-hqrm-r3gf---moderate">C. PostCSS Selector Parser CPU Exhaustion (<code>GHSA-rj75-hqrm-r3gf</code> - Moderate)<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#c-postcss-selector-parser-cpu-exhaustion-ghsa-rj75-hqrm-r3gf---moderate" class="hash-link" aria-label="Direct link to c-postcss-selector-parser-cpu-exhaustion-ghsa-rj75-hqrm-r3gf---moderate" title="Direct link to c-postcss-selector-parser-cpu-exhaustion-ghsa-rj75-hqrm-r3gf---moderate" translate="no">​</a></h3>
<ul>
<li class=""><strong>Vulnerability</strong>: Quadratic complexity in flat selector parsing allows Denial of Service (DoS) via maliciously crafted CSS strings.</li>
<li class=""><strong>Reality in SSG</strong>: The CSS files being parsed are your own theme stylesheets (<code>custom.css</code>). Unless an attacker has write access to your Git repository, your build server will never parse untrusted external CSS.</li>
</ul>
<p>Running <code>npm audit fix --force</code> in this situation attempts to downgrade <code>@docusaurus/core</code> from <code>3.10.2</code> to <code>3.7.0</code>. This breaks Docusaurus v4 future flags and React 19 rendering, introducing breaking bugs into your codebase to patch an exploit that cannot physically occur in your architecture.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-what-actually-matters-the-4-layer-static-security-blueprint">3. What Actually Matters: The 4-Layer Static Security Blueprint<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#3-what-actually-matters-the-4-layer-static-security-blueprint" class="hash-link" aria-label="Direct link to 3. What Actually Matters: The 4-Layer Static Security Blueprint" title="Direct link to 3. What Actually Matters: The 4-Layer Static Security Blueprint" translate="no">​</a></h2>
<p>If npm bundler vulnerabilities are low risk, where should static site engineers invest their security budget? Here is the multi-layered defense pipeline implemented in this blog.</p>
<p><img decoding="async" loading="lazy" alt="4-Layer Static Blog Security Hardening Pipeline" src="https://globaldevlog.com/assets/images/02_security_pipeline-120240a37282a9b3b578a5dc4f6231c0.png" width="2000" height="900" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="layer-1-automated-pre-commit-secret-scanning">Layer 1: Automated Pre-Commit Secret Scanning<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#layer-1-automated-pre-commit-secret-scanning" class="hash-link" aria-label="Direct link to Layer 1: Automated Pre-Commit Secret Scanning" title="Direct link to Layer 1: Automated Pre-Commit Secret Scanning" translate="no">​</a></h3>
<p>The single greatest catastrophic risk for public technical blogs is accidentally committing an OpenAI key (<code>sk-...</code>), a GitHub Personal Access Token (<code>ghp_...</code>), or an AWS access credential (<code>AKIA...</code>).</p>
<p>We created an automated audit script (<code>tools/audit_security.py</code>) that utilizes Git's object database to scan every tracked file against high-entropy regex patterns:</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>Running this audit script before every release ensures zero hardcoded tokens ever reach the remote repository.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="layer-2-personally-identifiable-information-pii-scrubbing">Layer 2: Personally Identifiable Information (PII) Scrubbing<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#layer-2-personally-identifiable-information-pii-scrubbing" class="hash-link" aria-label="Direct link to Layer 2: Personally Identifiable Information (PII) Scrubbing" title="Direct link to Layer 2: Personally Identifiable Information (PII) Scrubbing" translate="no">​</a></h3>
<p>When publishing engineering articles on AI datasets (such as our 23-part Maeumjigi clinical counseling series or 5-part ANPR Vision AI series):</p>
<ul>
<li class=""><strong>Synthetic Data First</strong>: All counseling records were synthetically generated via LLM pipelines (<code>synth_gen</code>) rather than using real clinical transcripts, guaranteeing zero medical privacy breach.</li>
<li class=""><strong>Regex PII Audit</strong>: Our scanner automatically validates markdown text against Korean national identity numbers (<code>\d{6}-[1-4]\d{6}</code>) and phone numbers (<code>010-\d{4}-\d{4}</code>).</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="layer-3-hardened-gitignore-configuration">Layer 3: Hardened <code>.gitignore</code> Configuration<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#layer-3-hardened-gitignore-configuration" class="hash-link" aria-label="Direct link to layer-3-hardened-gitignore-configuration" title="Direct link to layer-3-hardened-gitignore-configuration" translate="no">​</a></h3>
<p>A standard <code>.gitignore</code> often ignores <code>.env.local</code> while leaving <code>.env</code> exposed. We hardened <code>.gitignore</code> to comprehensively block sensitive files, private certificates, and raw analytics dumps:</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"># Environment &amp; Secret Protection</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"># Analytics &amp; CSV dumps</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"># Archived template files</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="layer-4-client-side-privacy--iframe-sandboxing">Layer 4: Client-Side Privacy &amp; Iframe Sandboxing<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#layer-4-client-side-privacy--iframe-sandboxing" class="hash-link" aria-label="Direct link to Layer 4: Client-Side Privacy &amp; Iframe Sandboxing" title="Direct link to Layer 4: Client-Side Privacy &amp; Iframe Sandboxing" translate="no">​</a></h3>
<p>Even without a backend server, static sites must respect user privacy and prevent data leakage:</p>
<ol>
<li class="">
<p><strong>Strict Referrer Policy</strong>: In <code>docusaurus.config.ts</code>, we inject a strict referrer header into <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>This ensures that when a reader clicks an external documentation link, your blog's specific full URL parameters are truncated, preventing path leakage.</p>
</li>
<li class="">
<p><strong>Giscus Discussion Sandboxing</strong>: We host comments via GitHub Discussions. In <code>BlogPostItem/index.tsx</code>, Giscus is configured with:</p>
<ul>
<li class=""><code>emitMetadata="0"</code>: Prevents post-message iframe broadcasts from exposing discussion details to third-party browser scripts.</li>
<li class=""><code>loading="lazy"</code>: Prevents iframe resource execution until the reader scrolls to the bottom of the article.</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-key-takeaways-for-developers">4. Key Takeaways for Developers<a href="https://globaldevlog.com/blog/static-site-security-npm-audit-truth#4-key-takeaways-for-developers" class="hash-link" aria-label="Direct link to 4. Key Takeaways for Developers" title="Direct link to 4. Key Takeaways for Developers" translate="no">​</a></h2>
<ul>
<li class=""><strong>Do Not Panic at <code>npm audit</code></strong>: Always distinguish between <strong>Build-Time Bundler Dependencies</strong> and <strong>Production Runtime Dependencies</strong>.</li>
<li class=""><strong>Beware of <code>--force</code></strong>: Automated downgrades often introduce real, breaking application bugs while solving theoretical vulnerabilities that cannot be triggered on a static CDN.</li>
<li class=""><strong>Focus on the Real Attack Surface</strong>: Automate secret detection, scrub sensitive data from datasets, enforce strict client-side referrer policies, and protect your Git history.</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] Korean ANPR OCR Engine, Regex Normalization & Temporal Majority Voting (v01 to v11 Evolution) (#5)]]></title>
        <id>https://globaldevlog.com/blog/2026-06-29-vision-anpr-5</id>
        <link href="https://globaldevlog.com/blog/2026-06-29-vision-anpr-5"/>
        <updated>2026-06-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Korean license plate syntax validation, EasyOCR tuning, temporal sliding window majority voting, and the complete 11-iteration engineering journey from v01 to v11.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Temporal Majority Voting and Final Confirmation" src="https://globaldevlog.com/assets/images/01_voting_evolution-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Single-frame OCR inevitably suffers from transient misreadings (e.g., <code>64아1511</code>, <code>24가1511</code>) due to vehicle yaw, glare, and vibration.</li>
<li class=""><strong>Solution</strong>: Implemented Korean plate regex validation (<code>^\d{2,3}[가-힣]\d{4}$</code>) coupled with a temporal sliding window majority voting engine across a 7-frame buffer (<code>plate_confirm_threshold=3</code>).</li>
<li class=""><strong>Result</strong>: Advanced through 11 system iterations (v01 to v11), transitioning from initial gibberish (<code>ACCIIXOY-1Z01...</code>) on June 5 to deterministic confirmation of <code>64가1511</code> on June 7 with 99.4% reliability.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-korean-anpr-syntax--easyocr-tuning">1. Korean ANPR Syntax &amp; EasyOCR Tuning<a href="https://globaldevlog.com/blog/2026-06-29-vision-anpr-5#1-korean-anpr-syntax--easyocr-tuning" class="hash-link" aria-label="Direct link to 1. Korean ANPR Syntax &amp; EasyOCR Tuning" title="Direct link to 1. Korean ANPR Syntax &amp; EasyOCR Tuning" translate="no">​</a></h2>
<p>Korean vehicle registration plates follow strict regulatory syntax:</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">Syntax: [2 or 3 Digits] + [1 Hangul Syllable] + [4 Digits]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Example: 64가1511, 123가4567</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">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">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">    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"># Korean License Plate Regex Match</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-engine">2. Temporal Majority Voting Engine<a href="https://globaldevlog.com/blog/2026-06-29-vision-anpr-5#2-temporal-majority-voting-engine" class="hash-link" aria-label="Direct link to 2. Temporal Majority Voting Engine" title="Direct link to 2. Temporal Majority Voting Engine" translate="no">​</a></h2>
<p>To overcome transient single-frame OCR flicker, we maintain a 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 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 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"># Confirm plate if 3 or more votes agree</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-the-11-iteration-evolution-june-5-to-june-7">3. The 11-Iteration Evolution: June 5 to June 7<a href="https://globaldevlog.com/blog/2026-06-29-vision-anpr-5#3-the-11-iteration-evolution-june-5-to-june-7" class="hash-link" aria-label="Direct link to 3. The 11-Iteration Evolution: June 5 to June 7" title="Direct link to 3. The 11-Iteration Evolution: June 5 to June 7" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Final v11 Integrated Pipeline in Action" src="https://globaldevlog.com/assets/images/02_final_system-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<p>Our <code>log.txt</code> preserves the raw chronological milestones:</p>
<table><thead><tr><th style="text-align:left">Timestamp</th><th style="text-align:left">Raw Plate Log</th><th style="text-align:left">Technical Iteration &amp; Key Breakthrough</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>: Unconstrained full-frame OCR. Road textures misinterpreted as text</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 vehicle bounding box integration</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>: Dedicated YOLOv5 plate detector crops plate 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 deep super-resolution resolves Hangul stroke</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>: Multi-frame image stacking cancels sensor noise</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 Final</strong>: Temporal Majority Voting locks deterministic plate confirmation!</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-conclusion--future-horizons">4. Conclusion &amp; Future Horizons<a href="https://globaldevlog.com/blog/2026-06-29-vision-anpr-5#4-conclusion--future-horizons" class="hash-link" aria-label="Direct link to 4. Conclusion &amp; Future Horizons" title="Direct link to 4. Conclusion &amp; Future Horizons" translate="no">​</a></h2>
<p>Across this 5-part series, we designed an end-to-end computer vision system integrating <strong>dynamic lane tracking, polygon IoU intrusion detection, hierarchical object localization, CSRT tracking, EDSR_x4 super-resolution, and temporal voting OCR</strong>. This pipeline provides a proven template for production-grade intelligent transportation systems.</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] Overcoming Blurry Blackbox Artifacts: Deep Super-Resolution (EDSR vs FSRCNN) & Multi-Frame Image Stacking (#4)]]></title>
        <id>https://globaldevlog.com/blog/2026-06-28-vision-anpr-4</id>
        <link href="https://globaldevlog.com/blog/2026-06-28-vision-anpr-4"/>
        <updated>2026-06-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Reconstructing cropped low-resolution license plate patches via EDSR 4x deep super-resolution and temporal multi-frame image stacking.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="EDSR Deep Super-Resolution Results" src="https://globaldevlog.com/assets/images/01_edsr_sr_comparison-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Cropped license plates from dashcam footage often measure under 40x20 pixels with heavy H.264 macroblocking; standard bicubic interpolation fails to resolve character contours.</li>
<li class=""><strong>Solution</strong>: Integrated EDSR_x4 deep residual networks (EDSR_x4.pb) that omit batch normalization to preserve dynamic range, combined with temporal multi-frame image stacking across 3 to 5 consecutive frames.</li>
<li class=""><strong>Result</strong>: Quadrupled plate pixel density and sharpened stroke edges, lifting downstream EasyOCR accuracy on Korean characters from 32% to over 91%.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-resolution-limitations--the-need-for-deep-super-resolution">1. Resolution Limitations &amp; The Need for Deep Super-Resolution<a href="https://globaldevlog.com/blog/2026-06-28-vision-anpr-4#1-resolution-limitations--the-need-for-deep-super-resolution" class="hash-link" aria-label="Direct link to 1. Resolution Limitations &amp; The Need for Deep Super-Resolution" title="Direct link to 1. Resolution Limitations &amp; The Need for Deep Super-Resolution" translate="no">​</a></h2>
<p>Even with precise license plate localization, optical limitations thwart standard OCR:</p>
<ol>
<li class=""><strong>Failure of Bicubic Scaling</strong>: Interpolation algorithms blur edges further without hallucinating high-frequency contours.</li>
<li class=""><strong>H.264 Compression &amp; Sensor Noise</strong>: Dashcam compression creates severe pixelation that distorts alphanumeric strokes.</li>
</ol>
<p>We integrated <strong>EDSR_x4 via the OpenCV DNN Super-Resolution module</strong> to reconstruct high-frequency details:</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">    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">    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">    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-multi-frame-image-stacking-for-noise-reduction">2. Multi-Frame Image Stacking for Noise Reduction<a href="https://globaldevlog.com/blog/2026-06-28-vision-anpr-4#2-multi-frame-image-stacking-for-noise-reduction" class="hash-link" aria-label="Direct link to 2. Multi-Frame Image Stacking for Noise Reduction" title="Direct link to 2. Multi-Frame Image Stacking for Noise Reduction" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Multi-frame Image Stacking Comparison" src="https://globaldevlog.com/assets/images/02_frame_stacking-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></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>This temporal stacking cancels random sensor noise while boosting text-background contrast.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-looking-forward">3. Looking Forward<a href="https://globaldevlog.com/blog/2026-06-28-vision-anpr-4#3-looking-forward" class="hash-link" aria-label="Direct link to 3. Looking Forward" title="Direct link to 3. Looking Forward" translate="no">​</a></h2>
<p>In Post #5, we conclude the series with <strong>Korean ANPR OCR tuning, regex syntactic validation</strong>, and the <strong>temporal majority voting engine</strong>, recounting our engineering evolution from initial gibberish to finalized recognition (<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] Hierarchical Object Detection: YOLOv3 Vehicle ROI, YOLOv5 Plate Detector & CSRT Tracking (#3)]]></title>
        <id>https://globaldevlog.com/blog/2026-06-27-vision-anpr-3</id>
        <link href="https://globaldevlog.com/blog/2026-06-27-vision-anpr-3"/>
        <updated>2026-06-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Two-stage hierarchical localization: broad vehicle detection via YOLOv3, localized license plate inference via YOLOv5, and CSRT correlation filter tracking.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="YOLOv5 Plate Detection Localization" src="https://globaldevlog.com/assets/images/01_plate_localization-e3a163202d7a73e57bdf504aafdd6919.png" width="957" height="766" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Searching for tiny plates (30x15 pixels) across the raw 640x480 frame generates massive false positives from roadside signs and drops inference speed below 10 FPS.</li>
<li class=""><strong>Solution</strong>: Designed a two-stage hierarchical architecture: broad vehicle detection (YOLOv3-tiny) crops the vehicle ROI, within which a fine-tuned YOLOv5 (lp_det.pt, conf=0.6) locates the license plate. Integrated OpenCV CSRT correlation filter tracking with a 15-frame redetection interval.</li>
<li class=""><strong>Result</strong>: Eliminated 98% of background false positives and slashed GPU overhead by 80%, sustaining real-time 50+ FPS continuous tracking.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-pitfalls-of-global-detection--the-hierarchical-solution">1. The Pitfalls of Global Detection &amp; The Hierarchical Solution<a href="https://globaldevlog.com/blog/2026-06-27-vision-anpr-3#1-the-pitfalls-of-global-detection--the-hierarchical-solution" class="hash-link" aria-label="Direct link to 1. The Pitfalls of Global Detection &amp; The Hierarchical Solution" title="Direct link to 1. The Pitfalls of Global Detection &amp; The Hierarchical Solution" translate="no">​</a></h2>
<p>Running plate detectors across the full 640x480 image causes two critical flaws:</p>
<ol>
<li class=""><strong>Background Clutter</strong>: Street advertisements, road billboards, and store signs trigger high false-alarm rates.</li>
<li class=""><strong>Resolution Downsampling Loss</strong>: Resizing 640x480 to typical model inputs (e.g., 640x640) squashes tiny 30-pixel plates into sub-feature extinction.</li>
</ol>
<p>Our two-stage hierarchical pipeline addresses this by constraining the search space:</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">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">    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">    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">        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-real-time-latency-mitigation-csrt-tracker-integration">2. Real-Time Latency Mitigation: CSRT Tracker Integration<a href="https://globaldevlog.com/blog/2026-06-27-vision-anpr-3#2-real-time-latency-mitigation-csrt-tracker-integration" class="hash-link" aria-label="Direct link to 2. Real-Time Latency Mitigation: CSRT Tracker Integration" title="Direct link to 2. Real-Time Latency Mitigation: CSRT Tracker Integration" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Continuous Tracking with OpenCV CSRT" src="https://globaldevlog.com/assets/images/02_csrt_tracking-bea82f7c3eb5c74b33968501166fbe5a.png" width="2139" height="1599" class="img_ev3q"></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">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">    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-looking-forward">3. Looking Forward<a href="https://globaldevlog.com/blog/2026-06-27-vision-anpr-3#3-looking-forward" class="hash-link" aria-label="Direct link to 3. Looking Forward" title="Direct link to 3. Looking Forward" translate="no">​</a></h2>
<p>In Post #4, we explore deep <strong>EDSR_x4 super-resolution</strong> to reconstruct crisp alphanumeric strokes from blurry low-resolution plate crops, combined with <strong>multi-frame image stacking</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] Dynamic Lane Boundary Extraction & Geometric IoU Intrusion Criterion (#2)]]></title>
        <id>https://globaldevlog.com/blog/2026-06-26-vision-anpr-2</id>
        <link href="https://globaldevlog.com/blog/2026-06-26-vision-anpr-2"/>
        <updated>2026-06-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Canny edge filtering, probabilistic Hough transform (HoughLinesP), slope thresholding, and geometric polygon IoU intrusion calculation.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Dynamic Lane Detection Overlay" src="https://globaldevlog.com/assets/images/01_lane_detection-0ce024bbbafb08d7449c40abec7b6dc2.png" width="2149" height="1599" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Dashcam footage contains heavy pavement specular reflection, tar cracks, and road shadows that induce false lane lines; simple coordinate checking cannot reliably catch the exact moment of lane encroachment.</li>
<li class=""><strong>Solution</strong>: Implemented vanishing-point trapezoidal ROI masking, slope thresholding (|slope| &gt; 0.5), and a 7-frame temporal moving average queue. Formulated intrusion as geometric IoU between vehicle tire footprints and lane boundaries.</li>
<li class=""><strong>Result</strong>: Filtered out &gt;95% of road texture noise while capturing vehicle lane crossing events with sub-frame accuracy.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-dynamic-lane-detection-canny--probabilistic-hough-transform">1. Dynamic Lane Detection: Canny &amp; Probabilistic Hough Transform<a href="https://globaldevlog.com/blog/2026-06-26-vision-anpr-2#1-dynamic-lane-detection-canny--probabilistic-hough-transform" class="hash-link" aria-label="Direct link to 1. Dynamic Lane Detection: Canny &amp; Probabilistic Hough Transform" title="Direct link to 1. Dynamic Lane Detection: Canny &amp; Probabilistic Hough Transform" 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 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">    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">    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-slope-filtering--temporal-smoothing">2. Slope Filtering &amp; Temporal Smoothing<a href="https://globaldevlog.com/blog/2026-06-26-vision-anpr-2#2-slope-filtering--temporal-smoothing" class="hash-link" aria-label="Direct link to 2. Slope Filtering &amp; Temporal Smoothing" title="Direct link to 2. Slope Filtering &amp; Temporal Smoothing" translate="no">​</a></h2>
<p>Raw line segments are filtered using slope criteria:</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">Slope m = (y2 - y1) / (x2 - x1)</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Left Lane</strong>: <code>m &lt; -0.5</code></li>
<li class=""><strong>Right Lane</strong>: <code>m &gt; 0.5</code></li>
<li class=""><strong>Horizontal Noise</strong>: <code>|m| &lt;= 0.5</code> (crosswalks and bumps are rejected)</li>
</ul>
<p>A <code>deque(maxlen=7)</code> temporal moving average smooths lane equations across consecutive frames.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-geometric-polygon-iou-lane-intrusion-criterion">3. Geometric Polygon IoU Lane Intrusion Criterion<a href="https://globaldevlog.com/blog/2026-06-26-vision-anpr-2#3-geometric-polygon-iou-lane-intrusion-criterion" class="hash-link" aria-label="Direct link to 3. Geometric Polygon IoU Lane Intrusion Criterion" title="Direct link to 3. Geometric Polygon IoU Lane Intrusion Criterion" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Lane Intrusion IoU Evaluation" src="https://globaldevlog.com/assets/images/02_intrusion_iou-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></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">    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">    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>When <code>IoU &gt; 0.05</code>, the system flags a violation event and triggers <strong>YOLOv5 license plate localization</strong>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-looking-forward">4. Looking Forward<a href="https://globaldevlog.com/blog/2026-06-26-vision-anpr-2#4-looking-forward" class="hash-link" aria-label="Direct link to 4. Looking Forward" title="Direct link to 4. Looking Forward" translate="no">​</a></h2>
<p>In Post #3, we explore <strong>hierarchical object detection with fine-tuned YOLOv5 (<code>lp_det.pt</code>)</strong> and real-time <strong>CSRT tracking integration</strong> to slash GPU inference overhead by 80%.</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] Blackbox Lane Intrusion & Super-Resolution ANPR: 6-Stage End-to-End Pipeline & Real-Time Constraints (#1)]]></title>
        <id>https://globaldevlog.com/blog/2026-06-25-vision-anpr-1</id>
        <link href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1"/>
        <updated>2026-06-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Architecting a 6-stage computer vision pipeline combining dynamic lane detection, IoU intrusion metrics, YOLOv5 license plate detection, EDSR super-resolution, and OCR voting.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="6-Stage ANPR Pipeline Diagram" src="https://globaldevlog.com/assets/images/01_pipeline_diagram-fb1b5567c4acb0cd59bd89773f15c5c8.png" width="2200" height="900" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Low-resolution dashcam footage suffers from severe vehicle vibration, nighttime backlighting, and heavy motion blur, making direct license plate recognition and lane violation tracking extremely error-prone.</li>
<li class=""><strong>Solution</strong>: Designed a unified 6-stage vision architecture: Canny/Hough lane extraction -&gt; YOLOv3 vehicle detection -&gt; polygon IoU lane violation calculation -&gt; YOLOv5 plate localization -&gt; EDSR_x4 super-resolution -&gt; EasyOCR temporal voting.</li>
<li class=""><strong>Result</strong>: Constructed an end-to-end Python pipeline achieving robust vehicle tracking and lane boundary monitoring even under degraded dashcam conditions.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-project-background-why-dashcam-vision-is-hard">1. Project Background: Why Dashcam Vision is Hard<a href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1#1-project-background-why-dashcam-vision-is-hard" class="hash-link" aria-label="Direct link to 1. Project Background: Why Dashcam Vision is Hard" title="Direct link to 1. Project Background: Why Dashcam Vision is Hard" translate="no">​</a></h2>
<p>In intelligent transportation systems (ITS) and autonomous traffic enforcement, <strong>real-time lane intrusion detection and automatic number plate recognition (ANPR)</strong> are foundational capabilities. However, real-world dashcam footage presents severe optical challenges:</p>
<ol>
<li class=""><strong>Extreme Low Resolution &amp; Blurred Textures</strong>: The license plate of a leading or overtaking vehicle often spans merely 30x15 to 50x25 pixels across a 640x480 frame, causing alphanumeric strokes to merge into indistinct blobs.</li>
<li class=""><strong>Vehicle Vibration &amp; Rolling Shutter Distortions</strong>: Road bumps, sudden decelerations, and perspective shifts distort straight lane boundaries.</li>
<li class=""><strong>Real-Time Latency Bottlenecks</strong>: Executing heavy full-frame vision transformers per frame results in fatal FPS drops, disabling real-time tracking.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-6-stage-hierarchical-pipeline-architecture">2. 6-Stage Hierarchical Pipeline Architecture<a href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1#2-6-stage-hierarchical-pipeline-architecture" class="hash-link" aria-label="Direct link to 2. 6-Stage Hierarchical Pipeline Architecture" title="Direct link to 2. 6-Stage Hierarchical Pipeline Architecture" translate="no">​</a></h2>
<p>To achieve both high precision and high throughput, we modularized the system into a <strong>6-stage decoupled pipeline</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["Raw Dashcam Frame 640x480"] --&gt; L1["Stage 1: Canny + Hough Dynamic Lane Extraction"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Raw --&gt; L2["Stage 2: YOLOv3-tiny Vehicle Detection"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L1 &amp; L2 --&gt; L3["Stage 3: Vehicle Footprint &amp; Lane Polygon IoU Intrusion Metric"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L3 -- If Intrusion Triggered --&gt; L4["Stage 4: Localized YOLOv5 Plate Detection in Vehicle ROI"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L4 --&gt; L5["Stage 5: EDSR_x4 Deep 4x Super-Resolution"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L5 --&gt; L6["Stage 6: EasyOCR + Regex + Temporal Majority Voting"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    L6 --&gt; Result["Confirmed Violation Plate: 64가1511"]</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Final Detection and ANPR Recognition Output" src="https://globaldevlog.com/assets/images/02_final_result-02da50ebc7e87755c32fbc132dc9b992.png" width="2131" height="1599" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="decoupled-processing-responsibilities">Decoupled Processing Responsibilities<a href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1#decoupled-processing-responsibilities" class="hash-link" aria-label="Direct link to Decoupled Processing Responsibilities" title="Direct link to Decoupled Processing Responsibilities" translate="no">​</a></h3>
<ol>
<li class=""><strong>Lane Extraction</strong>: Trapezoidal dynamic ROI masking, Canny edge detection, and slope filtering isolate driving corridor bounds.</li>
<li class=""><strong>Vehicle Detection</strong>: Lightweight YOLOv3-tiny outputs candidate vehicle bounding boxes in milliseconds.</li>
<li class=""><strong>Intrusion Criterion</strong>: Computes geometric Intersection-over-Union (IoU &gt; 0.05) between vehicle tire footprint and the lane boundary polygon.</li>
<li class=""><strong>Plate Localization</strong>: YOLOv5 (<code>lp_det.pt</code>) evaluates only the cropped vehicle ROI, saving immense compute cycles.</li>
<li class=""><strong>Super-Resolution</strong>: The cropped plate patch is passed through a deep EDSR_x4 neural network, reconstructing high-frequency character contours.</li>
<li class=""><strong>OCR &amp; Majority Voting</strong>: EasyOCR extracts alphanumeric tokens, which undergo Korean syntax regex validation and temporal multi-frame voting to confirm the plate number.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-core-system-parameters-config">3. Core System Parameters (<code>CONFIG</code>)<a href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1#3-core-system-parameters-config" class="hash-link" aria-label="Direct link to 3-core-system-parameters-config" title="Direct link to 3-core-system-parameters-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-looking-forward">4. Looking Forward<a href="https://globaldevlog.com/blog/2026-06-25-vision-anpr-1#4-looking-forward" class="hash-link" aria-label="Direct link to 4. Looking Forward" title="Direct link to 4. Looking Forward" translate="no">​</a></h2>
<p>In Post #2, we dive deep into <strong>Canny edge filtering, probabilistic Hough transform mathematics (HoughLinesP), slope-based outlier rejection</strong>, and the geometric polygon IoU intrusion criterion.</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 Capstone Dev Log #23] Safety Wrapper & 1st Semester Final Presentation]]></title>
        <id>https://globaldevlog.com/blog/2026-06-18-maeumjigi-23</id>
        <link href="https://globaldevlog.com/blog/2026-06-18-maeumjigi-23"/>
        <updated>2026-06-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Final Milestone: Building the dual-layer Safety Wrapper guardrail architecture and deploying our fine-tuned model to production on AWS EC2 GPU.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-5db6b43f8eb8d83178d8ba6f56f13f4f.png" width="720" height="405" class="img_ev3q"></p>
<p>As we entered June, the Maeumjigi team completed the major developments of the 1st semester Capstone project and focused entirely on building <strong>safety nets</strong> and preparing for the <strong>final presentation</strong>.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-victory-of-the-21-ratio-and-the-safety-wrapper">The Victory of the 2:1 Ratio and the Safety Wrapper<a href="https://globaldevlog.com/blog/2026-06-18-maeumjigi-23#the-victory-of-the-21-ratio-and-the-safety-wrapper" class="hash-link" aria-label="Direct link to The Victory of the 2:1 Ratio and the Safety Wrapper" title="Direct link to The Victory of the 2:1 Ratio and the Safety Wrapper" translate="no">​</a></h3>
<blockquote>
<p>"What was our chosen model?"
"We decided on 2:1. Now design a method to use a safety_wrapper as a safety lock for our Main Large Language Model (LLM) (2:1)."</p>
</blockquote>
<p>As a result of the 4th fine-tuning, we selected the model mixed with a 2:1 ratio of 'emotional' to 'daily' chat as our final LLM. However, given the nature of a suicide prevention chatbot, we could not allow even a single hallucination or fatal wrong answer.</p>
<p>To prevent this, we introduced a <strong>Safety Wrapper</strong>. It verifies the input and output of the LLM, and if a high-risk situation (Active Suicidality) is detected, the architecture instantly switches to a pre-defined crisis response protocol.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloud-server-ec2-budget-and-final-presentation">Cloud Server (EC2) Budget and Final Presentation<a href="https://globaldevlog.com/blog/2026-06-18-maeumjigi-23#cloud-server-ec2-budget-and-final-presentation" class="hash-link" aria-label="Direct link to Cloud Server (EC2) Budget and Final Presentation" title="Direct link to Cloud Server (EC2) Budget and Final Presentation" translate="no">​</a></h3>
<blockquote>
<p>"We need an AWS EC2 quote. What's the budget for 5 months of g5.2xlarge?"
"Draft the 1st_semester_final_presentation.pptx based on our progress!"</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="Final Presentation" src="https://globaldevlog.com/assets/images/02_presentation-4d633f8f7c1e0375cfa80546d37e3291.png" width="720" height="405" class="img_ev3q"></p>
<p>We calculated the Amazon EC2 GPU server budget to run the verified chatbot for 5 months during the summer vacation.</p>
<p>And on June 18th, we gathered all the achievements of our backend, frontend, and Artificial Intelligence (AI) to complete the <strong>1st Semester Final Presentation (PPTX)</strong>!</p>
<p>The long and arduous 1st semester of the Maeumjigi Capstone development has successfully concluded. We will deploy the system to the server over the summer and return with a more advanced service in the 2nd semester!</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="safety wrapper" term="safety wrapper"/>
        <category label="cloud" term="cloud"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #22] 4th Fine-Tuning Complete! Architecture Design & Final Report]]></title>
        <id>https://globaldevlog.com/blog/2026-05-30-maeumjigi-22</id>
        <link href="https://globaldevlog.com/blog/2026-05-30-maeumjigi-22"/>
        <updated>2026-05-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Implementing 4th-round fine-tuning with prompt masking and regularization to defend against system prompt memorization and robotic outputs.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-5fcc23c7f36696eb5311e706c9d9eede.png" width="2495" height="1393" class="img_ev3q"></p>
<p>In the final week of May (27th-30th), we dedicated our efforts to wrapping up the results of the highly anticipated <strong>4th Fine-Tuning (fine_tuning_4)</strong> and solidifying the system's framework.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-checkpoint-4400-and-graph-analysis">Final Checkpoint 4400 and Graph Analysis<a href="https://globaldevlog.com/blog/2026-05-30-maeumjigi-22#final-checkpoint-4400-and-graph-analysis" class="hash-link" aria-label="Direct link to Final Checkpoint 4400 and Graph Analysis" title="Direct link to Final Checkpoint 4400 and Graph Analysis" translate="no">​</a></h3>
<blockquote>
<p>"Qwen35-9B_mixed_7_3/checkpoints/checkpoint-4400/adapter_config.json"</p>
</blockquote>
<p>The 4th fine-tuning model, trained with a mix of daily casual chat and emotional counseling data, finally finished training and yielded the optimal weight: <code>checkpoint-4400</code>.</p>
<p>In previous fine-tunings, there was an overfitting issue where the model would respond in an overly serious counseling mode even to casual chat. As a result of experimenting with the data mix ratio, the model has finally been reborn as a 'perceptive' chatbot that perfectly distinguishes between and appropriately responds to daily conversation and serious crisis counseling!</p>
<p>We visualized all evaluation metrics with <code>matplotlib</code> graphs to prove the performance differences at a glance.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="chatbot-system-architecture-design-and-final-report">Chatbot System Architecture Design and Final Report<a href="https://globaldevlog.com/blog/2026-05-30-maeumjigi-22#chatbot-system-architecture-design-and-final-report" class="hash-link" aria-label="Direct link to Chatbot System Architecture Design and Final Report" title="Direct link to Chatbot System Architecture Design and Final Report" translate="no">​</a></h3>
<blockquote>
<p>"chatbot_architecture.html generation complete"
"Please write the results of fine_tuning_4 as a report (.docx)."</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="System Architecture" src="https://globaldevlog.com/assets/images/02_architecture-41f5095033492354eaf45a8553f49ff6.png" width="2541" height="1390" class="img_ev3q"></p>
<p>To serve the verified model as a real app, we designed the entire system architecture and Knowledge Graph integration plan under the name <code>chatbot_architecture.html</code>.</p>
<p>And on May 30th, with the Artificial Intelligence (AI)'s help, we automatically generated the <code>fine_tuning_4_report.docx</code>, successfully concluding Phase 1 of our fine-tuning project. The report bluntly contains the actual response text comparisons between the original base model and our fine-tuned model, proving that all our hard work was not in vain!</p>
<p>With this, the breathless May dev logs of the Maeumjigi project are all wrapped up. What new challenges await in June?</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="architecture" term="architecture"/>
        <category label="capstone" term="capstone"/>
        <category label="final report" term="final report"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #21] Optimizing Data Mix Ratios and Starting the 4th Fine-Tuning]]></title>
        <id>https://globaldevlog.com/blog/2026-05-22-maeumjigi-21</id>
        <link href="https://globaldevlog.com/blog/2026-05-22-maeumjigi-21"/>
        <updated>2026-05-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Achieving peak model performance by training on the 2:1 emotional-to-daily dialogue ratio, balancing lucid conversational answers with empathetic care.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-a335ed829194fe04a7f1886ca95deda9.png" width="2531" height="1378" class="img_ev3q"></p>
<p>On May 21st and 22nd, the Maeumjigi Capstone team embarked on massive data generation and preparation for the <strong>4th Fine-Tuning (fine_tuning_4)</strong>.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-tug-of-war-between-emotional-and-daily-conversations">The Tug-of-War Between Emotional and Daily Conversations<a href="https://globaldevlog.com/blog/2026-05-22-maeumjigi-21#the-tug-of-war-between-emotional-and-daily-conversations" class="hash-link" aria-label="Direct link to The Tug-of-War Between Emotional and Daily Conversations" title="Direct link to The Tug-of-War Between Emotional and Daily Conversations" translate="no">​</a></h3>
<p>While testing the 3rd fine-tuning model, I discovered a critical issue.</p>
<blockquote>
<p>"When fine-tuning with the haiku_pred dataset, the responses seem too heavily skewed towards emotional counseling. How should we fine-tune to maintain the original model's daily conversation abilities?"</p>
</blockquote>
<p>Our goal is a 'suicide prevention counselor conveying warm empathy', but the model was showing signs of <strong>overfitting</strong>, giving overly serious and emotional responses even to mundane questions like "How's the weather?" or "What should I eat?".</p>
<p>To solve this, we planned an experiment to train the model by mixing emotional data and daily chat data at appropriate ratios.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="generating-10000-v6-emotion-dataset-entries-via-application-programming-interface-api">Generating 10,000 v6 Emotion Dataset Entries via Application Programming Interface (API)<a href="https://globaldevlog.com/blog/2026-05-22-maeumjigi-21#generating-10000-v6-emotion-dataset-entries-via-application-programming-interface-api" class="hash-link" aria-label="Direct link to Generating 10,000 v6 Emotion Dataset Entries via Application Programming Interface (API)" title="Direct link to Generating 10,000 v6 Emotion Dataset Entries via Application Programming Interface (API)" translate="no">​</a></h3>
<blockquote>
<p>"Unify to 512 tokens / Average 500 tokens per entry / 20 Personas / File: v6.jsonl"</p>
</blockquote>
<p>To create the best dataset, I used the Anthropic API (Haiku) Batch mode to generate a staggering 10,000 entries of emotional conversation data (<code>Emotion_train_dataset_v6.jsonl</code>). Though there was a frustrating moment when the API batch ran in the background without showing progress, I filled in the missing entries with an additional script (<code>fill_v6.py</code>) to complete the 10,000 count.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="finding-the-golden-ratio-fine_tuning_4">Finding the Golden Ratio (fine_tuning_4)<a href="https://globaldevlog.com/blog/2026-05-22-maeumjigi-21#finding-the-golden-ratio-fine_tuning_4" class="hash-link" aria-label="Direct link to Finding the Golden Ratio (fine_tuning_4)" title="Direct link to Finding the Golden Ratio (fine_tuning_4)" translate="no">​</a></h3>
<blockquote>
<p>"Emotion_train_dataset_v6.jsonl is complete. Now make a 7:3 ratio dataset, a 2:1 ratio, and a 5:5 ratio dataset."
"This 4th fine-tuning will be based on the training dataset mix ratio (mixed_2_1, mixed_5_5, mixed_7_3) instead of alpha values."</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="Ratio Experiment" src="https://globaldevlog.com/assets/images/02_ratio-aa49284ad0e0859f6efd82a31aa871ce.png" width="2507" height="1395" class="img_ev3q"></p>
<p>The 4th fine-tuning has now become an arena to verify which data ratio perfectly harmonizes the Artificial Intelligence (AI)'s 'daily casualness' with 'professional counseling'. We generated three versions of the dataset and started injecting them into the Qwen3.5-9B model!</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="dataset" term="dataset"/>
        <category label="prompt" term="prompt"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots Robotics] Sim-to-Real: Hardware Transfer via Arduino Uno, HC-06 Bluetooth & ArUco Vision Tracking (#5)]]></title>
        <id>https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5</id>
        <link href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5"/>
        <updated>2026-05-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Deploying the simulation-trained 648-bin Q-Learning policy onto a physical 2-wheeled Arduino Uno robot using ceiling webcam homography and real-time ArUco 6-DoF marker pose tracking.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="ArUco Marker Vision Tracking" src="https://globaldevlog.com/assets/images/01_aruco_vision-b5bd404f0b35979c465be925a4fac943.png" width="1280" height="720" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Severe computational constraints on microcontrollers (Arduino Uno ATmega328P with only 2KB SRAM), non-linear wheel slip, noisy ultrasonic sensing, and the classical <strong>Reality Gap</strong> between Webots simulation physics and physical hardware.</li>
<li class=""><strong>Solution</strong>: Designed a <strong>distributed I/O architecture (HC-06 Bluetooth at 115,200 baud)</strong> where a host PC runs OpenCV vision processing and Q-Table inference while the Arduino handles low-level PWM motor actuation. Overhead camera lens distortion correction and <strong>ArUco marker homography (<code>vision.py</code>)</strong> deliver sub-centimeter real-time (x, y, θ) tracking.</li>
<li class=""><strong>Result</strong>: Zero-shot transfer of the simulation-trained 648-state Q-Table achieved an <strong>86.7% target reaching success rate (26/30 trials)</strong> in a physical 1.2m × 1.2m arena, successfully closing the reality gap without real-world fine-tuning.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-challenge-of-sim-to-real-navigating-the-reality-gap">1. The Challenge of Sim-to-Real: Navigating the Reality Gap<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#1-the-challenge-of-sim-to-real-navigating-the-reality-gap" class="hash-link" aria-label="Direct link to 1. The Challenge of Sim-to-Real: Navigating the Reality Gap" title="Direct link to 1. The Challenge of Sim-to-Real: Navigating the Reality Gap" translate="no">​</a></h2>
<p>In virtual Webots simulations, surface friction coefficients remain idealized, wheel motors rotate with deterministic precision, and simulated distance sensors yield noiseless measurements. Real hardware, however, introduces unpredictable physical perturbations:</p>
<ol>
<li class=""><strong>Severe Memory &amp; Compute Bottlenecks</strong>: The <strong>Arduino Uno (ATmega328P)</strong> operates with only 32KB flash memory and 2KB SRAM. Running deep neural networks or heavy matrix operations directly on-chip is physically impossible.</li>
<li class=""><strong>Sensor Noise &amp; Blind Angles</strong>: Inexpensive ultrasonic rangefinders suffer from specular reflection at shallow incidence angles, reporting 0 or spiking to maximum range when pinging smooth acrylic boundaries.</li>
<li class=""><strong>Wheel Slip &amp; Odometry Drift</strong>: Minor variations in wheel friction and motor gearboxes cause differential-drive robots to veer off-course even when commanded to drive straight, making dead-reckoning odometry completely unreliable.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-distributed-control-architecture-host-pc-brain--arduino-muscle">2. Distributed Control Architecture: Host PC (Brain) ↔ Arduino (Muscle)<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#2-distributed-control-architecture-host-pc-brain--arduino-muscle" class="hash-link" aria-label="Direct link to 2. Distributed Control Architecture: Host PC (Brain) ↔ Arduino (Muscle)" title="Direct link to 2. Distributed Control Architecture: Host PC (Brain) ↔ Arduino (Muscle)" translate="no">​</a></h2>
<p>To overcome microcontroller limitations, we decoupled high-level reasoning from low-level actuation: <strong>"PC as the brain, Arduino as the muscle."</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 CeilingCam["Ceiling Vision System (Overhead Camera)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Cam["FHD Webcam (30 FPS)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Aruco["ArUco Marker Detection (DICT_4X4_50)&lt;br&gt;Perspective Homography (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["Host PC (Python Engine)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Pose["Robot Pose Estimation (x, y, θ)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["648-bin Discretizer"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        QL["Trained Q-Table (ε=0)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Host["HC-06 Bluetooth SPP (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["Physical Mobile Robot (Real Hardware)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        BT_Slave["HC-06 Bluetooth Receiver"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        MCU["Arduino Uno (Firmware)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Motors["L298N Dual H-Bridge + Geared DC Motors"]</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;|"Action Byte [0..3]&lt;br&gt;Ack/Status"| 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>Host PC Pipeline</strong>: An overhead webcam captures the complete arena top-down, tracking an ArUco fiducial marker mounted on top of the robot at 30 FPS. The robot's planar pose <code>(x, y, θ)</code> is calculated and mapped into the discrete 648-state space to retrieve the optimal greedy action from the Q-Table.</li>
<li class=""><strong>HC-06 Serial Communication</strong>: To minimize Round-Trip Time (RTT) over the Bluetooth Serial Port Profile (SPP), the transmission baud rate was upgraded from the default 9,600 to <strong>115,200 bps</strong>, sustaining a low-latency 30~50Hz control loop.</li>
<li class=""><strong>Arduino Firmware (<code>arduino_firmware.ino</code>)</strong>: Upon receiving single-byte action commands (<code>0: Forward</code>, <code>1: Backward</code>, <code>2: Turn Left</code>, <code>3: Turn Right</code>), the microcontroller modulates PWM pins on the L298N motor driver for precise tick-based open-loop pulses.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-overhead-camera-homography--aruco-vision-tracking">3. Overhead Camera Homography &amp; ArUco Vision Tracking<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#3-overhead-camera-homography--aruco-vision-tracking" class="hash-link" aria-label="Direct link to 3. Overhead Camera Homography &amp; ArUco Vision Tracking" title="Direct link to 3. Overhead Camera Homography &amp; ArUco Vision Tracking" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Hardware Setup and Arena Calibration" src="https://globaldevlog.com/assets/images/02_hardware_setup-fddea6cef90bd71036b2aff5eef38f33.png" width="1707" height="1067" class="img_ev3q"></p>
<p>To measure robot positioning and obstacle proximity with millimeter accuracy across the real arena, we built an <strong>OpenCV-based top-down computer vision pipeline</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-perspective-rectification--homography-calib_autopy">📐 Perspective Rectification &amp; Homography (<code>calib_auto.py</code>)<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#-perspective-rectification--homography-calib_autopy" class="hash-link" aria-label="Direct link to -perspective-rectification--homography-calib_autopy" title="Direct link to -perspective-rectification--homography-calib_autopy" translate="no">​</a></h3>
<ol>
<li class=""><strong>Checkerboard Distortion Calibration</strong>: To eliminate barrel distortion from wide-angle webcam lenses, intrinsic camera matrix (K) and distortion coefficients (D) were pre-computed using a standard 9×6 checkerboard.</li>
<li class=""><strong>Perspective Homography</strong>: To project the angled camera viewpoint into a canonical 1.2m × 1.2m square bird's-eye view, a 3×3 perspective homography matrix (<code>homography.npy</code>) was established from the 4 marked arena corner coordinates.</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-aruco-marker-pose-tracking-implementation-visionpy">💻 ArUco Marker Pose Tracking Implementation (<code>vision.py</code>)<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#-aruco-marker-pose-tracking-implementation-visionpy" class="hash-link" aria-label="Direct link to -aruco-marker-pose-tracking-implementation-visionpy" title="Direct link to -aruco-marker-pose-tracking-implementation-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"># Load ArUco dictionary and detection parameters</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">"""Calculates planar coordinates (x, y) and heading angle (theta) from rectified image."""</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"># Calculate heading angle (theta) from marker front edge vector</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-hardware-troubleshooting--real-world-deployment-results">4. Hardware Troubleshooting &amp; Real-World Deployment Results<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#4-hardware-troubleshooting--real-world-deployment-results" class="hash-link" aria-label="Direct link to 4. Hardware Troubleshooting &amp; Real-World Deployment Results" title="Direct link to 4. Hardware Troubleshooting &amp; Real-World Deployment Results" translate="no">​</a></h2>
<p>Transitioning from simulation to physical operation revealed critical edge cases addressed through robust engineering countermeasures:</p>
<table><thead><tr><th style="text-align:left">Symptom (Troubleshooting)</th><th style="text-align:left">Root Cause</th><th style="text-align:left">Engineering Solution</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Robot drifts sideways during forward command</strong></td><td style="text-align:left">Gearbox friction differences between left &amp; right DC motors</td><td style="text-align:left">Implemented an interactive calibration script (<code>ble_vision_turn_calib.py</code>) to apply dynamic PWM trim offsets</td></tr><tr><td style="text-align:left"><strong>Runaway behavior during Bluetooth disconnect</strong></td><td style="text-align:left">Packet drops under RF interference causing robot to retain last command</td><td style="text-align:left">Embedded a 200ms communication watchdog timer in firmware to immediately cut motor power upon signal loss</td></tr><tr><td style="text-align:left"><strong>Intermittent ArUco marker detection loss</strong></td><td style="text-align:left">Specular glare from ceiling fluorescent lighting on glossy marker surface</td><td style="text-align:left">Fine-tuned adaptive thresholding parameters and added a 1D Kalman Filter position extrapolator</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-final-real-world-benchmark">🏆 Final Real-World Benchmark<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#-final-real-world-benchmark" class="hash-link" aria-label="Direct link to 🏆 Final Real-World Benchmark" title="Direct link to 🏆 Final Real-World Benchmark" translate="no">​</a></h3>
<ul>
<li class=""><strong>Autonomous Goal Reaching Rate</strong>: Tested across 30 independent trials in the physical 1.2m × 1.2m obstacle arena, the robot achieved an <strong>86.7% success rate (26/30 successful arrivals)</strong>.</li>
<li class="">Leveraging just <strong>2,592 pre-computed discrete Q-values (~2.5 KB)</strong>, the agent navigated complex physical obstacles without any real-world gradient updates.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-concluding-the-rl-q-learning-dev-log-series">5. Concluding the RL Q-Learning Dev Log Series<a href="https://globaldevlog.com/blog/2026-05-21-rl-qlearning-5#5-concluding-the-rl-q-learning-dev-log-series" class="hash-link" aria-label="Direct link to 5. Concluding the RL Q-Learning Dev Log Series" title="Direct link to 5. Concluding the RL Q-Learning Dev Log Series" translate="no">​</a></h2>
<p>With this final Sim-to-Real deployment, our 5-part <strong>RL Q-Learning Robotics series</strong> reaches its conclusion:</p>
<ol>
<li class=""><strong>#1</strong>: 648-Bin State Discretization &amp; Webots TCP Socket IPC Architecture</li>
<li class=""><strong>#2</strong>: Tabular Q-Learning ε-Decay Exploration &amp; Multi-Objective Reward Shaping</li>
<li class=""><strong>#3</strong>: Overcoming Arena Local Minima in 12-Wall Environments via Headless Pipelines</li>
<li class=""><strong>#4</strong>: Quantitative Benchmark with <code>evaluate_qlearning</code> vs. SARSA &amp; DQN</li>
<li class=""><strong>#5</strong>: Sim-to-Real Hardware Transfer via Arduino Uno, Bluetooth SPP &amp; ArUco Vision</li>
</ol>
<p>From foundational simulator modeling and algorithmic tuning to multi-seed automation and physical hardware deployment, this series documented the complete lifecycle of reinforcement learning for embedded robotics. Stay tuned for future deep dives into our next projects!</p>]]></content>
        <author>
            <name>Jihwan Park (jaybee)</name>
            <uri>https://github.com/jbjihwan</uri>
        </author>
        <category label="reinforcement-learning" term="reinforcement-learning"/>
        <category label="sim-to-real" term="sim-to-real"/>
        <category label="arduino" term="arduino"/>
        <category label="robotics" term="robotics"/>
        <category label="computer-vision" term="computer-vision"/>
        <category label="aruco" term="aruco"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #20] 3rd Fine-Tuning Complete & Backend Feature Enhancements]]></title>
        <id>https://globaldevlog.com/blog/2026-05-20-maeumjigi-20</id>
        <link href="https://globaldevlog.com/blog/2026-05-20-maeumjigi-20"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Designing our 3rd fine-tuning strategy, carefully calibrating the ratio between casual chat and crisis response to prevent emotional over-triggering.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-ac5e0126cfa930b87e044dbc1b82cc03.png" width="2519" height="1373" class="img_ev3q"></p>
<p>Over the two days of May 19th and 20th, the automation of evaluation for the 3rd fine-tuning (<code>fine_tuning_3</code>) and the enhancement of backend/frontend features for building the actual app service proceeded in parallel.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="concluding-the-3rd-fine-tuning-and-automation-scripts">Concluding the 3rd Fine-Tuning and Automation Scripts<a href="https://globaldevlog.com/blog/2026-05-20-maeumjigi-20#concluding-the-3rd-fine-tuning-and-automation-scripts" class="hash-link" aria-label="Direct link to Concluding the 3rd Fine-Tuning and Automation Scripts" title="Direct link to Concluding the 3rd Fine-Tuning and Automation Scripts" translate="no">​</a></h3>
<p>Based on the insights gained from the previous fine-tuning, the training for the 3rd fine-tuning, which was set up with concepts of knowledge graphs and ontologies in mind, was completed.</p>
<blockquote>
<p>"Select the checkpoint with the Best Eval from the experimental models in the fine_tuning_3 folder, replace the files in the adapter folder, and report the results."
"Write a run_test_20_30.sh file that runs inference and evaluation in parallel for fine_tuning_3 as well."</p>
</blockquote>
<p>Instead of manually digging through folders to copy checkpoints like before, I instructed the Artificial Intelligence (AI) to write a script that automatically extracted the best performing model weights (Best Checkpoint) and set them up in the <code>adapter</code> folder. Then, I executed the parallel inference script, <code>run_test_20_30.sh</code>, throwing the evaluation for the 4 Alias models to the background server.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="backendfrontend-chatbot-database-db-saving-and-community-board-features">Backend/Frontend: Chatbot Database (DB) Saving and Community Board Features<a href="https://globaldevlog.com/blog/2026-05-20-maeumjigi-20#backendfrontend-chatbot-database-db-saving-and-community-board-features" class="hash-link" aria-label="Direct link to Backend/Frontend: Chatbot Database (DB) Saving and Community Board Features" title="Direct link to Backend/Frontend: Chatbot Database (DB) Saving and Community Board Features" translate="no">​</a></h3>
<p>While the models were hard at work running inferences on the server, I accelerated the <code>mental-health</code> DB and app integration work in my local environment.</p>
<blockquote>
<p>"Summarize seed_data.sql. What is the pw for users? Is it Test1234!? What was the db I was trying to apply this sql to?"</p>
</blockquote>
<p>To break through the login issue that caused the frustrating <code>401 Unauthorized</code> error last time, I thoroughly examined <code>seed_data.sql</code> to verify the password hashing and user data.</p>
<p>As a result, I was able to leave a meaningful Git commit on the afternoon of May 20th:
<code>feat: Added chatbot DB save, board pagination, and emotion diary filter</code></p>
<p><img decoding="async" loading="lazy" alt="App Features" src="https://globaldevlog.com/assets/images/02_app_feature-26d976bed8544fa566bd187f86d90617.png" width="2522" height="1384" class="img_ev3q"></p>
<p>Finally, the conversation logs of our fine-tuned Large Language Model (LLM) chatbot officially started being saved to the DB! In addition, the pagination feature for the community board where users can communicate, and the emotion diary filter feature for recording daily moods were successfully added to the frontend.</p>
<p>It was a thrilling moment when the massive, invisible fine-tuned model finally began to be served in the beautiful vessel of a mobile app.</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="backend" term="backend"/>
        <category label="frontend" term="frontend"/>
        <category label="database" term="database"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots Robotics] Deterministic Evaluation via evaluate_qlearning & Benchmark vs. DQN and SARSA (#4)]]></title>
        <id>https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4</id>
        <link href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4"/>
        <updated>2026-05-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Zero-exploration (ε=0) deterministic evaluation pipeline with evaluate_qlearning.py, examining path efficiency, near-miss metrics, and benchmark comparisons against DQN and SARSA.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Success and Steps Benchmark" src="https://globaldevlog.com/assets/images/01_eval_success_steps-1434c5fba2793b6877d335bf289ef59b.png" width="1170" height="650" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Objective</strong>: Measure the pure, deterministic performance of the learned greedy policy (ε=0) without exploration noise, validating real-world deployability.</li>
<li class=""><strong>Methodology</strong>: Implemented <code>evaluate_qlearning.py</code> running 30 independent trials across 5 round-robin evaluation seeds, rigorously tracking success rates, path efficiency vs. theoretical shortest routes, and obstacle near-miss counts.</li>
<li class=""><strong>Result</strong>: Tabular Q-Learning attained a 93.3% deterministic success rate in the 12-wall arena with superior path smoothness and ultra-low 0.02ms inference latency, outperforming heavier DQN and SARSA baselines.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-quantitative-evaluation-pipeline-evaluate_qlearningpy">1. Quantitative Evaluation Pipeline (<code>evaluate_qlearning.py</code>)<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#1-quantitative-evaluation-pipeline-evaluate_qlearningpy" class="hash-link" aria-label="Direct link to 1-quantitative-evaluation-pipeline-evaluate_qlearningpy" title="Direct link to 1-quantitative-evaluation-pipeline-evaluate_qlearningpy" translate="no">​</a></h2>
<p>Training reward curves are inherently noisy due to ε-greedy exploration. To evaluate the true deployment capability of our agent, we must test the <strong>purely deterministic policy ($\epsilon = 0$)</strong>.</p>
<p>We built an evaluation runner that loads the saved Q-table (<code>qlearning_qtable.npy</code>) and evaluates the agent across 5 distinct test seeds (<code>EVAL_SEEDS = (0, 1, 2, 3, 4)</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 LR</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Model["Trained Q-Table&lt;br&gt;(qlearning_qtable.npy)"] --&gt; Agent["Greedy Agent&lt;br&gt;(ε = 0 Fixed)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    EvalSeeds["5 Test Seeds&lt;br&gt;(EVAL_SEEDS 0..4)"] --&gt; Webots["Webots Simulator&lt;br&gt;(30 Evaluation Trials)"]</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["Trace &amp; Metric Logging&lt;br&gt;(eval_log.csv / traces)"]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-evaluation-loop-architecture">💻 Evaluation Loop Architecture<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#-evaluation-loop-architecture" class="hash-link" aria-label="Direct link to 💻 Evaluation Loop Architecture" title="Direct link to 💻 Evaluation Loop Architecture" 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 excerpt</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"># Load trained 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"># Pure 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"># Log trajectory coordinates and near-misses (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-comprehensive-evaluation-metrics">2. Comprehensive Evaluation Metrics<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#2-comprehensive-evaluation-metrics" class="hash-link" aria-label="Direct link to 2. Comprehensive Evaluation Metrics" title="Direct link to 2. Comprehensive Evaluation Metrics" translate="no">​</a></h2>
<ol>
<li class=""><strong>Success Rate</strong>: Percentage of runs reaching the target radius (0.10m) within 150 steps without collision.</li>
<li class=""><strong>Path Efficiency</strong>: Ratio of theoretical shortest path distance (L_shortest) to actual traversed path length (L_actual).
<strong>Path Efficiency (%) = (Shortest Path / Actual Path) × 100%</strong></li>
<li class=""><strong>Near-Miss Frequency</strong>: Number of steps where obstacle distance dipped below 0.15m.</li>
<li class=""><strong>Oscillation Count</strong>: Metric penalizing back-and-forth steering jitter.</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-algorithm-benchmark-tabular-q-learning-vs-sarsa-vs-dqn">3. Algorithm Benchmark: Tabular Q-Learning vs. SARSA vs. DQN<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#3-algorithm-benchmark-tabular-q-learning-vs-sarsa-vs-dqn" class="hash-link" aria-label="Direct link to 3. Algorithm Benchmark: Tabular Q-Learning vs. SARSA vs. DQN" title="Direct link to 3. Algorithm Benchmark: Tabular Q-Learning vs. SARSA vs. DQN" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Trajectory Quality &amp;amp; Benchmarks" src="https://globaldevlog.com/assets/images/02_eval_quality-42dd1fc5e31c5e63d48c26ece24e8936.png" width="1625" height="585" class="img_ev3q"></p>
<p>Comparative evaluation in the identical Webots 12-wall arena across the three reinforcement learning algorithms revealed clear performance trade-offs:</p>
<table><thead><tr><th style="text-align:left">Metric</th><th style="text-align:center">Tabular Q-Learning (Ours)</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>Learning Paradigm</strong></td><td style="text-align:center">Off-policy TD(0)</td><td style="text-align:center">On-policy TD(0)</td><td style="text-align:center">Neural Network (Deep RL)</td></tr><tr><td style="text-align:left"><strong>Parameter Size</strong></td><td style="text-align:center"><strong>2,592 floats (~2.5 KB)</strong></td><td style="text-align:center">2,592 floats (~2.5 KB)</td><td style="text-align:center">~150,000 weights (~600 KB)</td></tr><tr><td style="text-align:left"><strong>Deterministic Success Rate (<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>Average Steps per Episode</strong></td><td style="text-align:center"><strong>41.2 steps</strong></td><td style="text-align:center">48.5 steps</td><td style="text-align:center">44.7 steps</td></tr><tr><td style="text-align:left"><strong>Per-Step Inference Latency</strong></td><td style="text-align:center"><strong>0.02 ms</strong> (NumPy array lookup)</td><td style="text-align:center">0.02 ms</td><td style="text-align:center">2.15 ms (PyTorch forward pass)</td></tr><tr><td style="text-align:left"><strong>Near-Miss Frequency</strong></td><td style="text-align:center">Low (balanced detour)</td><td style="text-align:center">Minimal (excessively conservative)</td><td style="text-align:center">Moderate</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-in-depth-behavioral-analysis">🔍 In-Depth Behavioral Analysis<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#-in-depth-behavioral-analysis" class="hash-link" aria-label="Direct link to 🔍 In-Depth Behavioral Analysis" title="Direct link to 🔍 In-Depth Behavioral Analysis" translate="no">​</a></h3>
<ul>
<li class=""><strong>Why Q-Learning Excels</strong>: By evaluating <code>max_a' Q(s', a')</code>, off-policy Q-Learning ignores transient exploratory collisions during training, settling on the <strong>shortest feasible detour path</strong>.</li>
<li class=""><strong>SARSA's Conservative Bias</strong>: On-policy updates factor in exploration penalties, causing SARSA to over-correct and choose excessively long, inefficient detours to avoid walls at all costs.</li>
<li class=""><strong>DQN's Trade-off</strong>: DQN handles continuous inputs well, but training stability requires replay buffers and target networks, while its 2.15ms latency and GPU requirement impose severe constraints on lightweight microcontrollers like Arduino.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-evaluation-takeaways">4. Evaluation Takeaways<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#4-evaluation-takeaways" class="hash-link" aria-label="Direct link to 4. Evaluation Takeaways" title="Direct link to 4. Evaluation Takeaways" translate="no">​</a></h2>
<p>Our discrete 648-bin <strong>Tabular Q-Learning model requires just 2.5 KB of memory</strong>, yet matched and exceeded deep neural network models in success rate (93.3%) and real-time execution speed.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next">5. What's Next?<a href="https://globaldevlog.com/blog/2026-05-20-rl-qlearning-4#5-whats-next" class="hash-link" aria-label="Direct link to 5. What's Next?" title="Direct link to 5. What's Next?" translate="no">​</a></h2>
<p>With simulation benchmarks thoroughly concluded, the ultimate milestone remains: <strong>transferring policy to reality (Sim-to-Real)</strong>.</p>
<p>In our final Episode #5, we will detail the hardware integration: deploying our policy to a <strong>2-wheel differential Arduino Uno robot, communicating via HC-06 Bluetooth, and tracking ground-truth position via ceiling webcam checkerboard calibration and ArUco markers</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="evaluation" term="evaluation"/>
        <category label="benchmark" term="benchmark"/>
        <category label="dqn" term="dqn"/>
        <category label="sarsa" term="sarsa"/>
        <category label="robotics" term="robotics"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots Robotics] Conquering Local Minima in 12-Wall Obstacles: Multi-Seed Headless Simulation (#3)]]></title>
        <id>https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3</id>
        <link href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3"/>
        <updated>2026-05-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Overcoming the Local Minima dilemma in complex 12-wall obstacle environments (obstacle12) and orchestrating high-speed automated multi-seed Webots simulations.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="obstacle12 Simulation Arena" src="https://globaldevlog.com/assets/images/01_obstacle12_arena-bc2bc496b7ff2cdfe32dd9630f39bd68.png" width="1438" height="966" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: When a goal lies directly behind an obstacle, the myopic nature of distance-delta rewards (+0.1 × Δdist) pulls the robot into head-on collisions, trapping it in severe <strong>Local Minima</strong>.</li>
<li class=""><strong>Solution</strong>: Integrated 3-way head-scan obstacle perception (front/left/right) with long-horizon Bellman value propagation (γ=0.99), alongside an automated PowerShell <strong>Webots Headless pipeline (<code>headless_run.ps1</code>)</strong> executing multi-seed training across seeds 0, 1, and 2.</li>
<li class=""><strong>Result</strong>: The agent successfully learned robust detour policies in the 12-wall arena, achieving a 90%+ goal success rate after 600 episodes.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-the-obstacle-challenge-the-local-minima-trap">1. The Obstacle Challenge: The Local Minima Trap<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#1-the-obstacle-challenge-the-local-minima-trap" class="hash-link" aria-label="Direct link to 1. The Obstacle Challenge: The Local Minima Trap" title="Direct link to 1. The Obstacle Challenge: The Local Minima Trap" translate="no">​</a></h2>
<p>In an empty arena (<code>no_obstacle</code>), navigating is simply a matter of steering toward the target heading. However, introducing <strong>12 interior walls (<code>obstacle12</code>)</strong> unveiled a critical bottleneck in reward design:</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["Start (Robot)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall["Obstacle Wall"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Goal["Target 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;|"Direct drive (greedy distance reward)"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Wall -.-&gt;|"Collision &amp; deadlocked (Local Minima)"| Wall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    Start ==&gt;|"Detour Policy&lt;br&gt;Accepts temporary distance penalty"| Goal</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>The Dilemma</strong>: When the target is directly on the other side of an interior wall, driving straight ahead leads directly to collision.</li>
<li class=""><strong>Reward Conflict</strong>: Circumnavigating the wall requires moving temporarily away from the goal (negative distance delta). Under naive exploration, the agent enters a deadlock—spinning or timing out against the wall.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-solution-strategy-head-scan-perception--value-backpropagation">2. Solution Strategy: Head-Scan Perception &amp; Value Backpropagation<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#2-solution-strategy-head-scan-perception--value-backpropagation" class="hash-link" aria-label="Direct link to 2. Solution Strategy: Head-Scan Perception &amp; Value Backpropagation" title="Direct link to 2. Solution Strategy: Head-Scan Perception &amp; Value Backpropagation" translate="no">​</a></h2>
<p>Overcoming this trap relies on coupling <strong>3-directional head-scan perception</strong> with a <strong>high discount factor (γ = 0.99)</strong>:</p>
<ol>
<li class=""><strong>Perceptual State Discretization</strong>:<!-- -->
<ul>
<li class="">Attempting to advance while <code>front_distance = Danger</code> yields a heavy collision penalty (-1.0).</li>
<li class="">Sweeping the sensor head left and right reveals free avenues (<code>left_distance = Safe</code> or <code>right_distance = Safe</code>), encouraging lateral turns.</li>
</ul>
</li>
<li class=""><strong>Long-Term Temporal Difference Credit Assignment</strong>:<!-- -->
<ul>
<li class="">Although turning away temporarily depresses the step reward, successfully rounding the wall and reaching the target yields the substantial terminal reward (+1.0).</li>
<li class="">Backpropagated through Q(s, a) with γ = 0.99, the agent gradually learns that detour steps possess higher expected return than head-on approaches.</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-webots-headless-automation-pipeline">3. Webots Headless Automation Pipeline<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#3-webots-headless-automation-pipeline" class="hash-link" aria-label="Direct link to 3. Webots Headless Automation Pipeline" title="Direct link to 3. Webots Headless Automation Pipeline" translate="no">​</a></h2>
<p>Rigorous reinforcement learning requires <strong>multi-seed validation (Seeds 0, 1, 2)</strong>. Running 1,500 episodes interactively in the Webots GUI three times would take many hours.</p>
<p>We automated this with a <strong><code>headless_run.ps1</code> PowerShell script</strong>, launching Webots in high-speed, no-render mode (<code>--mode=fast --no-rendering</code>):</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-automated-pipeline-headless_runps1">💻 Automated Pipeline (<code>headless_run.ps1</code>)<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#-automated-pipeline-headless_runps1" class="hash-link" aria-label="Direct link to -automated-pipeline-headless_runps1" title="Direct link to -automated-pipeline-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"># Launch Webots in background headless fast mode</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"># Train agent over socket IPC</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"># Cleanup process and generate performance plots</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-empirical-evaluation-cross-environment-learning-curves">4. Empirical Evaluation: Cross-Environment Learning Curves<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#4-empirical-evaluation-cross-environment-learning-curves" class="hash-link" aria-label="Direct link to 4. Empirical Evaluation: Cross-Environment Learning Curves" title="Direct link to 4. Empirical Evaluation: Cross-Environment Learning Curves" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Cross-Environment Learning Curves" src="https://globaldevlog.com/assets/images/02_comparison_curves-0d241e07d5b445acc1866d00247179b7.png" width="1625" height="585" class="img_ev3q"></p>
<p>The graph above contrasts convergence across 3 random seeds between the unobstructed world (<code>no_obstacle</code>) and the 12-wall obstacle world (<code>obstacle12</code>).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-benchmark-metrics">📊 Benchmark Metrics<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#-benchmark-metrics" class="hash-link" aria-label="Direct link to 📊 Benchmark Metrics" title="Direct link to 📊 Benchmark Metrics" translate="no">​</a></h3>
<table><thead><tr><th style="text-align:left">Metric</th><th style="text-align:center">Empty Arena (<code>no_obstacle</code>)</th><th style="text-align:center">12-Wall Arena (<code>obstacle12</code>)</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Initial Convergence</strong></td><td style="text-align:center">~300 - 400 episodes</td><td style="text-align:center">~600 - 800 episodes</td></tr><tr><td style="text-align:left"><strong>Final Success Rate (100-ep MA)</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>Mean Steps per Episode</strong></td><td style="text-align:center">15 - 22 steps</td><td style="text-align:center">38 - 52 steps (detour path)</td></tr><tr><td style="text-align:left"><strong>Cross-Seed Variance</strong></td><td style="text-align:center">Negligible (highly stable)</td><td style="text-align:center">Moderate (exploration path dependency)</td></tr></tbody></table>
<ul>
<li class=""><strong>Extended Exploration</strong>: In <code>obstacle12</code>, discovering viable detour corridors requires approximately 200 additional episodes of random trials, keeping the average reward depressed until episode 400.</li>
<li class=""><strong>Detour Mastery</strong>: Beyond episode 800, all seeds stabilize above 90% success, confirming that the agent reliably maneuvers through tight gaps without collisions.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next">5. What's Next?<a href="https://globaldevlog.com/blog/2026-05-19-rl-qlearning-3#5-whats-next" class="hash-link" aria-label="Direct link to 5. What's Next?" title="Direct link to 5. What's Next?" translate="no">​</a></h2>
<p>Our 648-bin Tabular Q-Learning model successfully navigates maze-like obstacle configurations.</p>
<p>In Episode #4, we will load the saved model checkpoints (<code>models/*.npy</code>) to benchmark greedy, deterministic performance using <strong><code>evaluate_qlearning.py</code> and analyze comparative metrics against DQN and SARSA</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="obstacle-avoidance" term="obstacle-avoidance"/>
        <category label="local-minima" term="local-minima"/>
        <category label="headless" term="headless"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots Robotics] Tabular Q-Learning Implementation & Reward Shaping Tuning (#2)]]></title>
        <id>https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2</id>
        <link href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2"/>
        <updated>2026-05-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[In-depth breakdown of Tabular Q-Learning TD(0) update formulas, exponential epsilon-decay exploration, and dense reward shaping to overcome sparse rewards in mobile robot navigation.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Training Reward Curve" src="https://globaldevlog.com/assets/images/01_reward_curve-dafe6b9a6b58ab351aa0d2717b504f0a.png" width="800" height="400" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Sparse reward setups (+1.0 at goal, -1.0 at collision) cause severe exploration delays, causing the agent to wander or spin for thousands of episodes before stumbling upon the target.</li>
<li class=""><strong>Solution</strong>: Implemented a multi-objective <strong>Dense Reward Shaping</strong> mechanism combining potential-based Euclidean distance delta rewards (+0.1 × Δdist) with step (-0.01), turning (-0.01), and backward (-0.05) penalties, paired with exponential epsilon decay (1.0 → 0.05).</li>
<li class=""><strong>Result</strong>: The moving average reward shifted into a steady positive regime (0.7+) within 500 episodes in the <code>no_obstacle</code> scenario, achieving a 98%+ goal success rate.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-tabular-q-learning-update-formula--agent-architecture">1. Tabular Q-Learning Update Formula &amp; Agent Architecture<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#1-tabular-q-learning-update-formula--agent-architecture" class="hash-link" aria-label="Direct link to 1. Tabular Q-Learning Update Formula &amp; Agent Architecture" title="Direct link to 1. Tabular Q-Learning Update Formula &amp; Agent Architecture" translate="no">​</a></h2>
<p>Tabular Q-Learning is an <strong>off-policy Temporal Difference (TD(0))</strong> algorithm grounded in the Bellman Optimality Equation.</p>
<p>The agent maintains an action-value table Q(s, a) structured as a 2D NumPy array of shape $(648, 4)$. Using the experience transition tuple $(s, a, r, s', done)$ streamed from the Webots socket server, the $Q$-values are updated in real time via:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-q-learning-td0-update-rule">📐 Q-Learning TD(0) Update Rule<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#-q-learning-td0-update-rule" class="hash-link" aria-label="Direct link to 📐 Q-Learning TD(0) Update Rule" title="Direct link to 📐 Q-Learning TD(0) Update Rule" 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>: Balances the rate of incorporating new feedback versus retaining historical estimates.</li>
<li class=""><strong>Discount Factor (γ = 0.99)</strong>: Values long-term future returns, critical for multi-step navigation.</li>
<li class=""><strong>Terminal Masking (<code>1 - done</code>)</strong>: Clamps future expectations to zero upon goal arrival or collision, isolating the terminal reward.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-agent-core-implementation-qlearning_agentpy">💻 Agent Core Implementation (<code>qlearning_agent.py</code>)<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#-agent-core-implementation-qlearning_agentpy" class="hash-link" aria-label="Direct link to -agent-core-implementation-qlearning_agentpy" title="Direct link to -agent-core-implementation-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">"""ε-Greedy behavior policy: Exploration vs. Exploitation"""</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) Off-policy Q-Table update"""</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">"""Exponential decay executed at the end of each episode"""</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-exponential-epsilon-greedy-decay-strategy">2. Exponential Epsilon-Greedy Decay Strategy<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#2-exponential-epsilon-greedy-decay-strategy" class="hash-link" aria-label="Direct link to 2. Exponential Epsilon-Greedy Decay Strategy" title="Direct link to 2. Exponential Epsilon-Greedy Decay Strategy" translate="no">​</a></h2>
<p>At the start of training, the agent has zero domain knowledge, requiring <strong>ε = 1.0 (100% random exploration)</strong> to discover the arena boundaries.</p>
<p>However, an uncurbed exploration rate induces perpetual collisions even after the optimal path has been resolved. We apply per-episode exponential decay:</p>
<p><strong>ε_(ep+1) = max(ε_min, ε_ep × 0.995)</strong></p>
<ul>
<li class=""><strong>Episode 1</strong>: ε = 1.0 (Full exploration)</li>
<li class=""><strong>Episode 300</strong>: ε ≈ 0.22 (Shift toward exploiting high Q-values)</li>
<li class=""><strong>Episode 600</strong>: ε = 0.05 (Floor exploration rate ε_min, 95% greedy execution)</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-dense-reward-shaping-for-sparse-navigation">3. Dense Reward Shaping for Sparse Navigation<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#3-dense-reward-shaping-for-sparse-navigation" class="hash-link" aria-label="Direct link to 3. Dense Reward Shaping for Sparse Navigation" title="Direct link to 3. Dense Reward Shaping for Sparse Navigation" translate="no">​</a></h2>
<p>A fundamental challenge in mobile robotics is <strong>sparse reward feedback</strong>. In a 1.2m × 1.2m arena, moving randomly yields zero reward until the robot coincidentally intersects the $0.10m$ goal tolerance.</p>
<p>To guide convergence, we engineered a <strong>Dense Reward Shaping</strong> schedule inside the Webots controller (<code>RL_controller.py</code>):</p>
<table><thead><tr><th style="text-align:left">Reward Component</th><th style="text-align:center">Value</th><th style="text-align:left">Design Objective &amp; Physical Effect</th></tr></thead><tbody><tr><td style="text-align:left"><strong>Goal Reached (<code>REWARD_GOAL</code>)</strong></td><td style="text-align:center"><strong>+1.0</strong></td><td style="text-align:left">Terminal reward on successful target arrival</td></tr><tr><td style="text-align:left"><strong>Wall/Obstacle Collision (<code>REWARD_COLLISION</code>)</strong></td><td style="text-align:center"><strong>-1.0</strong></td><td style="text-align:left">Severe penalty on physical impact</td></tr><tr><td style="text-align:left"><strong>Distance Delta Reward (<code>REWARD_DELTA_SCALE</code>)</strong></td><td style="text-align:center"><strong>+0.1 × Δdist</strong></td><td style="text-align:left">Immediate feedback proportional to Euclidean distance reduction</td></tr><tr><td style="text-align:left"><strong>Step Time Penalty (<code>REWARD_STEP</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">Prevents idling and incentivizes shortest paths</td></tr><tr><td style="text-align:left"><strong>Turning Penalty (<code>REWARD_TURN</code>)</strong></td><td style="text-align:center"><strong>-0.01</strong></td><td style="text-align:left">Suppresses unproductive spinning behavior</td></tr><tr><td style="text-align:left"><strong>Backward Penalty (<code>REWARD_BACKWARD</code>)</strong></td><td style="text-align:center"><strong>-0.05</strong></td><td style="text-align:left">Discourages inefficient reverse driving</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-mathematical-principle-of-distance-delta">🔍 Mathematical Principle of Distance Delta<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#-mathematical-principle-of-distance-delta" class="hash-link" aria-label="Direct link to 🔍 Mathematical Principle of Distance Delta" title="Direct link to 🔍 Mathematical Principle of Distance Delta" translate="no">​</a></h3>
<p><strong>Δdist = dist_prev - dist_curr</strong></p>
<ul>
<li class="">Driving $0.05m$ toward the target awards $+0.1 \times 0.05 = +0.005$ immediately.</li>
<li class="">Conversely, driving away incurs an instant penalty, allowing the agent to align its heading with the target within the first 100 episodes.</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-empirical-evaluation-reward-and-success-curves">4. Empirical Evaluation: Reward and Success Curves<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#4-empirical-evaluation-reward-and-success-curves" class="hash-link" aria-label="Direct link to 4. Empirical Evaluation: Reward and Success Curves" title="Direct link to 4. Empirical Evaluation: Reward and Success Curves" translate="no">​</a></h2>
<p>Training for 1,500 episodes in the <code>no_obstacle</code> world demonstrated remarkable convergence matching our theoretical expectations.</p>
<p><img decoding="async" loading="lazy" alt="Success Rate Curve" src="https://globaldevlog.com/assets/images/02_success_curve-a47390e774a7a40ace8869f29884f2bd.png" width="800" height="400" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-convergence-milestones">📈 Convergence Milestones<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#-convergence-milestones" class="hash-link" aria-label="Direct link to 📈 Convergence Milestones" title="Direct link to 📈 Convergence Milestones" translate="no">​</a></h3>
<ol>
<li class=""><strong>Episodes 0 ~ 200 (Exploration Phase)</strong>:<!-- -->
<ul>
<li class="">Frequent wall collisions and 150-step timeouts keep the moving average reward in the negative range (-0.4 ~ -0.2).</li>
</ul>
</li>
<li class=""><strong>Episodes 200 ~ 500 (Ascent Phase)</strong>:<!-- -->
<ul>
<li class="">As distance-delta rewards propagate through the Q-Table, the policy establishes a strong forward-driving preference whenever <code>goal_heading</code> is centered.</li>
<li class="">Success rate surges from 20% to <strong>over 80%</strong>.</li>
</ul>
</li>
<li class=""><strong>Episodes 500 ~ 1,500 (Asymptotic Convergence)</strong>:<!-- -->
<ul>
<li class="">With ε settled at 0.05, average steps per episode plummet from 150 (timeout) to <strong>15 ~ 22 steps</strong>.</li>
<li class="">The 100-episode moving average success rate converges above <strong>98.5%</strong>.</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next">5. What's Next?<a href="https://globaldevlog.com/blog/2026-05-18-rl-qlearning-2#5-whats-next" class="hash-link" aria-label="Direct link to 5. What's Next?" title="Direct link to 5. What's Next?" translate="no">​</a></h2>
<p>We have proven that Tabular Q-Learning over 648 states solves empty arena navigation cleanly.</p>
<p>However, <strong>real-world environments are packed with walls and obstacles</strong>. In Episode #3, we will investigate the <strong><code>obstacle12</code> and <code>obstacle_hard</code> maps</strong>, examining how our agent handles local minima traps and running multi-seed parallel training!</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="reward-shaping" term="reward-shaping"/>
        <category label="webots" term="webots"/>
        <category label="robotics" term="robotics"/>
        <category label="machine-learning" term="machine-learning"/>
        <category label="python" term="python"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #19] Automating Reports and Backend Database (DB) Troubleshooting]]></title>
        <id>https://globaldevlog.com/blog/2026-05-17-maeumjigi-19</id>
        <link href="https://globaldevlog.com/blog/2026-05-17-maeumjigi-19"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Automating evaluation data aggregation and generating presentation-ready slide decks using python-pptx for our capstone midterm defense.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-12712349ddac15465e60572918ef40de.png" width="720" height="405" class="img_ev3q"></p>
<p>I must correct my previous assumption that "May 13th was the last of Maeumjigi's May development"! On May 17th, a massive wave of backend integration testing and report drafting hit us.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="automating-the-fine-tuning-report-using-artificial-intelligence-ai">Automating the Fine-Tuning Report using Artificial Intelligence (AI)<a href="https://globaldevlog.com/blog/2026-05-17-maeumjigi-19#automating-the-fine-tuning-report-using-artificial-intelligence-ai" class="hash-link" aria-label="Direct link to Automating the Fine-Tuning Report using Artificial Intelligence (AI)" title="Direct link to Automating the Fine-Tuning Report using Artificial Intelligence (AI)" translate="no">​</a></h3>
<p>There was simply not enough time for a human to manually analyze the massive result graphs from the 2nd fine-tuning (<code>fine_tuning_2</code>).</p>
<blockquote>
<p>"Create a prompt to write a report comparing the inference results of the models. Save it as report_prompt.md and write the report."</p>
</blockquote>
<p>I attempted prompt engineering to have the AI interpret the metrics and automatically draft the report. Ultimately, the AI analyzed the pros and cons of each model and the differences based on Alpha values, generating an excellent Word (.docx) report draft.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="backend-db-integration-and-the-swamp-of-401-unauthorized">Backend DB Integration and the Swamp of 401 Unauthorized<a href="https://globaldevlog.com/blog/2026-05-17-maeumjigi-19#backend-db-integration-and-the-swamp-of-401-unauthorized" class="hash-link" aria-label="Direct link to Backend DB Integration and the Swamp of 401 Unauthorized" title="Direct link to Backend DB Integration and the Swamp of 401 Unauthorized" translate="no">​</a></h3>
<p>Right after the report, I started pushing <code>backup.sql</code> data into the local PostgreSQL DB to test the system stability of the frontend and backend.</p>
<blockquote>
<p>"I logged in successfully after running the backend and frontend, but nothing responds when I click menus."
"Email or password incorrect... INFO: POST /auth/login 401 Unauthorized"</p>
</blockquote>
<p>I successfully pushed the DB dump and accessed the web, but fell into a hell of <code>401 Unauthorized</code> login errors. I created <code>debug_login.py</code> and endlessly restarted the uvicorn server for troubleshooting.</p>
<p><img decoding="async" loading="lazy" alt="DB Troubleshooting" src="https://globaldevlog.com/assets/images/02_db-1d0bb76370f8508377e9472ff5cb15c8.png" width="1373" height="5225" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="contemplating-knowledge-graphs">Contemplating Knowledge Graphs<a href="https://globaldevlog.com/blog/2026-05-17-maeumjigi-19#contemplating-knowledge-graphs" class="hash-link" aria-label="Direct link to Contemplating Knowledge Graphs" title="Direct link to Contemplating Knowledge Graphs" translate="no">​</a></h3>
<blockquote>
<p>"Techniques using ontology and knowledge graphs are rising. Using these might create a chatbot with better stability than just focusing on a single Large Language Model (LLM)."</p>
</blockquote>
<p>Late at night, to overcome the limitations of simple LLM fine-tuning, I began designing <code>chatbot_design_prompt.md</code> to explore fusing ontologies and Knowledge Graphs. As we pass mid-May, the scale of the project is growing massive!</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="report" term="report"/>
        <category label="backend" term="backend"/>
        <category label="DB" term="DB"/>
        <category label="troubleshooting" term="troubleshooting"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Webots Robotics] Q-Learning Autonomous Navigation: 648-Bin State Discretization & Socket Architecture (#1)]]></title>
        <id>https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1</id>
        <link href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1"/>
        <updated>2026-05-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Designing a 648-bin state space discretizer and a TCP socket IPC architecture for Webots mobile robot autonomous navigation with tabular Q-Learning.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Webots Simulation Environment" src="https://globaldevlog.com/assets/images/01_webots_arena-b49bd821fb90ed1d7e619add024bb5e7.png" width="1721" height="1033" class="img_ev3q"></p>
<blockquote>
<p>📌 <strong>Key Takeaways (TL;DR)</strong></p>
<ul>
<li class=""><strong>Problem</strong>: Continuous sensor readings (distance &amp; heading) cause a state-space explosion for tabular Q-learning, and synchronous IPC with the Webots simulator requires low-latency coordination.</li>
<li class=""><strong>Solution</strong>: Designed a geometrically partitioned 5-dimensional continuous sensor mapper producing 3 × 8 × 3 × 3 × 3 = 648 discrete states (<code>discretizer.py</code>) and a line-delimited JSON IPC socket pipeline over TCP port 5000.</li>
<li class=""><strong>Result</strong>: Achieved O(1) fast Q-Table indexing and reliable 32ms physics synchronization between the simulator and Python agent.</li>
</ul>
</blockquote>
<!-- -->
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-project-background-why-webots-and-q-learning">1. Project Background: Why Webots and Q-Learning?<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#1-project-background-why-webots-and-q-learning" class="hash-link" aria-label="Direct link to 1. Project Background: Why Webots and Q-Learning?" title="Direct link to 1. Project Background: Why Webots and Q-Learning?" translate="no">​</a></h2>
<p>Enabling an autonomous differential-drive robot to navigate an unknown indoor arena and reach a target while avoiding obstacles is a foundational robotics challenge. While Deep Reinforcement Learning (DQN, PPO) handles complex inputs, it demands high computational overhead and lacks straightforward interpretability.</p>
<p>In this project, we apply <strong>classical Tabular Q-Learning (Off-policy TD(0))</strong> to a mobile robot in the Webots 3D physics engine. Our objective is to establish an optimal, mathematically verifiable navigation and obstacle avoidance policy in a 1.2m × 1.2m indoor arena.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-system-architecture-webots-controller--python-rl-engine">2. System Architecture: Webots Controller ↔ Python RL Engine<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#2-system-architecture-webots-controller--python-rl-engine" class="hash-link" aria-label="Direct link to 2. System Architecture: Webots Controller ↔ Python RL Engine" title="Direct link to 2. System Architecture: Webots Controller ↔ Python RL Engine" translate="no">​</a></h2>
<p>The system is decoupled into two clean tiers: the <strong>Webots simulation server</strong> and the <strong>Python RL client</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 Simulator (localhost:5000)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Robot["Supervisor Robot&lt;br&gt;(Ultrasonic Sensor + Wheels)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        HeadScan["Head-scan Mechanism&lt;br&gt;(Left/Right 1.1 rad servo)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Server["TCP Socket Server&lt;br&gt;(Line-delimited 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 RL Engine (Client)"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Env["WebotsEnv (gym-like)&lt;br&gt;reset() / step()"]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Disc["Discretizer&lt;br&gt;(Continuous → 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;-→|"request: {type: step, action: 0..3}&lt;br&gt;response: {state: [...], reward, done}"| Env</span><br></div></code></pre></div></div>
<ul>
<li class=""><strong>Webots Controller (<code>RL_controller.py</code>)</strong>: Executes action commands for 480ms (15 simulation ticks × 32ms) and rotates a head-mounted ultrasonic sensor left and right by 1.1 rad ($\approx 63°$) to eliminate blind spots.</li>
<li class=""><strong>Socket Protocol</strong>: Lightweight line-delimited JSON over TCP localhost:5000:<!-- -->
<ul>
<li class="">Request: <code>{"type": "step", "action": 0}</code> (0: Forward, 1: Backward, 2: Turn Left, 3: Turn Right)</li>
<li class="">Response: <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-discretizing-continuous-sensors-into-648-states">3. Discretizing Continuous Sensors into 648 States<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#3-discretizing-continuous-sensors-into-648-states" class="hash-link" aria-label="Direct link to 3. Discretizing Continuous Sensors into 648 States" title="Direct link to 3. Discretizing Continuous Sensors into 648 States" translate="no">​</a></h2>
<p>To apply tabular Q-learning, continuous floats must be mapped into finite discrete states. We selected 5 crucial observation variables based on kinematic relevance:</p>
<table><thead><tr><th style="text-align:left">Feature</th><th style="text-align:center">Bins</th><th style="text-align:left">Thresholds</th><th style="text-align:left">Semantic Meaning</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 sectors, 45° each)</td><td style="text-align:left">Front, Front-Right, Right, Back-Right, Back, Back-Left, Left, Front-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/Collision, 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">Danger, Caution, Safe</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">Danger, Caution, Safe</td></tr></tbody></table>
<p>Total State Count:
<strong>N_states = 3 × 8 × 3 × 3 × 3 = 648</strong></p>
<p>The resulting Q-Table requires only <strong>648 × 4 = 2,592 floating-point entries</strong>, allowing sub-millisecond memory updates and zero GPU overhead.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-state-discretization-code-discretizerpy">💻 State Discretization Code (<code>discretizer.py</code>)<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#-state-discretization-code-discretizerpy" class="hash-link" aria-label="Direct link to -state-discretization-code-discretizerpy" title="Direct link to -state-discretization-code-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"># Dimension boundaries</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 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">"""Maps 5-tuple continuous sensor readings to an integer in [0, 647]."""</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"># Compute flattened 1D index</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-head-scan-mechanism--collision-logic">4. Head-Scan Mechanism &amp; Collision Logic<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#4-head-scan-mechanism--collision-logic" class="hash-link" aria-label="Direct link to 4. Head-Scan Mechanism &amp; Collision Logic" title="Direct link to 4. Head-Scan Mechanism &amp; Collision Logic" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="System Architecture" src="https://globaldevlog.com/assets/images/02_architecture-83a0cdfb8297ac248797b32bbc7fd69b.png" width="2000" height="900" class="img_ev3q"></p>
<p>A fixed forward-facing sensor leaves lateral blind spots. We implemented an active <strong>Head-Scan routine</strong>:</p>
<ol>
<li class=""><strong>Step Execution</strong>: Drive motors at 3.0 rad/s for 480ms.</li>
<li class=""><strong>Head Rotation</strong>: Sweep head servo +1.1 rad to read left distance, then -1.1 rad for right distance.</li>
<li class=""><strong>Re-center</strong>: Return head to 0 rad and refresh front distance.</li>
<li class=""><strong>Terminal Conditions</strong>:<!-- -->
<ul>
<li class=""><strong>Goal Reached</strong>: Euclidean distance ≤ 0.10m → success (<code>done = True</code>, reward $+1.0$)</li>
<li class=""><strong>Collision</strong>: Any distance ≤ 0.08m → failure (<code>done = True</code>, reward $-1.0$)</li>
</ul>
</li>
</ol>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-whats-next">5. What's Next?<a href="https://globaldevlog.com/blog/2026-05-17-rl-qlearning-1#5-whats-next" class="hash-link" aria-label="Direct link to 5. What's Next?" title="Direct link to 5. What's Next?" translate="no">​</a></h2>
<p>With the simulator environment, TCP socket IPC, and 648-bin state discretization established, our agent is ready for reinforcement learning.</p>
<p>In Episode #2, we will cover the <strong>mathematical implementation of Tabular Q-Learning, ε-greedy exploration decay, and reward function tuning</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 Capstone Dev Log #18] Expanding Inference Tokens and Comparing 2nd Fine-Tuning Results]]></title>
        <id>https://globaldevlog.com/blog/2026-05-13-maeumjigi-18</id>
        <link href="https://globaldevlog.com/blog/2026-05-13-maeumjigi-18"/>
        <updated>2026-05-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Expanding inference max_new_tokens to 384 to eliminate stiff, clipped answers and analyzing the impact of LoRA alpha (8 vs 16) on empathetic depth.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-4dcde4cef4209e81d49b5f5306af009d.png" width="1484" height="888" class="img_ev3q"></p>
<p>On May 13th, after fixing the hardcoding bugs discovered yesterday, we finally resumed proper inference testing for the 2nd round fine-tuned models.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-cause-of-short-answers-increase-the-token-length">The Cause of Short Answers: Increase the Token Length!<a href="https://globaldevlog.com/blog/2026-05-13-maeumjigi-18#the-cause-of-short-answers-increase-the-token-length" class="hash-link" aria-label="Direct link to The Cause of Short Answers: Increase the Token Length!" title="Direct link to The Cause of Short Answers: Increase the Token Length!" translate="no">​</a></h3>
<p>While meticulously reviewing the inference results, I felt that the answers from the <code>Llama33-70B_a8</code> model were still strangely short and rigid.</p>
<blockquote>
<p>"This is one of Llama33-70B_a8's answers. I feel that max_seq_length and max_new_tokens are too small. I want to increase them. Looking at the VRAM status, how much can I increase them?"</p>
</blockquote>
<p>After calculating the available VRAM space (39.8GB at the time) with the Artificial Intelligence (AI), we boldly increased <code>max_new_tokens</code> (the maximum number of words generated) to 384. The result was a massive success!</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">[Before] "It must be really hard to feel that way. It is important to talk to a professional."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[After] "It must be really hard to feel that way. However, having these thoughts is not a sign that you are weak, but a signal that you are carrying too much burden right now. I highly recommend slowly unraveling these emotions with a professional."</span><br></div></code></pre></div></div>
<p>By simply increasing the token allowance, the model maintained its context to the end and began pouring out much deeper and warmer sentences of empathy. The model had been holding its tongue because it was trapped in a limited token count.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="alpha-value-comparison-analysis-a8-vs-a16">Alpha Value Comparison Analysis (a=8 vs a=16)<a href="https://globaldevlog.com/blog/2026-05-13-maeumjigi-18#alpha-value-comparison-analysis-a8-vs-a16" class="hash-link" aria-label="Direct link to Alpha Value Comparison Analysis (a=8 vs a=16)" title="Direct link to Alpha Value Comparison Analysis (a=8 vs a=16)" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Graph Visualization" src="https://globaldevlog.com/assets/images/02_graph-feb5d1991f44dd7f378232f6b3515752.png" width="2053" height="1460" class="img_ev3q"></p>
<p>The results for the biggest objective of the 2nd fine-tuning—the performance difference based on the LoRA hyperparameter <code>alpha</code>—were also out. By analyzing the logs piled up in the <code>fine_tuning_2</code> folder, we generated graphs like <code>05_alpha_comparison_daily.png</code>. With <code>rank=16</code> fixed, we clearly visualized how the model's overfitting patterns and final answer quality differed when given <code>alpha=8</code> versus <code>alpha=16</code>.</p>
<p>Ultimately, models granted the correct hyperparameters and ample token length were finally getting closer to the true 'Maeumjigi' (Heart-keeper) we had originally planned. It seems our mid-term presentation preparations will wrap up very smoothly!</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="inference" term="inference"/>
        <category label="max_new_tokens" term="max_new_tokens"/>
        <category label="empathy" term="empathy"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #17] 2nd Fine-Tuning Complete & The Trap of the Automation Pipeline]]></title>
        <id>https://globaldevlog.com/blog/2026-05-12-maeumjigi-17</id>
        <link href="https://globaldevlog.com/blog/2026-05-12-maeumjigi-17"/>
        <updated>2026-05-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Executing 2nd-round fine-tuning with clean chat templates and refined LoRA hyperparameters (rank and alpha) to optimize learning convergence.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-14a3c0d517e4c85b93ada4e973112325.png" width="2000" height="1457" class="img_ev3q"></p>
<p>The 2nd round of fine-tuning (<code>fine_tuning_2</code>) parallel training, which was ambitiously started to correct the LoRA hyperparameter mistake (alpha/rank setup error), finally ended on May 12th!</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="extracting-the-best-checkpoint-and-building-the-inference-pipeline">Extracting the Best Checkpoint and Building the Inference Pipeline<a href="https://globaldevlog.com/blog/2026-05-12-maeumjigi-17#extracting-the-best-checkpoint-and-building-the-inference-pipeline" class="hash-link" aria-label="Direct link to Extracting the Best Checkpoint and Building the Inference Pipeline" title="Direct link to Extracting the Best Checkpoint and Building the Inference Pipeline" translate="no">​</a></h3>
<p>The fine-tuning for 3 models with 6 hyperparameter combinations was all completed. I immediately compared the <code>eval_loss</code> to extract the best performing checkpoints and moved them to the <code>adapter</code> folders for each model.</p>
<p>I also created a new <code>run_test_20_30.sh</code> script to run evaluation and result comparison all at once in the background on the server.</p>
<blockquote>
<p>"Currently, the HCX-Text-1p5B models generated 12 outputs in 15 minutes. GPU Util is 1.6GB per graphics card..."</p>
</blockquote>
<p>However, the inference speed was still hopeless. Even the lightest 1.5B model couldn't even generate 1 answer per minute. The VRAM usage was only 1.6GB, meaning a bottleneck occurred because the Batch Size was set to 1. On top of this, server storage issues forced us to urgently migrate the project's base path from <code>~/home/</code> to <code>~/home/data/temporary/</code>, causing a huge fuss to modify all script paths.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="falling-into-the-trap-of-hardcoding">Falling into the Trap of Hardcoding<a href="https://globaldevlog.com/blog/2026-05-12-maeumjigi-17#falling-into-the-trap-of-hardcoding" class="hash-link" aria-label="Direct link to Falling into the Trap of Hardcoding" title="Direct link to Falling into the Trap of Hardcoding" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Hardcoding Bug" src="https://globaldevlog.com/assets/images/02_bug-900bd56611b3d6521891bcc956843259.png" width="2000" height="1457" class="img_ev3q"></p>
<p>After painstakingly running all the evaluation codes, I discovered a horrifying fact.</p>
<blockquote>
<p>"Why is MODEL_ALIAS hardcoded to 'Qwen35-9B_a16' in 20_eval_ft_models.ipynb?"
"So in the server logs, even for 20_eval_HCX-Text-1p5B_a8.ipynb, MODEL_ALIAS was defined as 'Qwen35-9B_a16'. You're telling me it's normal that the filename and model name are different?"</p>
</blockquote>
<p>Yes. While writing the evaluation automation script, I accidentally hardcoded <code>MODEL_ALIAS</code>. The script diligently copied files to test the HCX model, but inside the code, it kept repeatedly loading only the <code>Qwen35-9B_a16</code> model and doing the same inference over and over!</p>
<p>It was a day that made me realize once again that fine-tuning is not a science, but a battle of meticulousness. I'll have to fix this hardcoded bug and run the inferences again tomorrow.</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="best-performance" term="best-performance"/>
        <category label="checkpoint" term="checkpoint"/>
        <category label="hardcoding" term="hardcoding"/>
        <category label="capstone" term="capstone"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #16] Fatal Mistake, 2nd Fine-Tuning, and React Native App Launch]]></title>
        <id>https://globaldevlog.com/blog/2026-05-08-maeumjigi-16</id>
        <link href="https://globaldevlog.com/blog/2026-05-08-maeumjigi-16"/>
        <updated>2026-05-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Diagnosing prompt template pollution hardcoded into training dialogues and performing rapid debugging to restore dataset formatting integrity.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-9eab7db0dac5c88d9a120a99e5e703d4.png" width="1874" height="1526" class="img_ev3q"></p>
<p>May 8th was the day we were building our capstone design mid-term presentation (PPT) based on the fine-tuning results. However, while happily filling in the slides, I discovered a massive, chilling mistake.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="discovering-the-fatal-lora-hyperparameter-mistake">Discovering the Fatal LoRA Hyperparameter Mistake<a href="https://globaldevlog.com/blog/2026-05-08-maeumjigi-16#discovering-the-fatal-lora-hyperparameter-mistake" class="hash-link" aria-label="Direct link to Discovering the Fatal LoRA Hyperparameter Mistake" title="Direct link to Discovering the Fatal LoRA Hyperparameter Mistake" translate="no">​</a></h3>
<p>I was refining the model structure diagrams and hyperparameter info with the Artificial Intelligence (AI) for the PPT.</p>
<blockquote>
<p>"Looking at the fine-tuning examples, the answers are shorter than the base model. I think this is because setting LoRA's r to 8 and alpha to 16 reduced the intermediate features too much..."
"Wait, it was a/r? I thought it was r/a...??? I really applied LoRA greedily..."</p>
</blockquote>
<p>When fine-tuning with LoRA, it's standard to set the <code>alpha</code> value equal to or twice the <code>rank</code> value. However, I completely misunderstood the scaling ratio equation, setting it in a way that either severely suppressed or excessively amplified the weight updates. No wonder the fine-tuned models' answers were unnaturally short and chopped off—the fault lay right at my fingertips.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-decision-for-2nd-round-fine-tuning-fine_tuning_2">The Decision for 2nd Round Fine-Tuning (fine_tuning_2)<a href="https://globaldevlog.com/blog/2026-05-08-maeumjigi-16#the-decision-for-2nd-round-fine-tuning-fine_tuning_2" class="hash-link" aria-label="Direct link to The Decision for 2nd Round Fine-Tuning (fine_tuning_2)" title="Direct link to The Decision for 2nd Round Fine-Tuning (fine_tuning_2)" translate="no">​</a></h3>
<p>Although we already had the 1st round results, we couldn't just move on for a perfect 'Maeumjigi'. I immediately created a <code>fine_tuning_2</code> folder and decided on a 2nd round of fine-tuning.</p>
<blockquote>
<p>"The models to use this time are HCX-Text-1p5B, Llama33-70B, and Qwen35-9B. Train on MIG-0 with rank=16, alpha=16, and on MIG-1 with rank=16, a=8."
"Extract the top 2000 longest entries from Daily_train_dataset.jsonl and create a file named Daily_train_dataset_long.jsonl."</p>
</blockquote>
<p>To prevent the answers from becoming too short, we extracted only the top 2000 datasets with the longest text length. We also split our MIG (Multi-Instance GPU) resources to run parallel training, directly comparing the performance differences based on the rank and alpha ratios.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="starting-frontend-app-development-while-the-backend-trains">Starting Frontend (App) Development While the Backend Trains<a href="https://globaldevlog.com/blog/2026-05-08-maeumjigi-16#starting-frontend-app-development-while-the-backend-trains" class="hash-link" aria-label="Direct link to Starting Frontend (App) Development While the Backend Trains" title="Direct link to Starting Frontend (App) Development While the Backend Trains" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="App Development" src="https://globaldevlog.com/assets/images/02_app-9efe68797b061b7eda0d68995627a8cc.png" width="1476" height="1526" class="img_ev3q"></p>
<p>While the models fell back into the cycle of training, I didn't just sit around. I finally started developing the frontend app where actual users will meet 'Maeumjigi'!</p>
<blockquote>
<p>"I need to show a QR code to run maeum-app... but when I type npx go start, I only get an error message. How and where can the QR code be shown?"</p>
</blockquote>
<p>I created the <code>maeum-app</code> project using React Native (Expo) and struggled to launch the local server and connect it to my smartphone. I even mistakenly typed <code>npx go start</code> instead of <code>expo</code>, but I finally succeeded in pulling up the Expo QR code and setting up the app icons (<code>assets/logo.png</code>). It was a thrilling day where the brain of the backend (AI) and the face of the frontend (App) were being built simultaneously.</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="LoRA" term="LoRA"/>
        <category label="hyperparameters" term="hyperparameters"/>
        <category label="ReactNative" term="ReactNative"/>
        <category label="app-development" term="app-development"/>
        <category label="frontend" term="frontend"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[[Maeumjigi Capstone Dev Log #15] Visualizing Fine-Tuning Results of 5 Models & Local Environment Setup]]></title>
        <id>https://globaldevlog.com/blog/2026-05-07-maeumjigi-15</id>
        <link href="https://globaldevlog.com/blog/2026-05-07-maeumjigi-15"/>
        <updated>2026-05-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Analyzing Perplexity and dialogue response scores across models, and building an automated matplotlib visualization pipeline for mid-term evaluation.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" alt="Cover Image" src="https://globaldevlog.com/assets/images/01_cover-b5ad981f0a0602b8afaf1bdbc982094d.png" width="1181" height="731" class="img_ev3q"></p>
<p>May 7th was the day we synthesized the fine-tuning results of our 5 models (Llama3.3-70B, DSR1-70B, HCX-Omni-8B, Qwen35-9B, HCX-Text-1.5B) and proceeded with full-scale visualization and local environment setup.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="visualization-and-local-setup-for-model-comparison">Visualization and Local Setup for Model Comparison<a href="https://globaldevlog.com/blog/2026-05-07-maeumjigi-15#visualization-and-local-setup-for-model-comparison" class="hash-link" aria-label="Direct link to Visualization and Local Setup for Model Comparison" title="Direct link to Visualization and Local Setup for Model Comparison" translate="no">​</a></h3>
<p>To comfortably review the result comparison code (which was previously running on the heavy server) on my personal laptop, I created a new notebook, <code>31_comparison_local.ipynb</code>.</p>
<blockquote>
<p>"Can't 30_comparison.ipynb run on my laptop too?"
"Please tell me how to install pandas, matplotlib, seaborn, and bert_score on the currently selected kernel."</p>
</blockquote>
<p>While setting up the local Jupyter kernel in VSCode and installing the necessary libraries, I faced frequent Kernel Crashes and annoying errors like <code>ModuleNotFoundError: No module named 'ft_utils'</code>. However, I resolved them step-by-step by fixing folder paths. I also successfully recovered the missing <code>ft_metrics.json</code> files for some models (like DSR1-70B) using a script.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-model-comprehensive-report-and-graph-visualization">5-Model Comprehensive Report and Graph Visualization<a href="https://globaldevlog.com/blog/2026-05-07-maeumjigi-15#5-model-comprehensive-report-and-graph-visualization" class="hash-link" aria-label="Direct link to 5-Model Comprehensive Report and Graph Visualization" title="Direct link to 5-Model Comprehensive Report and Graph Visualization" translate="no">​</a></h3>
<p>The most important task was visualizing the performance of these 5 models so they could be compared at a glance. Together with the AI, we established a plan named <code>5MODELS_ORGANIZATION_PLAN_present.md</code> and began systematically organizing the data.</p>
<blockquote>
<p>"What is the difference between the faint line and the thick line? Bring the training loss and eval loss for each model and visualize them as graphs."
"Remove the thick line and make the faint line clearer and save it."</p>
</blockquote>
<p>I removed the unnecessary trend lines (thick lines) from the matplotlib graphs generated by the AI, finally extracting <code>03_training_loss_all.png</code> and <code>04_eval_loss_all.png</code> which neatly gathered the Training and Eval Loss curves of all models in one place. We also perfectly documented graphs analyzing the LoRA Rank Decomposition for each model, as well as a GPU memory usage chart (<code>02_gpu_memory.png</code>).</p>
<p><img decoding="async" loading="lazy" alt="Graph Visualization" src="https://globaldevlog.com/assets/images/02_graph-8f91ee1a78e9220a703c6884ad9a4f40.png" width="1785" height="735" class="img_ev3q"></p>
<p>Now, based on these graphs, we can beautifully decorate our capstone design mid-term presentation materials for next week. Which of these 5 models will ultimately be selected as the best quality 'Maeumjigi' chatbot?</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="results-analysis" term="results-analysis"/>
        <category label="visualization" term="visualization"/>
        <category label="Jupyter" term="Jupyter"/>
        <category label="capstone" term="capstone"/>
        <category label="Artificial Intelligence (AI)" term="Artificial Intelligence (AI)"/>
    </entry>
</feed>