<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://yucol.top/feed.xml" rel="self" type="application/atom+xml" /><link href="https://yucol.top/" rel="alternate" type="text/html" /><updated>2026-04-04T02:25:31+00:00</updated><id>https://yucol.top/feed.xml</id><title type="html">Yucol</title><subtitle>个人的一个技术博客站点，主要用于记录个人在学习过程中遇到的技术问题及解决方法、以及一些比较有趣的事情。</subtitle><author><name>yucol</name></author><entry><title type="html">我的现场编年史：不只是听歌！</title><link href="https://yucol.top/life/concert.html" rel="alternate" type="text/html" title="我的现场编年史：不只是听歌！" /><published>2026-02-25T04:00:00+00:00</published><updated>2026-02-25T04:00:00+00:00</updated><id>https://yucol.top/life/concert</id><content type="html" xml:base="https://yucol.top/life/concert.html"><![CDATA[<h1 id="-我的演唱会流浪计划">🚀 我的演唱会流浪计划</h1>

<hr />
<h3 id="-no001--房东的猫---世界青年-2024巡回演唱会">🎵 NO.[001] | [房东的猫] - [世界/青年 2024巡回演唱会]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2024 年 06 月 29 日<br />
<strong>📍 地点：</strong> [北京] · [首都体育馆]<br />
<strong>🎫 座位：</strong> [ 看台山顶 ]
<strong>🏷️ 关键词：</strong> #第一次演唱会 #合唱 #太治愈了</p>
</blockquote>

<h4 id="1--content">1. 📜 Content</h4>

<p>到北京上学后，有机会看到了人生中的第一次演唱会。那个周末是最开心的时候（周六听房东的猫，周日看成果🥳），从此激起了对演唱会的热爱。<del>直到现在工作后的平淡无趣</del></p>

<h4 id="2--memory">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/03c35fc6b5402510d671a28704338784.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/b08933f2fec09709c5e4ce5c72c7b710.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/6dc51b2e507b801b2d4e1237202e5d50.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/4f880cf7da00e9bf01004dac6542c814.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/ecfa47753c2c0d15679f02a36afb924c.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/eb23d7c5e5d14d200732fbcd4574e803.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/6af2fb0d633f677461f94a63cc649083.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/52158116f13f17fac1f55f5cc1313a47.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/04d872bbc0a16cfc83e5431853dbd6e4.webp" alt="" /></p>

<h4 id="3--个人评分">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />
<h3 id="-no002--成果---海王星飞船">🎵 NO.[002] | [成果] - [海王星飞船]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2024 年 06 月 30 日<br />
<strong>📍 地点：</strong> [北京] · [晓剧场]<br />
<strong>🎫 座位：</strong> [ 3排 16号 ]<br />
<strong>🏷️ 关键词：</strong> #音乐 #舞台剧 #合影（线下真实版）</p>
</blockquote>

<h4 id="1--content-1">1. 📜 Content</h4>

<p>狗哥的第一次舞台剧（成年儿童版😄），虽然不是演唱会，但却是第一次线下见面。</p>

<h4 id="2--memory-1">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/09010902d25eba6b5e0d4c03d192dc18.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/3a61aeeadff176eade7bf7c7a1ffafbe.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/71176807134ee73c254b0ae91fe8f165.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/25f11ae3934295c98afea56033a64f2c.webp" alt="" /></p>

<h4 id="3--个人评分-1">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>演员状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />

<h3 id="-no003--夏日入侵企画---温暖人心的冬日聚会-不插电特别专场">🎵 NO.[003] | [夏日入侵企画] - [温暖人心的冬日聚会-不插电特别专场]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2024 年 12 月 26 日<br />
<strong>📍 地点：</strong> [北京] · [福浪LIVEHOUSE-福]<br />
<strong>🎫 座位：</strong> [ Live House ]
<strong>🏷️ 关键词：</strong> #欢乐 #蹦迪 #近距离</p>
</blockquote>

<h4 id="1--content-2">1. 📜 Content</h4>

<p>首次参加live house，夏企的魅力太足了，从开头蹦到结尾！我去的比较早，所以抢到了比较靠前的位置 <del>（早去早罚站）</del>。</p>

<h4 id="2--memory-2">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/d30bfd4a68f4dc0b0ae8ea722758091e.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/9ce5697d3c3a05e611248f2a1756fb61.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/a07dfafb65d1067e68bacb9cb98fb3e2.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/9214b0d4ecb795c553c7a016c6e0502f.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/0423acd25e4b2249b3d44caadcbe9d6a.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/510149ef4a309514aba2e355bde0f8e8.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/fb7ee9c7157b2475f11d861938ce9642.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/e550a42cc6d94c5b7866bfebdd73e16e.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/15218fcce6159777c7985751a353f7b6.webp" alt="" /></p>

<h4 id="3--个人评分-2">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />

<h3 id="-no004--双笙---经过时你我相遇">🎵 NO.[004] | [双笙] - [经过时你我相遇]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2024 年 12 月 30 日<br />
<strong>📍 地点：</strong> [北京] · [北京展览馆剧场]<br />
<strong>🎫 座位：</strong> [ 一层 27排 74号 ]
<strong>🏷️ 关键词：</strong> #国风 #老粉</p>
</blockquote>

<h4 id="1--content-3">1. 📜 Content</h4>

<p>大学时特别喜欢<strong>双笙</strong>的歌曲，声音清甜，歌曲时而温柔，时而清脆。这次演唱会开到了北京，终于有机会可以现场听她唱的歌。</p>

<p>现场的人数没有满座，但是也有很多人来捧场，上次关注双笙的时间我已经记不清了，我的记忆里还是她之前唱过的歌，所以演唱会上很多歌我都不太熟悉，算是个小遗憾吧。</p>

<h4 id="2--memory-3">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/57e8bbeebc68f673226fc632cdf1af5e.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/7dc997c6941d520ae27eaa5c99089d9d.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/9d43b58f0aabe1f2cf45910caa1d968f.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/bf662e347ad4267f56099d337ac1e3d1.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/5cbd4184524954e55f8c0aa403f5a525.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/3d11d108aab3971c2f81224e7eedbd07.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/9b495142e3b4af457bd9bdcd9123036a.webp" alt="" /></p>

<h4 id="3--个人评分-3">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />

<h3 id="-no005--张杰---未live---开往1982">🎵 NO.[005] | [张杰] - [未·LIVE - 开往1982]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2025 年 04 月 19 日<br />
<strong>📍 地点：</strong> [北京] · [国家体育场-鸟巢]<br />
<strong>🎫 座位：</strong> [ G区 529通道 五层 19排 25号 ]
<strong>🏷️ 关键词：</strong> #逆战 #哪个男孩子不想现场听张杰唱逆战 #直接给到夯</p>
</blockquote>

<h4 id="1--content-4">1. 📜 Content</h4>

<p>遥想12年的夏天，我拥有了人生中的第一台电脑，第一件事就是把《逆战》（游戏）下载下来。无他，因为<strong>逆战</strong>这首歌在当时就是现在小学生心目中的<strong>孤勇者</strong>。</p>

<p><strong>张杰</strong>的演唱会真的太顶了，不论是氛围、嗓音、歌曲、编舞、音效还是灯光都是无敌的存在！</p>

<p>还有个小插曲，演唱会结束后发现我的一位初中同学和另一位朋友竟然和我一块在现场。</p>

<h4 id="2--memory-4">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/b28c1685eb4743eb21ab2986f0750d45.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/842b5b707b6db0d659e13a7d292d19b5.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/e99d4ffbce3959249a29e2a1045b4c42.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/fcc01ffc42829688b50d782019e66d6b.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/059de7b30040024af60e69b08e08bfb7.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/a7d38cca39226b693c41a854492baf27.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/aff90d71d1257b3f8520bd1b93f4010e.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/4b18796a1d5883e642d1f1ea8e291307.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/f675fd3bdd93dd46098a61e6e71990fd.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/e29cb6bd399aafca0fb923fe7606e1d2.webp" alt="" /></p>

<h4 id="3--个人评分-4">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />

<h3 id="-no006--凤凰传奇---吉祥如意">🎵 NO.[006] | [凤凰传奇] - [吉祥如意]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2025 年 05 月 24 日<br />
<strong>📍 地点：</strong> [北京] · [国家体育场-鸟巢]<br />
<strong>🎫 座位：</strong> [ H区 635通道 六层 26排 7~10号 ]
<strong>🏷️ 关键词：</strong> #组队看演唱会 #蹦迪</p>
</blockquote>

<h4 id="1--content-5">1. 📜 Content</h4>

<p><strong>这一次，四人终于抵达了他们忠诚的鸟巢！</strong></p>

<h4 id="2--memory-5">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/a54d93c781ef47f0f8ddb1637ebb7a2b.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/94c0b136f85d70fb02f49b208070b8d5.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/f2ed607b6e326f2e5b7fe388a869bce0.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/4a0e4c8c8e75c6e242ebb87f62eaf6e5.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/07b83a6cf11f23014e9c68704b1b2baa.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/f657397dad165ca1f5e1d670ef46e4a4.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/5b56bd318c476f65dce021c7b070b500.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/5fdc7c1d644c05f1148a86bbcdf9e8fb.webp" alt="" /></p>

<h4 id="3--个人评分-5">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />

<h3 id="-no007--房东的猫---世界青年---2025巡回演唱会--南京站">🎵 NO.[007] | [房东的猫] - [世界/青年 - 2025巡回演唱会- 南京站]</h3>

<blockquote>
  <p><strong>📅 时间：</strong> 2025 年 06 月 14 日<br />
<strong>📍 地点：</strong> [南京] · [南京奥体中心国缘V9体育馆]<br />
<strong>🎫 座位：</strong> [ 看台六十五区 看台6F 3排 4座 ]
<strong>🏷️ 关键词：</strong> #毕业旅行</p>
</blockquote>

<h4 id="1--content-6">1. 📜 Content</h4>

<p>临近毕业的阶段，买到了南京站的🎫（卡点抢的票，结果后面打折卖，可恶的大麦！）。下午去参加粉丝活动的时候认识了一位南林大的新朋友，从而使这段旅程没有太过“安静”。晚上演唱会结束后下起了暴雨⛈️，没有带伞，而免费雨披也送完了，所以就做了一次“雨中追雨的少年”，回到酒店后衣服都湿透了。</p>

<p>之后在南京玩了三天，美丽的南京，去了还想去。“旅途”最后两天去了上海找我同学，25岁第一次踏上了我曾梦想去的城市-上海。</p>

<p><strong>无论在何处，祝你美梦成真</strong></p>

<h4 id="2--memory-6">2. 📸 Memory</h4>

<p><img src="https://img.yucol.uk/2026/02/ad24a56a290d4ba114b755465661b09c.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/2dd6aa01f510c8f605d047e7eebe001d.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/ab9644a46148cdcdd090204fbb04c5fd.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/224605f108ac8205dd767e3e2648de3a.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/7cd6790b2ac8938c8946256479b8900c.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/7a6ee0db182e98c183e92d16f8789ac4.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/9e7136d0f5a64e093ad0de3ef28b3e00.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/7c16fa33c0b4fa5d95c7426d335a7dbf.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/e4283321f950066abf9d43c24fdd106b.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/51341cf870e66bd1fdf1bd95a36b4795.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/1beaa9477f2f0363afb028cec83299f5.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/c92e27acfc7c79442ea914015072e09c.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/71e01ee963fa31b66f11df3247abed7a.webp" alt="" />
<img src="https://img.yucol.uk/2026/02/4ddd9f16a8e1802cf77421aa868e0b05.webp" alt="" /></p>

<h4 id="3--个人评分-6">3. ⭐ 个人评分</h4>

<ul>
  <li><strong>曲目编排：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>舞美视听：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>歌手状态：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>推荐指数：</strong> ⭐⭐⭐⭐⭐</li>
  <li><strong>综合评分：</strong> ⭐⭐⭐⭐⭐</li>
</ul>

<hr />]]></content><author><name>yucol</name></author><category term="life" /><category term="演唱会" /><category term="房东的猫" /><category term="成果" /><category term="夏日入侵企画" /><category term="双笙" /><category term="张杰" /><category term="凤凰传奇" /><summary type="html"><![CDATA[🚀 我的演唱会流浪计划]]></summary></entry><entry><title type="html">从零到一：阿里云 200M 带宽服务器的全栈基建（1Panel + Docker + GitHub Actions）</title><link href="https://yucol.top/server/server-setup.html" rel="alternate" type="text/html" title="从零到一：阿里云 200M 带宽服务器的全栈基建（1Panel + Docker + GitHub Actions）" /><published>2026-02-25T00:00:00+00:00</published><updated>2026-02-25T00:00:00+00:00</updated><id>https://yucol.top/server/server-setup</id><content type="html" xml:base="https://yucol.top/server/server-setup.html"><![CDATA[<h1 id="1-前言为什么放弃手动搬运">1. 前言：为什么放弃“手动搬运”？</h1>

<p>在拥有了自己的服务器后，第一反应是手动上传文件。但这存在三个弊端：</p>

<ul>
  <li><strong>效率低</strong>：改一个字也要开一遍 FTP。</li>
  <li><strong>易出错</strong>：容易漏传或传错目录。</li>
  <li><strong>无版本控制</strong>：无法回溯历史。
本篇记录如何利用 <strong>GitHub Actions</strong> 打造一条自动化流水线，实现“本地代码一推，服务器即刻更新”。</li>
</ul>

<hr />

<h1 id="2-环境说明">2. 环境说明</h1>

<ul>
  <li><strong>服务器</strong>：阿里云 2核2G / 200M 带宽 / Ubuntu 系统。</li>
  <li><strong>运维面板</strong>：1Panel（基于 Docker 的现代化管理面板）。</li>
  <li><strong>静态引擎</strong>：Jekyll (Ruby 3.4.4)。</li>
  <li><strong>部署通道</strong>：SSH + Rsync。</li>
</ul>

<hr />

<h1 id="3-核心步骤详解">3. 核心步骤详解</h1>

<h2 id="第一步服务器基建-1panel">第一步：服务器基建 (1Panel)</h2>

<ol>
  <li>安装 1Panel 并在应用商店安装 <strong>OpenResty</strong>（Nginx 的增强版）。</li>
  <li>在“网站”菜单下创建一个静态网站。</li>
</ol>

<ul>
  <li><strong>路径</strong>：<code class="language-plaintext highlighter-rouge">/opt/1panel/apps/openresty/openresty/www/sites/test/index</code>（此路径为后期同步的目标 <code class="language-plaintext highlighter-rouge">TARGET</code>）。</li>
</ul>

<ol>
  <li>确保服务器防火墙已开启 80/443 端口，以及 SSH 通讯所需的 22 端口。</li>
</ol>

<h2 id="第二步建立信任关系-ssh-key">第二步：建立“信任关系” (SSH Key)</h2>

<p>为了让 GitHub 机器人能直接登录我们的服务器，需要配置免密登录：</p>

<ol>
  <li><strong>本地生成密钥对</strong>：
<code class="language-plaintext highlighter-rouge">ssh-keygen -t rsa -b 4096 -f id_rsa_github</code></li>
  <li><strong>分发公钥</strong>：将 <code class="language-plaintext highlighter-rouge">id_rsa_github.pub</code> 的内容追加到服务器的 <code class="language-plaintext highlighter-rouge">/root/.ssh/authorized_keys</code> 中。</li>
  <li><strong>权限加固</strong>：确保 <code class="language-plaintext highlighter-rouge">.ssh</code> 目录权限为 <code class="language-plaintext highlighter-rouge">700</code>，<code class="language-plaintext highlighter-rouge">authorized_keys</code> 权限为 <code class="language-plaintext highlighter-rouge">600</code>。</li>
</ol>

<h2 id="第三步配置-github-secrets">第三步：配置 GitHub Secrets</h2>

<p>在 GitHub 仓库的 <code class="language-plaintext highlighter-rouge">Settings -&gt; Secrets and variables -&gt; Actions</code> 中配置四个关键变量：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">REMOTE_HOST</code>: 服务器公网 IP。</li>
  <li><code class="language-plaintext highlighter-rouge">REMOTE_USER</code>: <code class="language-plaintext highlighter-rouge">root</code>。</li>
  <li><code class="language-plaintext highlighter-rouge">SSH_PRIVATE_KEY</code>: <code class="language-plaintext highlighter-rouge">id_rsa_github</code> 文件里的完整私钥内容。</li>
  <li><code class="language-plaintext highlighter-rouge">REMOTE_TARGET</code>: 服务器上的静态目录路径。</li>
</ul>

<h2 id="第四步编写自动化流水线-yaml">第四步：编写自动化流水线 (YAML)</h2>

<p>在 <code class="language-plaintext highlighter-rouge">.github/workflows/</code> 下创建 <code class="language-plaintext highlighter-rouge">jekyll.yml</code>。核心逻辑是利用 <code class="language-plaintext highlighter-rouge">easingthemes/ssh-deploy</code> 插件执行 <code class="language-plaintext highlighter-rouge">rsync</code>。</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 关键配置摘要</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to Aliyun Server</span>
  <span class="na">uses</span><span class="pi">:</span> <span class="s">easingthemes/ssh-deploy@v5.1.0</span>
  <span class="na">with</span><span class="pi">:</span>
    <span class="na">SSH_PRIVATE_KEY</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">ARGS</span><span class="pi">:</span> <span class="s2">"</span><span class="s">-rltzvi</span><span class="nv"> </span><span class="s">--delete"</span> <span class="c1"># 增量同步，删除服务器多余文件</span>
    <span class="na">SOURCE</span><span class="pi">:</span> <span class="s2">"</span><span class="s">_site/"</span>         <span class="c1"># Jekyll 构建后的产物目录</span>
    <span class="na">REMOTE_HOST</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">REMOTE_USER</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">TARGET</span><span class="pi">:</span> <span class="s">$</span>

</code></pre></div></div>

<hr />

<h1 id="4-外部资源集成">4. 外部资源集成：</h1>

<p>为了追求极致的性能和稳定性，我并没有将所有功能都死磕在自己的服务器上，而是引入了更成熟的云原生方案：</p>

<h2 id="a-评论系统waline--vercel--neon">A. 评论系统：Waline + Vercel + Neon</h2>

<p>为了不让评论数据的存储和处理占用阿里云的 2G 内存，我采用了 <strong>Serverless（无服务器）</strong> 架构：</p>

<ul>
  <li><strong>后端部署 (Vercel)</strong>：利用 Vercel 的免费额度托管 Waline 后端程序。</li>
  <li><strong>数据库 (Neon)</strong>：所有的评论内容存储在 Neon 的云数据库中，安全且免费。</li>
  <li><strong>集成方式</strong>：在 Jekyll 模板中引入 Waline 的 JS 脚本，通过 API 与 Vercel 通信。</li>
  <li><strong>深度心得</strong>：这种方案实现了“数据随人走”，即便我以后重装服务器系统，评论数据也不会丢失。</li>
</ul>

<h2 id="b-极致图床cloudflare-r2--picgo">B. 极致图床：Cloudflare R2 + PicGo</h2>

<p>虽然服务器有 200M 带宽，但为了节省宝贵的公网流量以及应对未来可能的并发访问，我搭建了专业的对象存储图床：</p>

<ul>
  <li><strong>存储底座 (Cloudflare R2)</strong>：利用 Cloudflare 的 R2 存储（S3 兼容模式），免流量费且自带全球 CDN 加速。</li>
  <li><strong>自动化工具 (PicGo)</strong>：在本地配置 PicGo 插件，实现“截图 -&gt; 上传 -&gt; 自动生成 Markdown 链接”的一键流。</li>
  <li><strong>安全加固 (V2 签名)</strong>：使用 V2 签名机制确保上传接口的安全，防止图床被恶意盗刷。</li>
</ul>

<hr />

<h1 id="5-未来扩展">5. 未来扩展：</h1>

<p>目前的基建仅完成了博客的部署。后续计划：</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><strong>后端 API</strong>：在 1Panel 中使用 Docker 部署 Python (FastAPI) 容器。</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><strong>移动端联调</strong>：Kotlin 开发的安卓 App 通过 HTTP 请求调用服务器 IP 上的 Python 接口，实现数据上云。</li>
</ul>]]></content><author><name>yucol</name></author><category term="server" /><category term="服务器" /><category term="GitHub Actions" /><category term="blog" /><summary type="html"><![CDATA[1. 前言：为什么放弃“手动搬运”？]]></summary></entry><entry><title type="html">我的第一款酒局神器App“摇了么”</title><link href="https://yucol.top/app/PartyDice.html" rel="alternate" type="text/html" title="我的第一款酒局神器App“摇了么”" /><published>2026-02-22T13:20:00+00:00</published><updated>2026-02-22T13:20:00+00:00</updated><id>https://yucol.top/app/PartyDice</id><content type="html" xml:base="https://yucol.top/app/PartyDice.html"><![CDATA[<h1 id="-我的第一款酒局神器app摇了么诞生记">🚀 我的第一款酒局神器App“摇了么”诞生记！</h1>

<blockquote>
  <p>源代码、安装包及效果图在文章结尾处。</p>
</blockquote>

<p>一直想做一个既好玩又实用的东西。终于，在AI的帮助下，我的第一款 Android 独立应用——<strong>“摇了么”</strong>，正式诞生啦！</p>

<p>这是一款专为酒局、聚会打造的摇骰子小工具。虽然市面上有很多类似的小程序，但作为一个强迫症+体验控，决定亲自操刀，给它注入真正的“灵魂”。</p>

<h3 id="-为什么叫摇了么">🎲 为什么叫“摇了么”？</h3>

<p>在酒桌上，最常听到的一句话可能就是“该你了，摇了么？”。这个名字既是对朋友的催促，也是这款 App 核心交互的体现——别点屏幕了，直接拿起手机摇吧！</p>

<h3 id="-核心亮点把细节死磕到底">✨ 核心亮点：把细节死磕到底</h3>

<p>在体验上主要有以下亮点：</p>

<ul>
  <li><strong>告别反人类UI，单手掌控全局：</strong>
抛弃了早期版本中需要呼出软键盘来输入骰子数量的繁琐设计。利用线性布局（LinearLayout）的权重属性，将核心视觉区（骰子展示）固定在中上方，而在底部手指出没的最舒适区域，加入了丝滑的 <code class="language-plaintext highlighter-rouge">-</code> 和 <code class="language-plaintext highlighter-rouge">+</code> 按钮。1到15个骰子，单手一秒切换。</li>
  <li><strong>注入灵魂的“物理外挂”（摇一摇）：</strong>
酒局游戏，怎么能只用手指点按钮呢？调用了 Android 的底层硬件——<strong>加速度传感器 (Accelerometer)</strong>。通过计算 X、Y、Z 三个维度的综合重力加速度，只要你像握着真实骰盅一样用力甩动手机，代码就会自动捕捉你的动作并触发掷骰子！</li>
  <li><strong>沉浸式的声感与触觉反馈：</strong>
为了模拟真实的物理反馈，加入了双重感官刺激：
    <ol>
      <li><strong>听觉</strong>：接入 <code class="language-plaintext highlighter-rouge">MediaPlayer</code>，摇晃时伴随清脆的“哗啦哗啦”骰子碰撞声。</li>
      <li><strong>触觉</strong>：调用 <code class="language-plaintext highlighter-rouge">Vibrator</code> 震动马达。在骰子滚动的 1 秒内，配合动画发出极其高频的轻微震动，停住的瞬间来一次重震。闭上眼睛，你手里握着的仿佛就是个真骰盅！</li>
    </ol>
  </li>
  <li><strong>安全纯净无广告：</strong>
代码完全开源，没有任何联网和收集隐私信息的行为；界面干净整洁，没有任何广告及干扰信息！</li>
</ul>

<hr />

<h3 id="-核心代码-show-me-the-code">💻 核心代码 (Show Me The Code)</h3>

<p>这里分享一下“摇了么”最核心的两个逻辑实现：</p>

<h4 id="1-如何让手机听懂摇晃传感器逻辑">1. 如何让手机听懂“摇晃”？(传感器逻辑)</h4>

<p>这里用到了初中的物理和数学知识 。手机的加速度传感器会实时返回 X、Y、Z 三个轴的受力。们算出它们除以地球重力加速度后的 G 力，然后利用勾股定理  算出综合受力：</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 传感器检测到手机运动时自动调用</span>
<span class="k">override</span> <span class="k">fun</span> <span class="nf">onSensorChanged</span><span class="p">(</span><span class="n">event</span><span class="p">:</span> <span class="nc">SensorEvent</span><span class="p">?)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">event</span> <span class="p">!=</span> <span class="k">null</span> <span class="p">&amp;&amp;</span> <span class="p">!</span><span class="n">isRolling</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">x</span> <span class="p">=</span> <span class="n">event</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="kd">val</span> <span class="py">y</span> <span class="p">=</span> <span class="n">event</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
        <span class="kd">val</span> <span class="py">z</span> <span class="p">=</span> <span class="n">event</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>

        <span class="c1">// 计算加速度，除以地球重力加速度得出 G 力</span>
        <span class="kd">val</span> <span class="py">gX</span> <span class="p">=</span> <span class="n">x</span> <span class="p">/</span> <span class="nc">SensorManager</span><span class="p">.</span><span class="nc">GRAVITY_EARTH</span>
        <span class="kd">val</span> <span class="py">gY</span> <span class="p">=</span> <span class="n">y</span> <span class="p">/</span> <span class="nc">SensorManager</span><span class="p">.</span><span class="nc">GRAVITY_EARTH</span>
        <span class="kd">val</span> <span class="py">gZ</span> <span class="p">=</span> <span class="n">z</span> <span class="p">/</span> <span class="nc">SensorManager</span><span class="p">.</span><span class="nc">GRAVITY_EARTH</span>

        <span class="c1">// 勾股定理计算综合受力大小</span>
        <span class="kd">val</span> <span class="py">gForce</span> <span class="p">=</span> <span class="nf">sqrt</span><span class="p">((</span><span class="n">gX</span> <span class="p">*</span> <span class="n">gX</span> <span class="p">+</span> <span class="n">gY</span> <span class="p">*</span> <span class="n">gY</span> <span class="p">+</span> <span class="n">gZ</span> <span class="p">*</span> <span class="n">gZ</span><span class="p">).</span><span class="nf">toDouble</span><span class="p">()).</span><span class="nf">toFloat</span><span class="p">()</span>

        <span class="c1">// 阈值设定为 2.7：大约是用力甩一下手机的力度</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">gForce</span> <span class="p">&gt;</span> <span class="mf">2.7f</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">val</span> <span class="py">now</span> <span class="p">=</span> <span class="nc">System</span><span class="p">.</span><span class="nf">currentTimeMillis</span><span class="p">()</span>
            <span class="c1">// 设置 1 秒的冷却防抖时间</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">now</span> <span class="p">-</span> <span class="n">lastShakeTime</span> <span class="p">&gt;</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">lastShakeTime</span> <span class="p">=</span> <span class="n">now</span>
                <span class="c1">// 模拟用户点击了摇骰子按钮</span>
                <span class="n">findViewById</span><span class="p">&lt;</span><span class="nc">Button</span><span class="p">&gt;(</span><span class="nc">R</span><span class="p">.</span><span class="n">id</span><span class="p">.</span><span class="n">btnRoll</span><span class="p">).</span><span class="nf">performClick</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h4 id="2-如何实现逼真的骰子翻滚与触觉反馈">2. 如何实现逼真的“骰子翻滚”与触觉反馈？</h4>

<p>为了不让结果显得太突兀，利用安卓自带的 <code class="language-plaintext highlighter-rouge">CountDownTimer</code> 写了一个 1 秒钟的动画循环。在这 1 秒内，不仅疯狂切换图片、随机倾斜角度，还同步调用了震动马达：</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 开始摇骰子动画（持续1000毫秒，每100毫秒刷新一次）</span>
<span class="kd">object</span> <span class="err">: </span><span class="nc">CountDownTimer</span><span class="p">(</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onTick</span><span class="p">(</span><span class="n">millisUntilFinished</span><span class="p">:</span> <span class="nc">Long</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">view</span> <span class="k">in</span> <span class="n">diceViews</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// 随机切图并赋予 -30 到 30 度的随机倾斜角</span>
            <span class="n">view</span><span class="p">.</span><span class="nf">setImageResource</span><span class="p">(</span><span class="n">diceImages</span><span class="p">[</span><span class="nc">Random</span><span class="p">.</span><span class="nf">nextInt</span><span class="p">(</span><span class="mi">6</span><span class="p">)])</span>
            <span class="n">view</span><span class="p">.</span><span class="n">rotation</span> <span class="p">=</span> <span class="nc">Random</span><span class="p">.</span><span class="nf">nextInt</span><span class="p">(-</span><span class="mi">30</span><span class="p">,</span> <span class="mi">30</span><span class="p">).</span><span class="nf">toFloat</span><span class="p">()</span>
        <span class="p">}</span>
        <span class="c1">// 摇晃过程中：触发 20 毫秒的极轻微震动（模拟骰子撞击杯壁）</span>
        <span class="k">if</span> <span class="p">(</span><span class="nc">Build</span><span class="p">.</span><span class="nc">VERSION</span><span class="p">.</span><span class="nc">SDK_INT</span> <span class="p">&gt;=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">VERSION_CODES</span><span class="p">.</span><span class="nc">O</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">vibrator</span><span class="p">.</span><span class="nf">vibrate</span><span class="p">(</span><span class="nc">VibrationEffect</span><span class="p">.</span><span class="nf">createOneShot</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="nc">VibrationEffect</span><span class="p">.</span><span class="nc">DEFAULT_AMPLITUDE</span><span class="p">))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">vibrator</span><span class="p">.</span><span class="nf">vibrate</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">override</span> <span class="k">fun</span> <span class="nf">onFinish</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">view</span> <span class="k">in</span> <span class="n">diceViews</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// 定格最终点数，并将角度摆正</span>
            <span class="n">view</span><span class="p">.</span><span class="nf">setImageResource</span><span class="p">(</span><span class="n">diceImages</span><span class="p">[</span><span class="nc">Random</span><span class="p">.</span><span class="nf">nextInt</span><span class="p">(</span><span class="mi">6</span><span class="p">)])</span>
            <span class="n">view</span><span class="p">.</span><span class="n">rotation</span> <span class="p">=</span> <span class="mf">0f</span> 
        <span class="p">}</span>
        <span class="c1">// 摇晃结束：触发 100 毫秒的重震（模拟落桌定音）</span>
        <span class="k">if</span> <span class="p">(</span><span class="nc">Build</span><span class="p">.</span><span class="nc">VERSION</span><span class="p">.</span><span class="nc">SDK_INT</span> <span class="p">&gt;=</span> <span class="nc">Build</span><span class="p">.</span><span class="nc">VERSION_CODES</span><span class="p">.</span><span class="nc">O</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">vibrator</span><span class="p">.</span><span class="nf">vibrate</span><span class="p">(</span><span class="nc">VibrationEffect</span><span class="p">.</span><span class="nf">createOneShot</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="nc">VibrationEffect</span><span class="p">.</span><span class="nc">DEFAULT_AMPLITUDE</span><span class="p">))</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">vibrator</span><span class="p">.</span><span class="nf">vibrate</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="n">isRolling</span> <span class="p">=</span> <span class="k">false</span> 
    <span class="p">}</span>
<span class="p">}.</span><span class="nf">start</span><span class="p">()</span>

</code></pre></div></div>

<h3 id="-写在最后">💡 写在最后</h3>

<p>如果你也想体验一下这款小工具，或者对源码感兴趣，欢迎讨论与交流！</p>

<ul>
  <li>项目地址：<a href="https://github.com/JMbaozi/PartyDice">https://github.com/JMbaozi/PartyDice</a></li>
  <li>安装包地址：<a href="https://github.com/JMbaozi/PartyDice/releases/tag/v1.0">摇了么 v1.0</a></li>
</ul>

<p><img src="https://youke.xn--y7xa690gmna.cn/s1/2026/02/22/699b03b332e82.webp" alt="home.jpg" /></p>]]></content><author><name>yucol</name></author><category term="app" /><category term="安卓" /><category term="骰子" /><category term="游戏" /><category term="独立开发" /><summary type="html"><![CDATA[🚀 我的第一款酒局神器App“摇了么”诞生记！]]></summary></entry><entry><title type="html">从 H2O 到 H2O-ac：我的博客迁移与填坑笔记</title><link href="https://yucol.top/blog/blog-migration-notes.html" rel="alternate" type="text/html" title="从 H2O 到 H2O-ac：我的博客迁移与填坑笔记" /><published>2026-02-22T00:00:00+00:00</published><updated>2026-02-22T00:00:00+00:00</updated><id>https://yucol.top/blog/blog-migration-notes</id><content type="html" xml:base="https://yucol.top/blog/blog-migration-notes.html"><![CDATA[<h2 id="前言">前言</h2>

<p>博客最初的采用的模板是由<a href="https://github.com/kaeyleo">kaeyleo</a>基于<a href="https://jekyllrb.com/">jekyll</a>开发的<a href="https://github.com/kaeyleo/jekyll-theme-H2O">H2O</a>主题。我在此基础上添加了评论区、友链、归档、音乐播放器、访客记录、目录等功能。但是限于能力有限，写的代码一堆BUG。最近我浏览到了一个基于<a href="https://github.com/kaeyleo/jekyll-theme-H2O">H2O</a>修改的<a href="https://github.com/zhonger/jekyll-theme-H2O-ac">H2O-ac</a>主题，其内容丰富，视觉效果和阅读体验非常好。</p>

<p>所以我决定对博客进行了一次“大手术”，将原本一直使用的H2O模板替换成了目前视觉效果非常出色的 <strong>H2O-ac</strong> 主题。</p>

<p>虽然 H2O-ac 带来了极佳的阅读体验和学术风界面，但在迁移和配置自动化的过程中，我遇到了不少令人头大的 Bug。</p>

<p>为了方便日后查阅，也为了给同样使用该主题的小伙伴提供参考，特写下这篇笔记。</p>

<h2 id="迁移过程从手动到全自动">迁移过程：从手动到全自动</h2>

<p>最初的迁移并不顺利，旧模板留下的残留文件（如 Azure 的部署脚本等）频繁导致构建失败。</p>

<h3 id="1-自动化-cicd-的重建">1. 自动化 CI/CD 的重建</h3>
<p>为了实现“推送即发布”，我弃用了原本混乱的部署脚本，重新编写了 <code class="language-plaintext highlighter-rouge">.github/workflows/jekyll.yml</code>。通过以下逻辑确保了环境的纯净：</p>
<ul>
  <li><strong>环境初始化</strong>：使用 Ruby 3.4.4 环境。</li>
  <li><strong>强制清理</strong>：在编译前运行 <code class="language-plaintext highlighter-rouge">bundle exec jekyll clean</code>。</li>
  <li><strong>强制覆盖</strong>：发布到 <code class="language-plaintext highlighter-rouge">gh-pages</code> 分支时使用 <code class="language-plaintext highlighter-rouge">force_orphan: true</code>，彻底杜绝旧文件缓存。</li>
</ul>

<h3 id="2-版权与开源协议">2. 版权与开源协议</h3>
<p>由于 H2O-ac 是基于 H2O 主题的 fork 版本，遵循 <strong>MIT 开源协议</strong>。在迁移过程中，我保留了原作者的版权信息。</p>

<h2 id="遇到的问题与-bug-排查重点">遇到的问题与 Bug 排查（重点）</h2>

<p>这次迁移最核心的挑战在于：文章在标签（Tags）页能看，但在首页（Blog）和归档（Archives）页却神秘失踪。经过反复排查，我定位到了以下几个“元凶”：</p>

<h3 id="bug-1pin-false-逻辑陷阱">Bug 1：<code class="language-plaintext highlighter-rouge">pin: false</code> 逻辑陷阱</h3>
<p>这是最难发现的一个点。H2O-ac 主题自带了置顶功能，但我发现：</p>
<ul>
  <li>当我在文章头部显式写下 <code class="language-plaintext highlighter-rouge">pin: false</code> 时，首页和归档页会完全无法显示该文章。</li>
  <li><strong>解决方案</strong>：普通文章直接<strong>删除 <code class="language-plaintext highlighter-rouge">pin</code> 属性</strong>即可。如果不置顶，就不要写这一行，写成 <code class="language-plaintext highlighter-rouge">false</code> 反而会被主题逻辑误判并过滤掉。</li>
</ul>

<h3 id="bug-2分支挂载错位">Bug 2：分支挂载错位</h3>
<p>在 GitHub Pages 设置中，如果将部署源选为 <code class="language-plaintext highlighter-rouge">master</code>，可能无法看到经过 Actions 机器人编译后的成品。</p>
<ul>
  <li><strong>解决方法</strong>：将 GitHub Pages 的部署分支切换到 <strong><code class="language-plaintext highlighter-rouge">gh-pages</code></strong>，这才是机器人存放 HTML 成品的地方。</li>
</ul>

<h2 id="参考文档">参考文档</h2>

<ul>
  <li><a href="https://github.com/zhonger/jekyll-theme-H2O-ac">H2O-ac主题仓库</a></li>
  <li><a href="https://lisz.me/tech/new-theme-h2o-ac">H2O-ac主题介绍</a></li>
  <li><a href="https://h2o-ac-doc.lisz.me/">H2O-ac主题文档</a></li>
</ul>

<h2 id="结语">结语</h2>

<p>感谢 H2O 系列主题的作者们提供的优秀代码。</p>]]></content><author><name>yucol</name></author><category term="blog" /><category term="Jekyll" /><category term="GitHub Actions" /><category term="填坑" /><category term="blog" /><summary type="html"><![CDATA[前言]]></summary></entry><entry><title type="html">HTTP 状态码</title><link href="https://yucol.top/tech/HTTP-%E7%8A%B6%E6%80%81%E7%A0%81.html" rel="alternate" type="text/html" title="HTTP 状态码" /><published>2026-01-06T00:00:00+00:00</published><updated>2026-01-06T00:00:00+00:00</updated><id>https://yucol.top/tech/HTTP%20%E7%8A%B6%E6%80%81%E7%A0%81</id><content type="html" xml:base="https://yucol.top/tech/HTTP-%E7%8A%B6%E6%80%81%E7%A0%81.html"><![CDATA[<h1 id="http-状态码完全指南详解版">HTTP 状态码完全指南（详解版）</h1>

<blockquote>
  <p>一篇面向 <strong>Web / 后端 / 前端 / GIS Web 应用开发者</strong> 的 HTTP 状态码系统性参考文章。</p>
</blockquote>

<hr />

<h2 id="一什么是-http-状态码">一、什么是 HTTP 状态码</h2>

<p><strong>HTTP 状态码（HTTP Status Code）</strong> 是服务器在响应客户端（浏览器、APP、程序）请求时，返回的一个 <strong>三位数字</strong>，用于说明：</p>

<blockquote>
  <p>👉 <strong>这次请求发生了什么、结果如何、是否需要进一步操作</strong></p>
</blockquote>

<p>HTTP 状态码是 <strong>HTTP 协议语义的核心组成部分</strong>，对于：</p>

<ul>
  <li>前后端接口设计</li>
  <li>RESTful API 规范</li>
  <li>错误处理与调试</li>
  <li>网络性能与安全分析</li>
</ul>

<p>都具有非常重要的意义。</p>

<hr />

<h2 id="二http-状态码的五大类">二、HTTP 状态码的五大类</h2>

<p>HTTP 状态码按照 <strong>首位数字</strong> 分为五大类：</p>

<table>
  <thead>
    <tr>
      <th>分类</th>
      <th>范围</th>
      <th>含义</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1xx</td>
      <td>100–199</td>
      <td>信息性响应（请求已接收）</td>
    </tr>
    <tr>
      <td>2xx</td>
      <td>200–299</td>
      <td>成功（请求已成功处理）</td>
    </tr>
    <tr>
      <td>3xx</td>
      <td>300–399</td>
      <td>重定向（需要进一步操作）</td>
    </tr>
    <tr>
      <td>4xx</td>
      <td>400–499</td>
      <td>客户端错误</td>
    </tr>
    <tr>
      <td>5xx</td>
      <td>500–599</td>
      <td>服务器错误</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="三1xx信息性状态码informational">三、1xx：信息性状态码（Informational）</h2>

<blockquote>
  <p>表示请求已被接收，正在处理，<strong>很少在业务代码中直接使用</strong>。</p>
</blockquote>

<h3 id="100-continue">100 Continue</h3>

<ul>
  <li>含义：客户端可以继续发送请求体</li>
  <li>场景：大文件上传前的确认机制</li>
</ul>

<h3 id="101-switching-protocols">101 Switching Protocols</h3>

<ul>
  <li>含义：服务器同意切换协议</li>
  <li>场景：HTTP → WebSocket</li>
</ul>

<hr />

<h2 id="四2xx成功状态码success">四、2xx：成功状态码（Success）</h2>

<blockquote>
  <p><strong>最重要、最常用的一类状态码</strong>，表示请求被正确处理。</p>
</blockquote>

<h3 id="200-ok">200 OK</h3>

<ul>
  <li>含义：请求成功</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>GET 查询成功</li>
      <li>PUT / PATCH 更新成功</li>
    </ul>
  </li>
</ul>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">GET /api/fields/123
</span><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
</code></pre></div></div>

<hr />

<h3 id="201-created">201 Created</h3>

<ul>
  <li>含义：资源创建成功</li>
  <li>场景：POST 新建资源</li>
  <li>建议：返回新资源的 URL</li>
</ul>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST /api/fields
</span><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">201</span> <span class="ne">Created</span>
<span class="na">Location</span><span class="p">:</span> <span class="s">/api/fields/456</span>
</code></pre></div></div>

<hr />

<h3 id="202-accepted">202 Accepted</h3>

<ul>
  <li>含义：请求已接收，但尚未处理完成</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>异步任务</li>
      <li>后台计算（如 GIS 插值、模型分析）</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="204-no-content">204 No Content</h3>

<ul>
  <li>含义：请求成功，但无返回内容</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>删除成功</li>
      <li>仅触发动作的接口</li>
    </ul>
  </li>
</ul>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">DELETE /api/fields/123
</span><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">204</span> <span class="ne">No Content</span>
</code></pre></div></div>

<hr />

<h2 id="五3xx重定向状态码redirection">五、3xx：重定向状态码（Redirection）</h2>

<blockquote>
  <p>表示资源位置发生变化，需要客户端采取进一步操作。</p>
</blockquote>

<h3 id="301-moved-permanently">301 Moved Permanently</h3>

<ul>
  <li>含义：永久重定向</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>域名迁移</li>
      <li>SEO 优化</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="302-found">302 Found</h3>

<ul>
  <li>含义：临时重定向（历史遗留）</li>
  <li>注意：可能被浏览器当作 GET 请求处理</li>
</ul>

<hr />

<h3 id="303-see-other">303 See Other</h3>

<ul>
  <li>含义：使用 GET 访问另一个 URI</li>
  <li>场景：POST 后跳转页面</li>
</ul>

<hr />

<h3 id="304-not-modified">304 Not Modified</h3>

<ul>
  <li>含义：资源未修改</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>浏览器缓存</li>
      <li>ETag / If-Modified-Since</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="六4xx客户端错误client-error">六、4xx：客户端错误（Client Error）</h2>

<blockquote>
  <p><strong>表示问题出在客户端请求本身</strong>。</p>
</blockquote>

<h3 id="400-bad-request">400 Bad Request</h3>

<ul>
  <li>含义：请求格式错误</li>
  <li>
    <p>常见原因：</p>

    <ul>
      <li>JSON 语法错误</li>
      <li>参数缺失</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="401-unauthorized">401 Unauthorized</h3>

<ul>
  <li>含义：未认证</li>
  <li>
    <p>特点：</p>

    <ul>
      <li>需要登录</li>
      <li>通常配合 <code class="language-plaintext highlighter-rouge">WWW-Authenticate</code></li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>⚠️ 注意：401 ≠ 权限不足</p>
</blockquote>

<hr />

<h3 id="403-forbidden">403 Forbidden</h3>

<ul>
  <li>含义：已认证，但无权限</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>普通用户访问管理员接口</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="404-not-found">404 Not Found</h3>

<ul>
  <li>含义：资源不存在</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>URL 错误</li>
      <li>资源被删除</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="405-method-not-allowed">405 Method Not Allowed</h3>

<ul>
  <li>含义：请求方法不被允许</li>
  <li>示例：</li>
</ul>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">POST /api/fields/123
</span><span class="k">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">405</span> <span class="ne">Method Not Allowed</span>
<span class="na">Allow</span><span class="p">:</span> <span class="s">GET, PUT</span>
</code></pre></div></div>

<hr />

<h3 id="409-conflict">409 Conflict</h3>

<ul>
  <li>含义：资源冲突</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>重复创建</li>
      <li>乐观锁冲突</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="422-unprocessable-entity">422 Unprocessable Entity</h3>

<ul>
  <li>含义：语义正确，但业务校验失败</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>表单校验</li>
      <li>数据规则不满足</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="七5xx服务器错误server-error">七、5xx：服务器错误（Server Error）</h2>

<blockquote>
  <p><strong>表示服务器在处理请求时发生异常</strong>。</p>
</blockquote>

<h3 id="500-internal-server-error">500 Internal Server Error</h3>

<ul>
  <li>含义：服务器内部错误</li>
  <li>
    <p>原因：</p>

    <ul>
      <li>未捕获异常</li>
      <li>代码 Bug</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="502-bad-gateway">502 Bad Gateway</h3>

<ul>
  <li>含义：上游服务器返回无效响应</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>反向代理</li>
      <li>微服务调用失败</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="503-service-unavailable">503 Service Unavailable</h3>

<ul>
  <li>含义：服务暂不可用</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>服务维护</li>
      <li>系统过载</li>
    </ul>
  </li>
</ul>

<hr />

<h3 id="504-gateway-timeout">504 Gateway Timeout</h3>

<ul>
  <li>含义：上游服务超时</li>
  <li>
    <p>场景：</p>

    <ul>
      <li>GIS 大规模计算</li>
      <li>数据库响应过慢</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="八restful-api-状态码设计建议">八、RESTful API 状态码设计建议</h2>

<h3 id="1️⃣-成功不要只用-200">1️⃣ 成功不要只用 200</h3>

<table>
  <thead>
    <tr>
      <th>场景</th>
      <th>推荐状态码</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>创建资源</td>
      <td>201</td>
    </tr>
    <tr>
      <td>删除成功</td>
      <td>204</td>
    </tr>
    <tr>
      <td>异步处理</td>
      <td>202</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="2️⃣-错误状态码要语义准确">2️⃣ 错误状态码要“语义准确”</h3>

<table>
  <thead>
    <tr>
      <th>错误场景</th>
      <th>正确做法</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>未登录</td>
      <td>401</td>
    </tr>
    <tr>
      <td>无权限</td>
      <td>403</td>
    </tr>
    <tr>
      <td>参数错误</td>
      <td>400 / 422</td>
    </tr>
    <tr>
      <td>资源不存在</td>
      <td>404</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="3️⃣-状态码--错误信息结构示例">3️⃣ 状态码 + 错误信息结构示例</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="mi">422</span><span class="p">,</span><span class="w">
  </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Validation failed"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"details"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"area"</span><span class="p">:</span><span class="w"> </span><span class="s2">"must be greater than 0"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<hr />

<h2 id="九常见误区总结">九、常见误区总结</h2>

<p>❌ 所有错误都返回 200</p>

<p>❌ 用 500 表示业务校验失败</p>

<p>❌ 401 和 403 混用</p>

<p>❌ 前端只判断 code，不看 HTTP 状态码</p>

<hr />

<h2 id="十结语">十、结语</h2>

<p>HTTP 状态码不仅是 <strong>网络协议的一部分</strong>，更是 <strong>接口设计质量的重要体现</strong>。</p>

<p>一个设计良好的接口，应该做到：</p>

<ul>
  <li><strong>状态码语义清晰</strong></li>
  <li><strong>前后端理解一致</strong></li>
  <li><strong>错误可定位、可处理</strong></li>
</ul>]]></content><author><name>yucol</name></author><category term="tech" /><category term="HTTP" /><category term="前端" /><category term="后端" /><summary type="html"><![CDATA[HTTP 状态码完全指南（详解版）]]></summary></entry><entry><title type="html">土壤采样点</title><link href="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E9%87%87%E6%A0%B7%E7%82%B9.html" rel="alternate" type="text/html" title="土壤采样点" /><published>2025-11-17T00:00:00+00:00</published><updated>2025-11-17T00:00:00+00:00</updated><id>https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E9%87%87%E6%A0%B7%E7%82%B9</id><content type="html" xml:base="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E9%87%87%E6%A0%B7%E7%82%B9.html"><![CDATA[<blockquote>
  <p>以 <strong>ArcGIS Pro 3.0.2</strong> 版本为例，制作某区域土壤采样点。
投影坐标系：WGS-84墨卡托</p>
</blockquote>

<h1 id="目视采样">目视采样</h1>

<p>按照能谱或其他数据（本文所有“异常点”采集样本均为<strong>能谱总计数Total</strong>）进行空间插值，对高/低值进行目视解译，根据需求选取合适数量的土壤采样点。</p>

<p>该方法过于简单，不再赘述。</p>

<h1 id="完全随机采样">完全随机采样</h1>

<h2 id="全局完全随机采样">全局完全随机采样</h2>

<p>将空间插值后的栅格数据进行<code class="language-plaintext highlighter-rouge">栅格转点</code>，之后使用<code class="language-plaintext highlighter-rouge">值提取至点</code>或<code class="language-plaintext highlighter-rouge">多值提取至点</code>。使用<code class="language-plaintext highlighter-rouge">数据管理工具</code>–&gt;<code class="language-plaintext highlighter-rouge">采样</code>–&gt;<code class="language-plaintext highlighter-rouge">创建随机点</code>，根据数据设置参数。</p>

<p>当前方法是在整个区域内进行采样，只考虑数量，不考虑任何其它因素。虽然使用了<code class="language-plaintext highlighter-rouge">值提取至点</code>或<code class="language-plaintext highlighter-rouge">多值提取至点</code>，但仅是为了后续制图时可以借助参考。</p>

<h2 id="探测器行进轨迹上随机采样">探测器行进轨迹上随机采样</h2>

<p>与上述方法不同的点在于，随机分为不是全局，而是探测器（无人机、探测车等）的<strong>行进轨迹</strong>。</p>

<p>根据数据点的<code class="language-plaintext highlighter-rouge">采集时间</code>字段生成轨迹路线：<code class="language-plaintext highlighter-rouge">数据管理工具</code>–&gt;<code class="language-plaintext highlighter-rouge">要素</code>–&gt;<code class="language-plaintext highlighter-rouge">点集转线</code>，<strong>排序字段</strong>选择<code class="language-plaintext highlighter-rouge">采集时间</code>字段。</p>

<p>之后，再将轨迹路线转为点要素：<code class="language-plaintext highlighter-rouge">数据管理工具</code>–&gt;<code class="language-plaintext highlighter-rouge">采样</code>–&gt;<code class="language-plaintext highlighter-rouge">沿线生成点</code>。</p>
<blockquote>
  <p>谨记：切勿使用<code class="language-plaintext highlighter-rouge">要素转点</code>工具，该工具只会生成一个点（中心点）。</p>
</blockquote>

<p>使用<code class="language-plaintext highlighter-rouge">创建随机点</code>，根据数据设置参数。</p>

<h1 id="沿线性要素的分层随机采样">沿线性要素的分层随机采样</h1>

<p>该方法是<strong>ArcGIS Pro</strong>环境下的随机土壤采样点方式之一，其考虑了探测器行进路径及不同属性高低值的因素。</p>

<p>使用<code class="language-plaintext highlighter-rouge">值提取至点</code>或<code class="language-plaintext highlighter-rouge">多值提取至点</code>，将空间插值数据（Total）提取至轨迹点中。打开点数据的<code class="language-plaintext highlighter-rouge">属性表</code>，<strong>添加</strong>一个新字段，用以对当前点进行分类，例如字段<code class="language-plaintext highlighter-rouge">class</code>，值有<code class="language-plaintext highlighter-rouge">coldPoint</code>、<code class="language-plaintext highlighter-rouge">hotPoint</code>、<code class="language-plaintext highlighter-rouge">normalPoint</code>。随后选中对应的分类，使用<code class="language-plaintext highlighter-rouge">创建随机点</code>，根据数据设置参数。这里三种分类各自的数量可以为5/5/3，最小间隔距离为100米。</p>

<p><img src="https://img14.360buyimg.com/ddimg/jfs/t1/368946/2/1327/174372/691aec6eF98883664/f1f40b9a051b4d08.jpg" alt="Sampling_Point.png" /></p>]]></content><author><name>yucol</name></author><category term="tech" /><category term="智慧农业" /><category term="测土" /><category term="土壤采样" /><summary type="html"><![CDATA[以 ArcGIS Pro 3.0.2 版本为例，制作某区域土壤采样点。 投影坐标系：WGS-84墨卡托]]></summary></entry><entry><title type="html">土壤属性网格图</title><link href="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%B1%9E%E6%80%A7%E7%BD%91%E6%A0%BC%E5%9B%BE.html" rel="alternate" type="text/html" title="土壤属性网格图" /><published>2025-11-12T00:00:00+00:00</published><updated>2025-11-12T00:00:00+00:00</updated><id>https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%B1%9E%E6%80%A7%E7%BD%91%E6%A0%BC%E5%9B%BE</id><content type="html" xml:base="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%B1%9E%E6%80%A7%E7%BD%91%E6%A0%BC%E5%9B%BE.html"><![CDATA[<blockquote>
  <p>以 <strong>ArcGIS Pro 3.0.2</strong> 版本为例，制作某区域pH空间分布网格图。
投影坐标系：WGS-84墨卡托</p>
</blockquote>

<h1 id="制作插值图">制作插值图</h1>

<p>对已有点数据进行插值，生成当前区域内的pH空间分布图，差值方法为克里金插值。</p>

<p><img src="https://img11.360buyimg.com/ddimg/jfs/t1/355481/40/7951/105945/69141d4cF02e773d9/df5da46c63f068fb.jpg" alt="Kriging_Region_A_pH.png" /></p>

<h1 id="创建渔网">创建渔网</h1>

<p>选择<code class="language-plaintext highlighter-rouge">创建渔网</code>工具，设置范围及像元大小，该演示设置像元宽度x高度为<code class="language-plaintext highlighter-rouge">30x30</code>，几何类型选择<code class="language-plaintext highlighter-rouge">Polygon</code>。</p>

<p><img src="https://img14.360buyimg.com/ddimg/jfs/t1/355827/7/8056/95896/69141d4cFc76e7528/3675c3c8bcc1f288.jpg" alt="Fishnet_Region_A.png" /></p>

<p>这会创建包围设置范围的一个矩形区域的渔网面要素及点要素。</p>

<p>之后选择<code class="language-plaintext highlighter-rouge">裁剪</code>工具对渔网面及及点进行裁剪，以当前区域为掩膜，只保留区域范围内的面及点。</p>

<p>打开渔网面要素的属性表，对面积字段<code class="language-plaintext highlighter-rouge">Shape_Area</code>进行降序/升序排列，将边缘不规则的网格进行删除（面积＜规则网格面积）。</p>

<h1 id="数据关联">数据关联</h1>

<p>选择<code class="language-plaintext highlighter-rouge">值提取至点</code>，将克里金插值栅格数据提取至渔网点数据中。</p>

<p>右键渔网面要素，选择<code class="language-plaintext highlighter-rouge">连接和关联</code>–&gt;<code class="language-plaintext highlighter-rouge">添加空间连接</code></p>

<p><img src="https://img13.360buyimg.com/ddimg/jfs/t1/346486/40/26084/128448/69141d4bF7c8bac77/9d032a96a2ce653c.jpg" alt="Grid_Region_A_pH.png" /></p>]]></content><author><name>yucol</name></author><category term="tech" /><category term="网格图" /><category term="智慧农业" /><category term="测土" /><summary type="html"><![CDATA[以 ArcGIS Pro 3.0.2 版本为例，制作某区域pH空间分布网格图。 投影坐标系：WGS-84墨卡托]]></summary></entry><entry><title type="html">Vue基础知识</title><link href="https://yucol.top/tech/Vue%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html" rel="alternate" type="text/html" title="Vue基础知识" /><published>2025-11-07T00:00:00+00:00</published><updated>2025-11-07T00:00:00+00:00</updated><id>https://yucol.top/tech/Vue%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86</id><content type="html" xml:base="https://yucol.top/tech/Vue%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.html"><![CDATA[<p><img src="https://img10.360buyimg.com/ddimg/jfs/t1/359854/5/117/12576/690db88cF095ad0bd/686b3ea8314345f2.jpg" alt="image.png" /></p>

<h1 id="数组变化侦听">数组变化侦听</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>
    <span class="nt">&lt;h3&gt;</span>数组变化侦听<span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;button</span> <span class="err">@</span><span class="na">click=</span><span class="s">"addListHandle"</span><span class="nt">&gt;</span>添加数据<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;ul&gt;</span>
        <span class="nt">&lt;li</span> <span class="na">v-for=</span><span class="s">"(item,index) of names"</span> <span class="na">:key=</span><span class="s">"index"</span><span class="nt">&gt;</span>{{ item }}<span class="nt">&lt;/li&gt;</span>
    <span class="nt">&lt;/ul&gt;</span>
<span class="nt">&lt;/template&gt;</span>

<span class="nt">&lt;script&gt;</span>
<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="na">names</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">Alice</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Bob</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Charlie</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Diana</span><span class="dl">'</span><span class="p">]</span>
        <span class="p">}</span>
    <span class="p">},</span>
    <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
        <span class="nf">addListHandle</span><span class="p">()</span> <span class="p">{</span>
            <span class="c1">// this.names.push('New Name ' + (this.names.length + 1));</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">names</span><span class="p">.</span><span class="nf">concat</span><span class="p">([</span><span class="dl">'</span><span class="s1">New Name </span><span class="dl">'</span> <span class="o">+</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">names</span><span class="p">.</span><span class="nx">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]);</span> <span class="c1">// 使用concat不会触发视图更新</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">names</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">names</span><span class="p">.</span><span class="nf">concat</span><span class="p">([</span><span class="dl">'</span><span class="s1">New Name </span><span class="dl">'</span> <span class="o">+</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">names</span><span class="p">.</span><span class="nx">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)]);</span> <span class="c1">// 重新赋值可以触发视图更新</span>
        <span class="p">}</span>
    <span class="p">},</span>
<span class="p">};</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="计算属性computed">计算属性computed</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>
    <span class="nt">&lt;h3&gt;</span>{{ yucol.name }}<span class="nt">&lt;/h3&gt;</span>
    <span class="c">&lt;!-- &lt;p&gt;{{ yucol.kills.length &gt; 0 ? 'Yes': "No" }}&lt;/p&gt; --&gt;</span>
    <span class="nt">&lt;p&gt;</span>{{ killCount }}<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;p&gt;</span>{{ yucolCounts() }}<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/template&gt;</span>


<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="p">{</span>
        <span class="c1">// 这里可以添加一些数据属性</span>
        <span class="na">yucol</span><span class="p">:{</span>
            <span class="na">name</span><span class="p">:</span><span class="dl">'</span><span class="s1">yucol</span><span class="dl">'</span><span class="p">,</span>
            <span class="na">age</span><span class="p">:</span><span class="mi">18</span><span class="p">,</span>
            <span class="na">kills</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">coding</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">gaming</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">reading</span><span class="dl">'</span><span class="p">]</span>
        <span class="p">}</span>
        <span class="p">};</span>
    <span class="p">},</span>
    <span class="na">computed</span><span class="p">:</span> <span class="p">{</span>
        <span class="c1">// 计算属性</span>
        <span class="c1">// 代码不更改时，只会计算一次，提升性能</span>
        <span class="nf">killCount</span><span class="p">()</span> <span class="p">{</span>
            <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">yucol</span><span class="p">.</span><span class="nx">kills</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">Yes</span><span class="dl">'</span><span class="p">:</span> <span class="dl">"</span><span class="s2">No</span><span class="dl">"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">},</span>
    <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
        <span class="c1">// 这里可以添加一些方法</span>
        <span class="nf">yucolCounts</span><span class="p">(){</span>
            <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">yucol</span><span class="p">.</span><span class="nx">kills</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">Yes</span><span class="dl">'</span><span class="p">:</span> <span class="dl">"</span><span class="s2">No</span><span class="dl">"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="class样式绑定">Class样式绑定</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>

    <span class="c">&lt;!-- &lt;p :class="myClass"&gt;Class样式绑定&lt;/p&gt; --&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">:class=</span><span class="s">"{'active':isActive,'text-danger':hasError}"</span><span class="nt">&gt;</span>Class样式绑定<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">:class=</span><span class="s">"classObject"</span><span class="nt">&gt;</span>Class样式绑定（多个对象绑定）<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">:class=</span><span class="s">"arrs"</span><span class="nt">&gt;</span>Class样式绑定（数组绑定）<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">:class=</span><span class="s">"[isActive ? 'active' : '', {'text-danger':hasError}]"</span><span class="nt">&gt;</span>Class样式绑定（数组和对象）<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/template&gt;</span>

<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">(){</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="c1">// 这里可以添加一些数据属性</span>
            <span class="c1">// myClass:'red-text'</span>
            <span class="na">isActive</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span>
            <span class="na">hasError</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span>
            <span class="na">classObject</span><span class="p">:{</span>
                <span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span>
                <span class="dl">'</span><span class="s1">text-danger</span><span class="dl">'</span><span class="p">:</span><span class="kc">true</span>
            <span class="p">},</span>
            <span class="na">arrs</span><span class="p">:[</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span><span class="dl">'</span><span class="s1">text-danger</span><span class="dl">'</span><span class="p">],</span>
            <span class="na">arrActive</span><span class="p">:</span><span class="dl">"</span><span class="s2">active</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">arrDanger</span><span class="p">:</span><span class="dl">"</span><span class="s2">text-danger</span><span class="dl">"</span>
        <span class="p">};</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="nt">&lt;/script&gt;</span>


<span class="nt">&lt;style&gt;</span>

    <span class="nc">.active</span><span class="p">{</span>
        <span class="nl">color</span><span class="p">:</span> <span class="nx">red</span><span class="p">;</span>
        <span class="nl">font-size</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nc">.text-danger</span><span class="p">{</span>
        <span class="nl">background-color</span><span class="p">:</span> <span class="nx">yellow</span><span class="p">;</span>
    <span class="p">}</span>
<span class="nt">&lt;/style&gt;</span>
</code></pre></div></div>

<h1 id="style绑定">Style绑定</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>

    <span class="nt">&lt;p</span> <span class="na">:style=</span><span class="s">"{color:activeColor, fontSize:'30px'}"</span><span class="nt">&gt;</span>Style绑定<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">:style=</span><span class="s">"styleObject"</span><span class="nt">&gt;</span>Style绑定<span class="nt">&lt;/p&gt;</span>

<span class="nt">&lt;/template&gt;</span>


<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">(){</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="c1">// 这里可以添加一些数据属性</span>
            <span class="na">activeColor</span><span class="p">:</span><span class="dl">'</span><span class="s1">red</span><span class="dl">'</span><span class="p">,</span>
            <span class="na">styleObject</span><span class="p">:{</span>
                <span class="na">color</span><span class="p">:</span><span class="dl">'</span><span class="s1">green</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">fontSize</span><span class="p">:</span><span class="dl">'</span><span class="s1">30px</span><span class="dl">'</span>
            <span class="p">}</span>
        <span class="p">};</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="侦听器watch">侦听器watch</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>

    <span class="nt">&lt;h3&gt;</span>侦听器<span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;p&gt;</span>{{ message }}<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;button</span> <span class="err">@</span><span class="na">click=</span><span class="s">"updateHandle"</span><span class="nt">&gt;</span>修改数据<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/template&gt;</span>


<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">(){</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="c1">// 这里可以添加一些数据属性</span>
            <span class="na">message</span><span class="p">:</span><span class="dl">"</span><span class="s2">Hello, Watch Demo!</span><span class="dl">"</span>
        <span class="p">};</span>
    <span class="p">},</span>
    <span class="na">methods</span><span class="p">:{</span>
        <span class="nf">updateHandle</span><span class="p">(){</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Data has been updated!</span><span class="dl">"</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">},</span>
    <span class="na">watch</span><span class="p">:{</span>
        <span class="c1">// 监听 message 属性的变化</span>
        <span class="c1">// newVal: 新值 oldVal: 旧值</span>
        <span class="c1">// 函数名必须与要监听的数据属性名相同</span>
        <span class="nf">message</span><span class="p">(</span><span class="nx">newVal</span><span class="p">,</span><span class="nx">oldVal</span><span class="p">){</span>
            <span class="c1">// 当 message 变化时执行的代码</span>
            <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`message changed from "</span><span class="p">${</span><span class="nx">oldVal</span><span class="p">}</span><span class="s2">" to "</span><span class="p">${</span><span class="nx">newVal</span><span class="p">}</span><span class="s2">"`</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="表单输入绑定">表单输入绑定</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>

    <span class="nt">&lt;h3&gt;</span>表单输入绑定<span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;form</span> <span class="na">action=</span><span class="s">""</span><span class="nt">&gt;</span>
        <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">v-model.lazy=</span><span class="s">"message"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;p&gt;</span>{{ message }}<span class="nt">&lt;/p&gt;</span>
        <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"checkbox"</span> <span class="na">id=</span><span class="s">"checkbox"</span> <span class="na">v-model=</span><span class="s">"checked"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">"checkbox"</span><span class="nt">&gt;</span>{{ checked }}<span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;/form&gt;</span>

<span class="nt">&lt;/template&gt;</span>

<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="na">message</span><span class="p">:</span> <span class="dl">""</span><span class="p">,</span>
            <span class="na">checked</span><span class="p">:</span> <span class="kc">false</span>
        <span class="p">}</span>
    <span class="p">}</span>

<span class="p">}</span>

<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="模板引用">模板引用</h1>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>

    <span class="nt">&lt;div</span> <span class="na">ref=</span><span class="s">"container"</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>
        {{ contents }}
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">ref=</span><span class="s">"username"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;button</span> <span class="err">@</span><span class="na">click=</span><span class="s">"getElementHandle"</span><span class="nt">&gt;</span>获取元素<span class="nt">&lt;/button&gt;</span>

<span class="nt">&lt;/template&gt;</span>


<span class="nt">&lt;script&gt;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="nf">data</span><span class="p">()</span> <span class="p">{</span>    
        <span class="k">return</span> <span class="p">{</span>
            <span class="na">contents</span><span class="p">:</span><span class="dl">"</span><span class="s2">内容</span><span class="dl">"</span>
        <span class="p">}</span>
    <span class="p">},</span>
    <span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
        <span class="nf">getElementHandle</span><span class="p">()</span> <span class="p">{</span>
            <span class="c1">// innerHTML:原生js的属性</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">container</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">hhhhh</span><span class="dl">"</span><span class="p">;</span>
            <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">$refs</span><span class="p">.</span><span class="nx">username</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h1 id="others">others…</h1>]]></content><author><name>yucol</name></author><category term="tech" /><category term="Vue" /><category term="前端" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">能谱数据处理</title><link href="https://yucol.top/tech/%E8%83%BD%E8%B0%B1%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86.html" rel="alternate" type="text/html" title="能谱数据处理" /><published>2025-11-05T00:00:00+00:00</published><updated>2025-11-05T00:00:00+00:00</updated><id>https://yucol.top/tech/%E8%83%BD%E8%B0%B1%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86</id><content type="html" xml:base="https://yucol.top/tech/%E8%83%BD%E8%B0%B1%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86.html"><![CDATA[<h1 id="能谱信息">能谱信息</h1>

<table>
  <thead>
    <tr>
      <th>元素系</th>
      <th>代表通道</th>
      <th>代表核素</th>
      <th>特征能量 (keV)</th>
      <th>土壤意义</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>K 系</td>
      <td>~1460 keV</td>
      <td>K-40</td>
      <td>1460</td>
      <td>钾元素含量</td>
    </tr>
    <tr>
      <td>Th 系</td>
      <td>~2615 keV</td>
      <td>Tl-208（来自 Th-232 系）</td>
      <td>2615</td>
      <td>钍元素含量</td>
    </tr>
    <tr>
      <td>U 系</td>
      <td>~609 keV</td>
      <td>Bi-214（来自 U-238 系）</td>
      <td>609</td>
      <td>铀元素含量</td>
    </tr>
  </tbody>
</table>

<p>目前的能谱探测有两种元件：<strong>NaI</strong>和<strong>CsI</strong>，其中：</p>
<ul>
  <li>通道总数：1024</li>
  <li>200通道：608keV Bi-214 (U-238系)</li>
  <li>490通道：1460keV K-40 (自然放射性)</li>
  <li>868通道：2614keV Tl-208 (Th-232系)</li>
</ul>

<h1 id="数据处理方法">数据处理方法</h1>

<h2 id="snip背景剔除">SNIP背景剔除</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SNIP</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">iterations</span><span class="o">=</span><span class="mi">40</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">iterations</span> <span class="o">=</span> <span class="n">iterations</span>
        <span class="n">self</span><span class="p">.</span><span class="n">background_</span> <span class="o">=</span> <span class="bp">None</span>
        <span class="n">self</span><span class="p">.</span><span class="n">net_counts_</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="k">def</span> <span class="nf">fit</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">counts</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">
        对输入谱执行 SNIP 背景估计（对数域版本）
        </span><span class="sh">"""</span>
        <span class="n">counts</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">(</span><span class="n">counts</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
        <span class="c1"># 为防止 log(0)，加上一个很小的常数
</span>        <span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">counts</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
        <span class="n">bg</span> <span class="o">=</span> <span class="n">y</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>

        <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="n">iterations</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
            <span class="n">temp</span> <span class="o">=</span> <span class="n">bg</span><span class="p">.</span><span class="nf">copy</span><span class="p">()</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="o">-</span> <span class="n">m</span><span class="p">):</span>
                <span class="n">bg</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="n">bg</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="p">(</span><span class="n">temp</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">m</span><span class="p">]</span> <span class="o">+</span> <span class="n">temp</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">m</span><span class="p">])</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>

        <span class="c1"># 转回线性域
</span>        <span class="n">bg</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">exp</span><span class="p">(</span><span class="n">bg</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="n">self</span><span class="p">.</span><span class="n">background_</span> <span class="o">=</span> <span class="n">bg</span>
        <span class="n">self</span><span class="p">.</span><span class="n">net_counts_</span> <span class="o">=</span> <span class="n">counts</span> <span class="o">-</span> <span class="n">bg</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">background_</span>

    <span class="k">def</span> <span class="nf">transform</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">counts</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">background_</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">net_counts_</span>

    <span class="k">def</span> <span class="nf">fit_transform</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">counts</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">net_counts_</span>

    <span class="k">def</span> <span class="nf">process_csv</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">input_csv</span><span class="p">,</span> <span class="n">output_csv</span><span class="p">,</span> <span class="n">counts_column</span><span class="o">=</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">
        批量处理 CSV 文件
        :param input_csv: 原始 CSV 文件路径
        :param output_csv: 输出 CSV 文件路径
        :param counts_column: 包含通道计数的列名
        </span><span class="sh">"""</span>
        <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="n">input_csv</span><span class="p">)</span>

        <span class="n">bg_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">net_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">net_total_counts</span> <span class="o">=</span> <span class="p">[]</span> <span class="c1"># 添加用于存储净计数总和的列表
</span>
        <span class="k">for</span> <span class="n">idx</span><span class="p">,</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="nf">iterrows</span><span class="p">():</span>
            <span class="c1"># 将字符串列表解析为 Python list
</span>            <span class="n">counts</span> <span class="o">=</span> <span class="n">ast</span><span class="p">.</span><span class="nf">literal_eval</span><span class="p">(</span><span class="n">row</span><span class="p">[</span><span class="n">counts_column</span><span class="p">])</span>
            <span class="n">bg</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span>
            <span class="n">net</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">transform</span><span class="p">(</span><span class="n">counts</span><span class="p">)</span>

            <span class="n">bg_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([</span><span class="nf">round</span><span class="p">(</span><span class="nf">float</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">bg</span><span class="p">])</span>
            <span class="n">net_list</span><span class="p">.</span><span class="nf">append</span><span class="p">([</span><span class="nf">round</span><span class="p">(</span><span class="nf">float</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">net</span><span class="p">])</span>
            <span class="n">net_total_counts</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="nf">round</span><span class="p">(</span><span class="nf">float</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nf">sum</span><span class="p">(</span><span class="n">net</span><span class="p">)),</span> <span class="mi">3</span><span class="p">))</span>

        <span class="c1"># 新增列
</span>        <span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">background</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">bg_list</span>
        <span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">net_counts</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">net_list</span>
        <span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">net_total_counts</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="n">net_total_counts</span>  <span class="c1"># 添加净计数总和列
</span>        <span class="c1"># 创建新的DataFrame，只保留需要的字段
</span>        <span class="n">new_df</span> <span class="o">=</span> <span class="n">df</span><span class="p">[[</span><span class="sh">'</span><span class="s">farm_id</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">collection_time</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">latitude</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">longitude</span><span class="sh">'</span><span class="p">,</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">background</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">net_counts</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">net_total_counts</span><span class="sh">'</span><span class="p">]]</span>

        <span class="c1"># 保存
</span>        <span class="n">new_df</span><span class="p">.</span><span class="nf">to_csv</span><span class="p">(</span><span class="n">output_csv</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">处理完成，已保存到 </span><span class="si">{</span><span class="n">output_csv</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">plot_spectrum</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">input_csv</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">counts_col</span><span class="o">=</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">,</span> <span class="n">bg_col</span><span class="o">=</span><span class="sh">'</span><span class="s">background</span><span class="sh">'</span><span class="p">,</span> <span class="n">net_col</span><span class="o">=</span><span class="sh">'</span><span class="s">net_counts</span><span class="sh">'</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">
        绘制指定索引的谱线对比图（原始谱、背景、净峰谱）
        :param input_csv: CSV 文件路径（包含 counts、background、net_counts）
        :param index: 要绘制的行号（从 0 开始）
        :param counts_col: 原始谱列名
        :param bg_col: 背景谱列名
        :param net_col: 净谱列名
        </span><span class="sh">"""</span>
        <span class="c1"># 读取数据
</span>        <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="n">input_csv</span><span class="p">)</span>

        <span class="c1"># 检查索引是否超出范围
</span>        <span class="k">if</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="nf">len</span><span class="p">(</span><span class="n">df</span><span class="p">):</span>
            <span class="k">raise</span> <span class="nc">IndexError</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">索引 </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s"> 超出范围（共有 </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">df</span><span class="p">)</span><span class="si">}</span><span class="s"> 条数据）</span><span class="sh">"</span><span class="p">)</span>

        <span class="c1"># 解析为 Python 列表
</span>        <span class="n">counts</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">(</span><span class="n">ast</span><span class="p">.</span><span class="nf">literal_eval</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">counts_col</span><span class="p">]),</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
        <span class="n">background</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">(</span><span class="n">ast</span><span class="p">.</span><span class="nf">literal_eval</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">bg_col</span><span class="p">]),</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>
        <span class="n">net</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">array</span><span class="p">(</span><span class="n">ast</span><span class="p">.</span><span class="nf">literal_eval</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">index</span><span class="p">,</span> <span class="n">net_col</span><span class="p">]),</span> <span class="n">dtype</span><span class="o">=</span><span class="nb">float</span><span class="p">)</span>

        <span class="c1"># 绘图
</span>        <span class="n">plt</span><span class="p">.</span><span class="nf">figure</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">plot</span><span class="p">(</span><span class="n">counts</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">原始谱</span><span class="sh">'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sh">'</span><span class="s">black</span><span class="sh">'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">plot</span><span class="p">(</span><span class="n">background</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">背景谱</span><span class="sh">'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sh">'</span><span class="s">orange</span><span class="sh">'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">plot</span><span class="p">(</span><span class="n">net</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">净峰谱</span><span class="sh">'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="sh">'</span><span class="s">red</span><span class="sh">'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">1.2</span><span class="p">)</span>

        <span class="n">plt</span><span class="p">.</span><span class="nf">xlabel</span><span class="p">(</span><span class="sh">'</span><span class="s">通道号</span><span class="sh">'</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">ylabel</span><span class="p">(</span><span class="sh">'</span><span class="s">计数</span><span class="sh">'</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">title</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">SNIP 谱线对比图（第 </span><span class="si">{</span><span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s"> 条数据）</span><span class="sh">'</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">legend</span><span class="p">()</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="mf">0.3</span><span class="p">)</span>
        <span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
</code></pre></div></div>

<p>调用方法（默认迭代40次）：</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># SNIP初始化
</span><span class="n">snip</span> <span class="o">=</span> <span class="nc">SNIP</span><span class="p">(</span><span class="n">iterations</span><span class="o">=</span><span class="mi">40</span><span class="p">)</span>
<span class="n">snip</span><span class="p">.</span><span class="nf">process_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">../file/point_area_a.csv</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">../file/snip_processed_area_a.csv</span><span class="sh">'</span><span class="p">,</span> <span class="n">counts_column</span><span class="o">=</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">)</span>
<span class="n">snip</span><span class="p">.</span><span class="nf">process_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">../file/point_area_b.csv</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">../file/snip_processed_area_b.csv</span><span class="sh">'</span><span class="p">,</span> <span class="n">counts_column</span><span class="o">=</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">)</span>

<span class="n">snip</span><span class="p">.</span><span class="nf">plot_spectrum</span><span class="p">(</span><span class="sh">'</span><span class="s">../file/snip_processed_area_a.csv</span><span class="sh">'</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="mi">99</span><span class="p">)</span>  <span class="c1"># 绘制第100条数据
</span></code></pre></div></div>
<p><img src="https://img10.360buyimg.com/ddimg/jfs/t1/358857/36/3421/22607/690af6ecF11f4a31b/c69fa9e95096a4f5.jpg" alt="image.png" /></p>

<h2 id="窗口求和">窗口求和</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">SpectrumPeak</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">
    根据指定通道和窗口范围计算能谱峰强度（窗口求和），支持批量多通道处理
    </span><span class="sh">"""</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">counts_column</span><span class="o">=</span><span class="sh">'</span><span class="s">counts</span><span class="sh">'</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">counts_column</span> <span class="o">=</span> <span class="n">counts_column</span>  <span class="c1"># CSV 中的计数列名
</span>
    <span class="k">def</span> <span class="nf">compute_peak</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">counts</span><span class="p">,</span> <span class="n">channel</span><span class="p">,</span> <span class="n">window</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">
        计算指定通道附近窗口的计数和
        :param counts: 样本的计数列表
        :param channel: 中心通道
        :param window: 窗口范围（正负范围）
        :return: 窗口内计数和
        </span><span class="sh">"""</span>
        <span class="n">start</span> <span class="o">=</span> <span class="nf">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">channel</span> <span class="o">-</span> <span class="n">window</span><span class="p">)</span>
        <span class="n">end</span> <span class="o">=</span> <span class="nf">min</span><span class="p">(</span><span class="nf">len</span><span class="p">(</span><span class="n">counts</span><span class="p">),</span> <span class="n">channel</span> <span class="o">+</span> <span class="n">window</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>  <span class="c1"># python 切片是左闭右开
</span>        <span class="k">return</span> <span class="nf">sum</span><span class="p">(</span><span class="n">counts</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">end</span><span class="p">])</span>

    <span class="k">def</span> <span class="nf">process_csv</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">input_csv</span><span class="p">,</span> <span class="n">output_csv</span><span class="p">,</span> <span class="n">peaks</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">
        处理 CSV 文件，计算峰强度，并保存新 CSV
        :param input_csv: 输入 CSV 路径
        :param output_csv: 输出 CSV 路径
        :param peaks: dict, {列名: (中心通道, 窗口范围)}
                      例如: {</span><span class="sh">'</span><span class="s">K40_peak</span><span class="sh">'</span><span class="s">: (490, 5), </span><span class="sh">'</span><span class="s">U238_peak</span><span class="sh">'</span><span class="s">: (600, 7)}
        </span><span class="sh">"""</span>
        <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="n">input_csv</span><span class="p">)</span>

        <span class="c1"># 先把原 counts 列解析成列表，方便重复使用
</span>        <span class="n">counts_list</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">self</span><span class="p">.</span><span class="n">counts_column</span><span class="p">].</span><span class="nf">apply</span><span class="p">(</span><span class="n">ast</span><span class="p">.</span><span class="n">literal_eval</span><span class="p">)</span>

        <span class="k">for</span> <span class="n">peak_name</span><span class="p">,</span> <span class="p">(</span><span class="n">channel</span><span class="p">,</span> <span class="n">window</span><span class="p">)</span> <span class="ow">in</span> <span class="n">peaks</span><span class="p">.</span><span class="nf">items</span><span class="p">():</span>
            <span class="n">df</span><span class="p">[</span><span class="n">peak_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">counts_list</span><span class="p">.</span><span class="nf">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">compute_peak</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">channel</span><span class="p">,</span> <span class="n">window</span><span class="p">))</span>

        <span class="n">df</span><span class="p">.</span><span class="nf">to_csv</span><span class="p">(</span><span class="n">output_csv</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
        <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">处理完成，已保存到 </span><span class="si">{</span><span class="n">output_csv</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<p>调用方法：</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 获取指定通道的窗口求和
</span><span class="n">sp</span> <span class="o">=</span> <span class="nc">SpectrumPeak</span><span class="p">(</span><span class="n">counts_column</span><span class="o">=</span><span class="sh">'</span><span class="s">net_counts</span><span class="sh">'</span><span class="p">)</span>

<span class="c1"># 定义要计算的峰，键是输出列名，值是 (中心通道, 窗口)
</span><span class="n">peaks_to_calc</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">'</span><span class="s">Bi214_peak</span><span class="sh">'</span><span class="p">:</span> <span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span>
    <span class="sh">'</span><span class="s">K40_peak</span><span class="sh">'</span><span class="p">:</span> <span class="p">(</span><span class="mi">490</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span>
    <span class="sh">'</span><span class="s">Tl208_peak</span><span class="sh">'</span><span class="p">:</span> <span class="p">(</span><span class="mi">868</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
<span class="p">}</span>

<span class="n">sp</span><span class="p">.</span><span class="nf">process_csv</span><span class="p">(</span>
    <span class="n">input_csv</span><span class="o">=</span><span class="sh">'</span><span class="s">../file/snip_processed_area_a.csv</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">output_csv</span><span class="o">=</span><span class="sh">'</span><span class="s">../file/data_peaks_area_a.csv</span><span class="sh">'</span><span class="p">,</span>
    <span class="n">peaks</span><span class="o">=</span><span class="n">peaks_to_calc</span>
<span class="p">)</span>
</code></pre></div></div>

<h2 id="主成分分析">主成分分析</h2>

<pre><code class="language-PYTHON">class SpectrumPCA:
    """
    光谱主成分分析（PCA）封装类
    支持 CSV 文件批量处理、自动保存主成分结果
    """

    def __init__(self, n_components=3):
        """
        初始化 PCA 模型
        :param n_components: 主成分数量（默认 3）
        """
        self.n_components = n_components
        self.pca_model = PCA(n_components=n_components)
        self.components_ = None
        self.explained_variance_ratio_ = None
        self.pca_scores_ = None

    def _parse_counts(self, counts_str):
        """
        将字符串 "[1, 2, 3]" 转换为 numpy 数组
        """
        if isinstance(counts_str, str):
            return np.array(ast.literal_eval(counts_str), dtype=float)
        elif isinstance(counts_str, (list, np.ndarray)):
            return np.array(counts_str, dtype=float)
        else:
            raise ValueError("counts 列格式错误，应为字符串或列表")

    def fit_transform_csv(self, input_csv, output_csv=None, counts_column='counts'):
        """
        从 CSV 文件读取能谱数据，执行 PCA 并保存结果
        :param input_csv: 输入 CSV 文件路径
        :param output_csv: 输出 CSV 文件路径（可选）
        :param counts_column: 光谱列名（默认 'counts'）
        :return: 包含 PCA 结果的 DataFrame
        """
        # === 1. 读取 CSV ===
        df = pd.read_csv(input_csv)
        print(f"✅ 成功读取 {len(df)} 条样本")

        # === 2. 转换 counts 列为矩阵 ===
        spectra = np.array([self._parse_counts(c) for c in df[counts_column]])
        print(f"每条光谱通道数: {spectra.shape[1]}")

        # === 3. 执行 PCA ===
        self.pca_scores_ = self.pca_model.fit_transform(spectra)
        self.components_ = self.pca_model.components_
        self.explained_variance_ratio_ = self.pca_model.explained_variance_ratio_

        # === 4. 将主成分结果添加到 df ===
        for i in range(self.n_components):
            df[f'PC{i+1}'] = self.pca_scores_[:, i]

        # === 5. 可选保存结果 ===
        if output_csv:
            base, ext = os.path.splitext(output_csv)
            result_csv = f"{base}_pca.csv"
            variance_csv = f"{base}_variance.csv"

            # 保存主成分结果
            df.to_csv(result_csv, index=False)

            # 保存方差贡献率信息
            var_df = pd.DataFrame({
                'PC': [f'PC{i+1}' for i in range(self.n_components)],
                'Explained_Variance_Ratio': self.explained_variance_ratio_
            })
            var_df.to_csv(variance_csv, index=False)

            print(f"📂 PCA结果已保存至: {result_csv}")
            print(f"📊 方差贡献率已保存至: {variance_csv}")

        return df

    # ==================== 方差贡献率柱状图 ====================
    def plot_variance_ratio(self):
        if self.explained_variance_ratio_ is None:
            raise ValueError("请先运行 fit_transform_csv()")
        plt.figure(figsize=(6, 4))
        plt.bar(range(1, len(self.explained_variance_ratio_) + 1),
                self.explained_variance_ratio_ * 100, color='skyblue')
        plt.xlabel("主成分")
        plt.ylabel("方差贡献率 (%)")
        plt.title("PCA 方差贡献率")
        plt.grid(True, alpha=0.3)
        plt.show()

    # ==================== PCA 散点图 ====================
    def plot_scatter(self, df, pc_x=1, pc_y=2):
        pc_x_col = f'PC{pc_x}'
        pc_y_col = f'PC{pc_y}'
        if pc_x_col not in df.columns or pc_y_col not in df.columns:
            raise ValueError("DataFrame 中缺少指定的 PCA 列，请先运行 fit_transform_csv()")
        plt.figure(figsize=(6, 6))
        plt.scatter(df[pc_x_col], df[pc_y_col], alpha=0.7, edgecolor='k')
        plt.xlabel(pc_x_col)
        plt.ylabel(pc_y_col)
        plt.title(f'PCA 散点图 ({pc_x_col} vs {pc_y_col})')
        plt.grid(True, alpha=0.3)
        plt.show()

    # ==================== 空间可视化 ====================
    def plot_spatial_pca(self, df, pc=1, lat_col='latitude', lon_col='longitude',
                         method='scatter', grid_res=100):
        """
        绘制 PCA 空间分布图
        :param df: 包含经纬度和 PCA 列的 DataFrame
        :param pc: PCA 编号（1,2,3,...）
        :param lat_col: 纬度列名
        :param lon_col: 经度列名
        :param method: 'scatter' 或 'heatmap'
        :param grid_res: 热力图插值网格分辨率
        """
        pc_col = f'PC{pc}'
        if pc_col not in df.columns:
            raise ValueError(f"{pc_col} 列不存在，请先运行 PCA")

        lats = df[lat_col].values
        lons = df[lon_col].values
        values = df[pc_col].values

        if method == 'scatter':
            plt.figure(figsize=(8, 6))
            sc = plt.scatter(lons, lats, c=values, cmap='viridis', s=50, edgecolor='k')
            plt.colorbar(sc, label=f'{pc_col} 值')
            plt.xlabel('Longitude')
            plt.ylabel('Latitude')
            plt.title(f'{pc_col} 空间散点图')
            plt.grid(True, alpha=0.3)
            plt.show()

        elif method == 'heatmap':
            # 创建网格
            lon_grid = np.linspace(lons.min(), lons.max(), grid_res)
            lat_grid = np.linspace(lats.min(), lats.max(), grid_res)
            lon_mesh, lat_mesh = np.meshgrid(lon_grid, lat_grid)

            # 插值
            grid_values = griddata((lons, lats), values, (lon_mesh, lat_mesh), method='cubic')

            # 绘制热力图
            plt.figure(figsize=(8, 6))
            hm = plt.imshow(grid_values, origin='lower',
                            extent=(lons.min(), lons.max(), lats.min(), lats.max()),
                            cmap='viridis', aspect='auto')
            plt.colorbar(hm, label=f'{pc_col} 值')
            plt.scatter(lons, lats, c='white', s=10, alpha=0.8)  # 原始点位置
            plt.xlabel('Longitude')
            plt.ylabel('Latitude')
            plt.title(f'{pc_col} 空间热力图')
            plt.show()
        else:
            raise ValueError("method 参数只支持 'scatter' 或 'heatmap'")
</code></pre>

<p>调用方法：</p>
<pre><code class="language-PYTHON"># PCA初始化
pca = SpectrumPCA(n_components=3)

# 对 CSV 数据进行 PCA 并保存结果
df_area_a = pca.fit_transform_csv(input_csv="../file/snip_processed_area_a.csv", output_csv="../file/area_a.csv", counts_column='net_counts')
df_area_b = pca.fit_transform_csv(input_csv="../file/snip_processed_area_b.csv", output_csv="../file/area_b.csv", counts_column='net_counts')


# # 绘制方差贡献率图
# pca.plot_variance_ratio()

# # 绘制主成分散点图（PC1 vs PC2）
# pca.plot_scatter(df_area_a, pc_x=1, pc_y=2)

# # 绘制空间散点图（PC1）
# pca.plot_spatial_pca(df_area_a, pc=1, method='scatter')

# # 绘制空间热力图（PC1）
# pca.plot_spatial_pca(df_area_a, pc=1, method='heatmap', grid_res=200)
</code></pre>

<p><img src="https://img11.360buyimg.com/ddimg/jfs/t1/337796/37/23098/88535/690af732F81de8443/9ba32d410e1ad71d.jpg" alt="image.png" /></p>]]></content><author><name>yucol</name></author><category term="tech" /><category term="土壤处方图" /><category term="GIS" /><category term="智慧农业" /><category term="能谱" /><summary type="html"><![CDATA[能谱信息]]></summary></entry><entry><title type="html">土壤处方图</title><link href="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%A4%84%E6%96%B9%E5%9B%BE.html" rel="alternate" type="text/html" title="土壤处方图" /><published>2025-10-29T00:00:00+00:00</published><updated>2025-10-29T00:00:00+00:00</updated><id>https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%A4%84%E6%96%B9%E5%9B%BE</id><content type="html" xml:base="https://yucol.top/tech/%E5%9C%9F%E5%A3%A4%E5%A4%84%E6%96%B9%E5%9B%BE.html"><![CDATA[<h1 id="土壤成分处方图调查">土壤成分处方图调查</h1>

<h2 id="何为土壤成分处方图">何为土壤成分处方图</h2>

<blockquote>
  <p>“土壤成分处方图”（Soil Composition Prescription Map）是近年来智慧农业、精准施肥领域非常重要的一个概念。</p>
</blockquote>

<p>土壤成分处方图是根据不同地块的土壤养分含量与作物需求，生成的 <strong>“精准管理地图”，指导农民在不同区域定量施肥、灌溉或改良土壤</strong>。</p>

<table>
  <thead>
    <tr>
      <th>功能</th>
      <th>说明</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>精准施肥</strong></td>
      <td>不同地块施不同的量，避免浪费或缺肥</td>
    </tr>
    <tr>
      <td><strong>提高产量和品质</strong></td>
      <td>让作物在“适宜”的营养环境下生长</td>
    </tr>
    <tr>
      <td><strong>减少环境污染</strong></td>
      <td>减少氮磷流失、面源污染</td>
    </tr>
    <tr>
      <td><strong>辅助农机作业</strong></td>
      <td>无人机、变量施肥机可以按图执行处方作业</td>
    </tr>
  </tbody>
</table>

<h2 id="相关企业">相关企业</h2>

<h3 id="soiloptix">SoilOptix</h3>

<blockquote>
  <p>技术原理：https://soiloptix.com/case_studies/from-start-to-finish-how-soiloptix-technology-works/</p>
</blockquote>

<p>这是一家位于加拿大的公司，创始人为<strong>Paul Raymer</strong>。SoilOptix的传感器以伽马射线为核心原理，以<strong>827个点/公顷</strong>的分辨率对土壤内腐殖物产生的<strong>铯-137，铀-238，钍-232，钾-40</strong>物质进行测量。由于这些物质稳定且可重复，因此监测到的数据能够在多年内有效使用。</p>

<p><img src="https://img14.360buyimg.com/ddimg/jfs/t1/245133/12/34980/94840/69017b7aF45bd11ff/da58b5daa3d8caba.jpg" alt="土壤成分检测技术原理.png" /></p>

<p>此外，还需要从田间提取土壤样本。每个田地的样品数量基于田地大小，源于每 8 英亩（3 公顷）1 个样品的比率;对于小于 25ac （10ha）的田地，至少需要 3 个样品。</p>

<p>结合传感器收集数据和土地采样信息，SoilOptix团队会在48小时内为用户们提供一份全方位展现耕地<strong>物理特性、宏微观影响因素、PH值、以及有机物在内的25层土壤分析图</strong>，直观全面地展现土壤的状况，帮助种植者们准确判断“种什么、种哪里、什么时候种”的问题，达到产量目标、作物死亡率和土壤肥力的最佳平衡，以获得经济和环境的双赢。</p>

<blockquote>
  <p>对80 英亩（32 公顷）的田地进行了采样，其中获得了 10 个土壤样本</p>
</blockquote>

<p><img src="https://img10.360buyimg.com/ddimg/jfs/t1/346203/11/18232/52594/69017b8cF8fda93bb/8c789ad7d1393d65.jpg" alt="土壤成分表1.png" />
<img src="https://img13.360buyimg.com/ddimg/jfs/t1/341748/32/19262/65918/69017b9aF0f848466/10fe61aec42c70ed.jpg" alt="土壤成分表2.png" /></p>

<p>Greasley在德比郡经营一家占地80公顷的畜牧场，SoilOptix的Terramap 产品提供的PH值地图图层让他节约了大量成本。“如果对所有田地都进行无差别的酸碱度调整措施，那么每公顷土地需要消耗5000公斤石灰；但SoilOptix使我们明确需要调整的点位，按需施用仅消耗石灰3050公斤，节省下来的石灰和人力成本可到达每公顷 66 英镑。”</p>
<blockquote>
  <p>原文：https://www.hutchinsons.co.uk/how-terramap-is-saving-a-derbyshire-farmer-over-66-ha-in-liming-costs/</p>
</blockquote>

<p><img src="https://img12.360buyimg.com/ddimg/jfs/t1/353557/31/2454/49186/69017bacFe7614018/011fd237278426b8.jpg" alt="pH.png" /></p>

<h4 id="可用的地表土壤属性">可用的地表土壤属性</h4>

<table>
  <thead>
    <tr>
      <th>分类</th>
      <th>英文属性名</th>
      <th>中文名称</th>
      <th>说明</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>🟤 <strong>Physical Properties（物理性质）</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td>Clay</td>
      <td>黏土含量 (%)</td>
      <td>土壤中粒径 &lt;0.002 mm 部分的比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Sand</td>
      <td>砂含量 (%)</td>
      <td>土壤中粒径 0.05–2 mm 部分的比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Loam</td>
      <td>壤土比例</td>
      <td>表征土壤质地平衡程度的综合指标</td>
    </tr>
    <tr>
      <td> </td>
      <td>Silt</td>
      <td>粉粒含量 (%)</td>
      <td>土壤中粒径 0.002–0.05 mm 部分的比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Cation Exchange Capacity (CEC)</td>
      <td>阳离子交换量</td>
      <td>土壤吸附与交换养分的能力（单位：cmol(+)/kg）</td>
    </tr>
    <tr>
      <td> </td>
      <td>Organic Matter</td>
      <td>有机质含量 (%)</td>
      <td>土壤中来源于动植物残体的有机成分</td>
    </tr>
    <tr>
      <td>🧪 <strong>Macro Nutrients（常量营养元素）</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td>Calcium (Ca)</td>
      <td>钙含量</td>
      <td>促进根系发育、改善土壤结构</td>
    </tr>
    <tr>
      <td> </td>
      <td>Calcium-Base Saturation</td>
      <td>钙基饱和度 (%)</td>
      <td>交换性阳离子中钙所占比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Calcium-Magnesium Ratio</td>
      <td>钙镁比</td>
      <td>土壤中钙、镁离子的平衡关系</td>
    </tr>
    <tr>
      <td> </td>
      <td>Magnesium Base Saturation</td>
      <td>镁基饱和度 (%)</td>
      <td>交换性阳离子中镁所占比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Potassium-Magnesium Ratio</td>
      <td>钾镁比</td>
      <td>土壤中钾、镁比例关系</td>
    </tr>
    <tr>
      <td> </td>
      <td>pH</td>
      <td>酸碱度</td>
      <td>土壤溶液的酸碱水平</td>
    </tr>
    <tr>
      <td> </td>
      <td>Phosphorus-Bicarb</td>
      <td>重碳酸盐磷（Olsen-P）</td>
      <td>碳酸盐或中性土壤中可有效利用的磷</td>
    </tr>
    <tr>
      <td> </td>
      <td>Phosphorus-Bray</td>
      <td>Bray 磷</td>
      <td>酸性土壤中可利用磷含量</td>
    </tr>
    <tr>
      <td> </td>
      <td>Potassium (K)</td>
      <td>钾含量</td>
      <td>作物生长必需的大量元素之一</td>
    </tr>
    <tr>
      <td> </td>
      <td>Potassium Base Saturation</td>
      <td>钾基饱和度 (%)</td>
      <td>交换性阳离子中钾所占比例</td>
    </tr>
    <tr>
      <td> </td>
      <td>Nitrate as N</td>
      <td>硝态氮 (以N计)</td>
      <td>土壤中可直接被植物吸收的氮形态</td>
    </tr>
    <tr>
      <td>🧩 <strong>Micro Nutrients（微量营养元素）</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td>Aluminum (Al)</td>
      <td>铝</td>
      <td>高浓度时对根系有毒性</td>
    </tr>
    <tr>
      <td> </td>
      <td>Boron (B)</td>
      <td>硼</td>
      <td>与花粉活性、细胞分裂相关</td>
    </tr>
    <tr>
      <td> </td>
      <td>Copper (Cu)</td>
      <td>铜</td>
      <td>参与酶活性与光合作用</td>
    </tr>
    <tr>
      <td> </td>
      <td>Iron (Fe)</td>
      <td>铁</td>
      <td>参与叶绿素合成与电子传递</td>
    </tr>
    <tr>
      <td> </td>
      <td>Manganese (Mn)</td>
      <td>锰</td>
      <td>参与光合作用与氧化还原反应</td>
    </tr>
    <tr>
      <td> </td>
      <td>Sodium (Na)</td>
      <td>钠</td>
      <td>过高会导致盐害</td>
    </tr>
    <tr>
      <td> </td>
      <td>Sulfur (S)</td>
      <td>硫</td>
      <td>构成氨基酸和蛋白质的成分</td>
    </tr>
    <tr>
      <td> </td>
      <td>Zinc (Zn)</td>
      <td>锌</td>
      <td>参与酶合成、促进生长素生成</td>
    </tr>
    <tr>
      <td>⚙️ <strong>Complex Models（复杂模型/衍生属性）</strong></td>
      <td> </td>
      <td> </td>
      <td> </td>
    </tr>
    <tr>
      <td> </td>
      <td>Plant Available Water</td>
      <td>植物可利用水分</td>
      <td>指植物能从土壤中吸收的有效水分含量</td>
    </tr>
    <tr>
      <td> </td>
      <td>Elevation</td>
      <td>高程</td>
      <td>地表相对高度（m）</td>
    </tr>
    <tr>
      <td> </td>
      <td>Leakability</td>
      <td>渗透性/漏失性</td>
      <td>土壤水分渗漏或流失的易发生程度</td>
    </tr>
  </tbody>
</table>

<p><img src="https://img14.360buyimg.com/ddimg/jfs/t1/338165/12/26237/75425/69017bbcFfdbd1be9/ce08aa71094da58b.jpg" alt="土壤属性图.png" /></p>

<p>Nutrien VRA 石灰施用处方图（Lime Prescription Zone Map）。基于可变速率施用（Variable Rate Application, VRA）技术生成的石灰施用区划图。
该图用于根据土壤酸碱度（pH）、缓冲能力、有机质及养分状况等差异，将农田划分为多个管理区，从而确定每个区域应施用的石灰量。通过这种方式，可在保证土壤酸碱平衡的同时，实现精准施肥、节约成本、提高作物产量和土壤健康。</p>

<p><img src="https://img10.360buyimg.com/ddimg/jfs/t1/344696/28/19412/114353/69017bcdFd81cbd56/f1202d693505add1.jpg" alt="石灰施用处方图.png" /></p>

<h3 id="medusa-explorations">Medusa Explorations</h3>

<p>公司位于荷兰，其部门<strong>Medusa Radiometrics</strong>制作伽马射线传感器等来检土壤成分。通过伽马射线可以测量土壤中的<strong>有机质、湿度</strong>等指标。</p>

<h4 id="应用案例土壤测绘以提高作物产量">应用案例：土壤测绘以提高作物产量</h4>

<p>在西班牙北部半干旱地区的许多依靠雨水灌溉、位于高海拔的农田中，土壤容易受到侵蚀、压实，并且有机质含量较低。因此，这些土壤的养分供应能力和持水能力都较差。再加上降水量低，导致作物产量低，农业生产难以持续。</p>

<p>欧盟 LIFE+ 项目“Crops for better soil”（更优土壤作物项目）旨在通过一系列农艺措施，证明这些农田的（土地和经济）产出都可以得到改善。该项目通过在100多个农田上实施这些措施，在相当大的规模上验证了这一方法。这些农田总面积为300至400公顷，分布在卡斯蒂利亚-拉曼查（瓜达拉哈拉）、阿拉贡、纳瓦拉以及卡斯蒂利亚-莱昂（萨莫拉/贝纳文特）等地区，由14至20位不同的农民所有。</p>

<p>所采取的农艺措施包括：转向有机农业、增加作物轮作、引入豆科植物，以及推广更适应寒冷半干旱气候的传统作物品种。</p>

<p>为评估哪些作物和哪种轮作适合哪种田地，并评估土壤的改良情况，设计了土壤数据采集和监测计划。要评估的土壤和田地特性包括土壤质地、土壤有机质、耕作层深度、压实度、海拔、坡度和养分浓度。</p>

<p><img src="https://img11.360buyimg.com/ddimg/jfs/t1/346022/27/19006/131292/69017bddF11e82a9b/622cc1c2a8c5bb9e.jpg" alt="土壤测绘方法.png" /></p>

<p>所使用的多种数据类型示例如下：IGME 提供的激光雷达数字高程模型（LIDAR DEM，左上）、IGME 的 MAGNA 50 地质图（左上）、由伽马射线能谱仪获取的⁴⁰K（钾-40）浓度分布图（右上）、由地质雷达（GPR）获取的 0–20 厘米深度时间切片（左下）以及 20–80 厘米深度时间切片（右下）。这些农田位于马德里以东、卡斯蒂利亚-拉曼查大区的伊亚纳（Illana）附近。</p>

<p><img src="https://img14.360buyimg.com/ddimg/jfs/t1/345687/17/18435/206272/69017bfbF11731775/1a1d7b0e59008efe.jpg" alt="多种数据类型图.png" /></p>

<h2 id="处方图相关方法">处方图相关方法</h2>

<h3 id="一般流程">一般流程</h3>

<ul>
  <li>土壤样品采集</li>
  <li>实验室数据分析</li>
  <li>结果解读</li>
  <li>肥料施用建议</li>
</ul>

<h3 id="插值方法">插值方法</h3>

<h4 id="克里金插值">克里金插值</h4>

<h4 id="反距离权重插值">反距离权重插值</h4>

<h3 id="土壤数据点采样测土方法">土壤数据点采样测土方法</h3>

<h4 id="grid测土">Grid测土</h4>

<p>Grid 测土通常要求较高的采样密度，可能会在1-5米的网格范围内进行采样，适合对土壤特性进行高精度分析。通过规则网格化采样，保证了数据点的均匀分布。每个网格中需要采样点，因此会产生大量数据，有时需要大量的计算和分析。</p>

<h4 id="zone测土">Zone测土</h4>

<p>土壤的地力成分并不是线性分布的，因此采用格点方式进行分割，取得的测土数据很有可能不能反映出地块的全貌。</p>

<p>Zone 测土技术，首先利用土壤、遥感、地形等数据将地块划分为若干区域（Zones），每个区域土壤情况相对一致，这样可以适当减少每个区域的测土数量，降低成本；同时，按照区域的方式进行测土规划，能够更加精准地还原土壤数据的全貌，提升测土的精度。</p>

<p>当拿到基于Zone的测土数据后，我们还可以进一步优化Zone Map，让地块的区域划分更加合理，为后续的测土规划、变量处方图的制作提供更加精准而有力的支撑。</p>

<table>
  <thead>
    <tr>
      <th>项目</th>
      <th>Grid 测土</th>
      <th>Zone 测土</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>采样密度</strong></td>
      <td>高（密集的采样点）</td>
      <td>低（每个区域一个采样点）</td>
    </tr>
    <tr>
      <td><strong>数据量</strong></td>
      <td>数据量大，分析复杂</td>
      <td>数据量少，分析简单</td>
    </tr>
    <tr>
      <td><strong>精度</strong></td>
      <td>高（适合详细分析）</td>
      <td>低（适合大致评估）</td>
    </tr>
    <tr>
      <td><strong>成本</strong></td>
      <td>高（需要更多资源）</td>
      <td>低（节省资源和时间）</td>
    </tr>
    <tr>
      <td><strong>适用范围</strong></td>
      <td>精准农业、土地细分分析</td>
      <td>土地管理、大规模区域性调查</td>
    </tr>
    <tr>
      <td><strong>应用场景</strong></td>
      <td>土壤成分精确分析，作物精准施肥</td>
      <td>区域性土壤质量监测和评估</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p><strong>Grid采样、 EC–Zone Sampling、SoilOptix采样区别：<a href="https://soiloptix.com/wp-content/uploads/2021/11/SoilOptix-Technology-Comparison-Study.pdf">pdf</a></strong></p>
</blockquote>

<h4 id="medusa伽马射线光谱仪数据采集示例">Medusa伽马射线光谱仪数据采集示例</h4>

<blockquote>
  <p>原文：https://the.medusa.institute/wiki/Working-version/on-accuracy-and-spatial-resolution#Onaccuracyandspatialresolution-RepeatedAverage</p>
</blockquote>

<p>我们使用了一个来自农田的示例数据集，该农田面积为 300×400 米，测绘时的测线间距为 12 米。采样频率为 1 Hz，车辆以 3.6 km/h 的速度行驶，因此相邻采样点之间的距离约为 1 米。</p>

<p>该数据集使用三种不同的方法进行了分析。在本次分析中，采用了一个较大的样本窗口，即对 15 个数据点进行平均处理，这会放大平滑效果。通常情况下，并不会采用 15 个样本的滑动平均，而是会使用较少数量的样本进行平滑计算。</p>

<ul>
  <li>Fit each 方法</li>
</ul>

<blockquote>
  <p>对每一个采样点分别进行拟合或插值计算，而不对数据进行平滑或平均。</p>
</blockquote>

<p>该方法的结果显示了所有数据点，但整体表现出一定的“噪声”特征。使用简单的 <strong>反距离加权（IDW）</strong> 生成的地图中，可以看到许多极端的高值和低值，因此需要应用 <strong>空间平滑滤波器（spatial smoothing filter）</strong> 来去除噪声、改善空间连续性。</p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddavbd47c62cad236a4c73965b3448411173.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddav83dc554e20b3d359685d043d13fc9806.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<ul>
  <li>滑动平均法（Running Average）</li>
</ul>

<p>采用滑动平均方法后，数据在测线方向上表现出明显的高值区和低值区。在对数据进行插值时，这种各向异性（anisotropy）特征十分明显，呈现出沿测线分布的条带状结构。</p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddavf36cd7e04114d6b1aa853986de2a5690.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddav97776506c4baeb2759a3a6bb1615eba5.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<ul>
  <li>重复平均法（Repeated Average）</li>
</ul>

<p>经过重复平均分析后，数据集中包含的采样点数量减少，但这些点不再沿测线分布，而是呈现出更加均匀（随机）分布的空间格局。</p>

<p>每个采样点都具有较高的精度，这在插值结果中得到了体现：高值和低值在网格中表现为<strong>“斑点状（bullet-like）”分布</strong>。这种分布是<strong>各向同性（omnidirectional）</strong>的，不会出现沿测线方向的过度采样现象。</p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddavc1034695c3ec33378bfa845be05af6b6.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<p><img src="https://the.medusa.institute/__attachments/351143217/worddav585b384fb2f913fbb88573e4c54ec955.png?inst-v=e2dfee16-41a5-4baf-a20c-9cfa3e0d709c" alt="alt text" /></p>

<table>
  <thead>
    <tr>
      <th>方法名称</th>
      <th>原理概述</th>
      <th>数据特征</th>
      <th>优点</th>
      <th>缺点</th>
      <th>典型应用场景</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Fit each</strong></td>
      <td>对每个采样点单独拟合，不进行平滑或平均处理；所有原始数据直接用于插值</td>
      <td>数据完整，细节丰富，但噪声大</td>
      <td>保留全部原始信息；便于发现异常点</td>
      <td>噪声多，空间连续性差；需后续平滑处理</td>
      <td>初步数据探索；噪声分析；原始信号验证</td>
    </tr>
    <tr>
      <td><strong>Running Average</strong></td>
      <td>沿采样路径对连续数据进行滑动平均（如每次平均 3–5 个点）</td>
      <td>沿测线方向平滑，形成条带结构（各向异性明显）</td>
      <td>降低噪声；平滑局部波动</td>
      <td>容易产生条纹效应；丢失横向空间特征</td>
      <td>实时监测；沿轨迹数据平滑；线性传感分析</td>
    </tr>
    <tr>
      <td><strong>Repeated Average</strong></td>
      <td>多次平均并重新采样，使点分布更加均匀随机</td>
      <td>数据点更少但分布均匀；无明显方向性</td>
      <td>平滑且各向同性；噪声低，插值效果更自然</td>
      <td>细节略有损失；需较多计算</td>
      <td>制图与建模；空间插值；最终成果展示</td>
    </tr>
  </tbody>
</table>

<h3 id="土壤成分探测方法">土壤成分探测方法</h3>

<table>
  <thead>
    <tr>
      <th><strong>方法</strong></th>
      <th><strong>技术描述</strong></th>
      <th><strong>应用/用途</strong></th>
      <th><strong>优点</strong></th>
      <th><strong>缺点</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>传统土壤采样与实验室分析</strong></td>
      <td>采集土壤样本并通过实验室化学分析测试主要养分（氮、磷、钾等）、pH、有机质、CEC等。</td>
      <td>提供准确的土壤化学成分、物理性质和有机质含量的详细信息。</td>
      <td>高精度，标准化方法。</td>
      <td>成本高，时间长，空间覆盖局限。</td>
    </tr>
    <tr>
      <td><strong>卫星遥感</strong></td>
      <td>通过卫星影像分析土壤反射率，推算土壤湿度、养分含量等，利用光谱反射特性分析土壤组成。</td>
      <td>适用于大范围土壤监测，提供土壤湿度、养分分布等的空间分布数据。</td>
      <td>大范围覆盖，实时监测。</td>
      <td>精度较低，受环境影响较大，无法提供详细的土壤成分数据。</td>
    </tr>
    <tr>
      <td><strong>无人机遥感</strong></td>
      <td>利用高分辨率相机和光谱传感器搭载于无人机，获取农田土壤的详细影像和光谱数据。</td>
      <td>提供农田的详细土壤信息，评估土壤湿度、肥力等。</td>
      <td>高分辨率，快速获取数据。</td>
      <td>高成本，需要技术支持，数据处理量大。</td>
    </tr>
    <tr>
      <td><strong>电磁探测</strong></td>
      <td>使用电磁感应仪（EMI）通过测量土壤电导率来推算土壤盐分、湿度等。</td>
      <td>适用于评估土壤的盐分、含水量等。</td>
      <td>非侵入性，快速获取大范围数据。</td>
      <td>精度较低，无法直接测量详细的化学成分。</td>
    </tr>
    <tr>
      <td><strong>伽马射线探测</strong></td>
      <td>通过探测土壤中的自然放射性元素（如钾-40、铀、钍等）辐射推算土壤的矿物成分。</td>
      <td>用于评估土壤中钾、铀、钍等元素的分布情况，间接推算土壤的矿物成分。</td>
      <td>非侵入性，快速覆盖大面积区域。</td>
      <td>只能推算某些矿物成分（如钾），不能测量氮、磷、pH等成分。</td>
    </tr>
    <tr>
      <td><strong>近红外光谱（NIR）分析</strong></td>
      <td>测量土壤样本的光谱反射特性，通过光谱数据推算土壤的有机质含量、水分和矿物成分。</td>
      <td>用于快速分析土壤的有机质、湿度、矿物含量等。</td>
      <td>高效、非破坏性，快速获取数据。</td>
      <td>需与化学分析结合，且精度受限于光谱数据模型的准确性。</td>
    </tr>
    <tr>
      <td><strong>土壤传感器网络</strong></td>
      <td>在农田中布设土壤传感器，实时监测土壤的pH、湿度、温度、盐分等。</td>
      <td>提供实时的土壤数据，帮助农民实时调整施肥、灌溉等管理措施。</td>
      <td>实时监测，高精度，连续数据。</td>
      <td>高成本，设备需要维护，数据处理复杂。</td>
    </tr>
    <tr>
      <td><strong>智能农业管理系统</strong></td>
      <td>集成多种技术，如土壤传感器、遥感数据、电磁探测等，进行土壤监测和作物管理，结合数据分析提供精准管理建议。</td>
      <td>用于精确管理农业生产，提供<strong>土壤处方图</strong>和作物管理方案。</td>
      <td>集成多种技术，提供全面、精准的农业管理方案。</td>
      <td>需要高成本的设备和技术支持，系统复杂，数据分析需要强大的计算能力。</td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>使用传感器探测土壤成分时，需要结合土壤采样来分析。</p>
</blockquote>

<h2 id="分辨率">分辨率</h2>

<blockquote>
  <p>原文：https://soiloptix.com/case_studies/a-clear-advantage-over-zone-sampling/#ftn7</p>
</blockquote>

<p>SoilOptix提供高分辨率的数据采集方法：335个数据点/英亩。下面分别是SoilOptix方法和其他方法的钾元素分布图。高分辨率数据可以分析更加细微的小区域，同时避免将存在差异性的区域连接成一片。</p>

<div style="display: flex; justify-content: center; ">
  <img src="https://soiloptix.com/wp-content/uploads/2024/04/Figure3_northeast-section.png" alt="图片1" style="margin-right: 10px;" />
  <img src="https://soiloptix.com/wp-content/uploads/2024/04/Figure4_Zone-Script_Muriate-of-Potash-219x300.png" alt="图片2" />
</div>]]></content><author><name>yucol</name></author><category term="tech" /><category term="土壤处方图" /><category term="GIS" /><category term="智慧农业" /><summary type="html"><![CDATA[土壤成分处方图调查]]></summary></entry></feed>