<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>白菜</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <icon>https://veridianbyte.github.io/images/feed-icon.png</icon>
  <id>https://veridianbyte.github.io/</id>
  <link href="https://veridianbyte.github.io/" rel="alternate"/>
  <link href="https://veridianbyte.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 白菜</rights>
  <subtitle>在世界的尽头你我又会遇见什么</subtitle>
  <title>ヾ(≧▽≦*)o飛鳥じんじん</title>
  <updated>2026-03-17T01:56:11.021Z</updated>
  <entry>
    <author>
      <name>白菜</name>
    </author>
    <category term="强化学习" scheme="https://veridianbyte.github.io/categories/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
    <category term="X-Plane" scheme="https://veridianbyte.github.io/tags/X-Plane/"/>
    <category term="Gymnasium" scheme="https://veridianbyte.github.io/tags/Gymnasium/"/>
    <category term="PPO" scheme="https://veridianbyte.github.io/tags/PPO/"/>
    <category term="DQN" scheme="https://veridianbyte.github.io/tags/DQN/"/>
    <content>
      <![CDATA[<p>基于X-Plane12飞行模拟的强化学习Open AI Gymnasium深度强化学习环境</p><span id="more"></span><blockquote><p>项目地址：<a href="https://github.com/VeridianByte/XPlaneGymEnvs">https://github.com/VeridianByte/XPlaneGymEnvs</a></p></blockquote><h2 id="一-项目概述与背景"><a class="markdownIt-Anchor" href="#一-项目概述与背景"></a> 一、项目概述与背景</h2><h3 id="11-为什么需要飞行模拟器强化学习环境"><a class="markdownIt-Anchor" href="#11-为什么需要飞行模拟器强化学习环境"></a> 1.1 为什么需要飞行模拟器强化学习环境</h3><p>   强化学习作为人工智能领域最具前景的研究方向之一，其应用范围已经从传统的棋类游戏、机器人控制逐步扩展到更为复杂的现实世界任务。飞行控制作为一个典型的高维度连续控制问题，天然适合作为强化学习算法的测试bed。然而，真实的飞行测试不仅成本高昂，还存在严重的安全风险。飞行模拟器的出现为这一问题提供了优雅的解决方案——它能够在虚拟环境中还原真实的物理飞行特性，同时消除实际飞行的风险和成本。</p><p>   X-Plane作为业界最逼真的飞行模拟器之一，其采用的有限元分析方法能够产生高度真实的飞行物理数据，这使得它成为验证飞行控制强化学习算法的理想平台。XPlaneGymEnvs项目正是基于这一理念应运而生，它为研究者和开发者提供了一个无缝衔接的桥梁，将强大的X-Plane模拟器与现代强化学习框架Gymnasium连接在一起。</p><h3 id="12-xplanegymenvs项目简介"><a class="markdownIt-Anchor" href="#12-xplanegymenvs项目简介"></a> 1.2 XPlaneGymEnvs项目简介</h3><p>   XPlaneGymEnvs是一个专门为X-Plane飞行模拟器设计的Gymnasium兼容强化学习环境库。该项目的核心目标是通过UDP通信协议实现与X-Plane的无缝连接，使强化学习智能体能够在真实的飞行物理环境中进行训练和学习。值得注意的是，整个连接过程不需要在X-Plane中安装任何第三方插件，这极大地简化了环境配置的复杂度，同时也保证了与不同版本X-Plane的兼容性。</p><p>   该项目支持离散和连续两种动作空间类型，能够满足不同强化学习算法的需求。无论是想要训练一个简单的俯仰角控制智能体，还是开发一个完整的飞行控制系统，XPlaneGymEnvs都提供了足够的灵活性和可扩展性。通过继承项目中提供的XPlaneEnv基类，开发者还可以创建完全定制化的飞行任务环境，以适应特定的研究需求。</p><h3 id="13-核心技术特性"><a class="markdownIt-Anchor" href="#13-核心技术特性"></a> 1.3 核心技术特性</h3><p>   XPlaneGymEnvs项目的技术特性体现了其在设计上的深思熟虑。首先，完全兼容Gymnasium接口意味着它能够与市面上主流的强化学习库无缝集成，包括Stable-Baselines3、Ray RLlib、CleanRL等。开发者无需修改已有的训练代码，即可直接将现有的强化学习算法应用于飞行控制任务。</p><p>   无插件式的UDP通信架构是该项目最显著的技术创新之一。传统上，与X-Plane的集成往往需要开发专门的插件程序，这不仅增加了开发难度，还可能面临版本兼容性问题。XPlaneGymEnvs通过监听X-Plane主动推送的UDP数据包来获取飞行状态信息，同时通过UDP向X-Plane发送控制指令，实现了完全外部化的环境交互模式。</p><p>   项目提供了丰富的环境参数配置选项。从初始位置、高度、速度到目标的俯仰角和滚转角，开发者可以根据具体任务需求灵活设置。这种高度的可配置性使得XPlaneGymEnvs既可以作为简单的基准环境用于算法比较，也能够满足复杂定制化任务的需求。</p><h2 id="二-技术架构深度解析"><a class="markdownIt-Anchor" href="#二-技术架构深度解析"></a> 二、技术架构深度解析</h2><h3 id="21-系统整体架构"><a class="markdownIt-Anchor" href="#21-系统整体架构"></a> 2.1 系统整体架构</h3><p>   XPlaneGymEnvs的系统架构采用经典的客户端-服务器模式，其中X-Plane模拟器作为服务器端，负责维护真实的飞行物理状态；而Python环境作为客户端，通过UDP协议进行双向通信。这种架构设计的优势在于保持了各组件的独立性，使得系统具有更好的可维护性和可扩展性。</p><p>   从通信流程来看，整个系统的运作可以分为以下几个关键步骤：智能体根据当前状态选择一个动作；该动作通过UDP协议发送给X-Plane；X-Plane执行相应的控制输入并更新飞行状态；更新后的状态信息通过UDP传回Python环境，用于计算奖励和生成下一帧观察。这种循环往复的过程构成了强化学习训练的基础。</p><h3 id="22-udp通信机制详解"><a class="markdownIt-Anchor" href="#22-udp通信机制详解"></a> 2.2 UDP通信机制详解</h3><p>   UDP（用户数据报协议）作为一种无连接的传输层协议，在实时性要求高的应用场景中具有显著优势。X-Plane内置了对UDP通信的支持，它会定期向指定IP地址和端口广播飞行数据，包括位置、速度、姿态角度、引擎参数等丰富信息。同时，X-Plane也能够接收来自外部程序的UDP数据包来设置飞机的控制输入。</p><p>   XPlaneGymEnvs默认使用49000端口进行通信，这也是X-Plane的默认UDP端口。在实际使用中，如果遇到端口冲突或需要同时运行多个环境实例，开发者可以通过配置参数修改端口号。通信超时设置为1秒，这在大多数网络环境下都是合理的默认值，既能保证数据更新的及时性，又给网络延迟留有足够的余地。</p><p>   数据包的解析是UDP通信中的关键环节。X-Plane发送的UDP数据包采用特定的二进制格式，包含多种不同类型的数据。XPlaneGymEnvs内部实现了完整的数据包解析逻辑，能够从原始字节流中提取出智能体所需的各类状态信息，包括但不限于：地理坐标（纬度、经度、高度）、空速、垂直速度、俯仰角、滚转角、航向角等。</p><h3 id="23-强化学习环境接口设计"><a class="markdownIt-Anchor" href="#23-强化学习环境接口设计"></a> 2.3 强化学习环境接口设计</h3><p>   作为Gymnasium兼容的环境，XPlaneGymEnvs严格遵循该框架定义的标准接口规范。核心接口包括：reset()方法用于初始化环境并返回初始观察；step(action)方法执行一个时间步并返回新的观察、奖励、终止标志和附加信息；render()方法用于可视化（虽然飞行模拟器本身已经提供了图形界面）；close()方法用于清理资源。</p><p>   观察空间（Observation Space）的设计需要平衡信息完整性与计算效率。XPlaneGymEnvs默认提供的观察包括飞行器当前的高度、速度、俯仰角、滚转角等关键状态变量。这些状态变量经过适当的缩放处理后，以连续空间的形式提供给智能体。开发者也可以通过继承和重写来定义完全自定义的观察空间。</p><p>   动作空间（Action Space）的设计是该项目的另一个亮点。支持离散动作空间意味着智能体可以从预定义的动作集合中选择一个执行，例如“增加俯仰角”、“减少俯仰角”、“保持不变”等。而连续动作空间则允许智能体输出任意范围内的浮点数，实现更精细的控制。两种模式的切换通过简单的配置参数即可完成，这使得同一环境可以适配不同类型的强化学习算法。</p><h3 id="24-奖励函数设计"><a class="markdownIt-Anchor" href="#24-奖励函数设计"></a> 2.4 奖励函数设计</h3><p>   奖励函数的设计对于强化学习训练的成功至关重要。XPlaneGymEnvs采用了一种基于状态追踪的奖励机制，其核心思想是鼓励智能体将飞行器引导至目标状态，同时惩罚偏离目标的行为。具体来说，奖励函数会计算当前状态与目标状态之间的差异，并将其转换为负值作为惩罚项。当智能体成功达成目标状态时，还会给予额外的正向奖励作为激励。</p><p>   这种奖励设计方式的优势在于它提供了持续的梯度信号，使智能体能够逐步学习到正确的控制策略。然而，在实际应用中，奖励函数的具体参数（如各状态分量的权重）往往需要根据具体任务进行调优。项目文档中提供了详细的奖励计算公式，开发者可以根据自己的需求进行修改。</p><h2 id="三-快速开始指南"><a class="markdownIt-Anchor" href="#三-快速开始指南"></a> 三、快速开始指南</h2><h3 id="31-环境准备与安装"><a class="markdownIt-Anchor" href="#31-环境准备与安装"></a> 3.1 环境准备与安装</h3><p>   在开始使用XPlaneGymEnvs之前，需要确保系统满足以下基本要求。首先，也是最显然的，需要安装X-Plane 12飞行模拟器。根据项目文档，该环境可能也支持较低版本的X-Plane，但为了获得最佳的兼容性，推荐使用最新版本。其次，需要安装Python 3.8或更高版本，以及几个关键的依赖库：gymnasium（强化学习环境标准库）和numpy（数值计算库）。</p><p>安装过程非常简单，只需几个命令行即可完成。首先从GitHub仓库克隆项目代码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/VeridianByte/XPlaneGymEnvs.git</span><br><span class="line"><span class="built_in">cd</span> XPlaneGymEnvs</span><br></pre></td></tr></table></figure><p>然后使用pip安装项目及其依赖：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -e .</span><br></pre></td></tr></table></figure><p>安装完成后，可以通过一个简单的Python脚本来验证环境是否正确安装：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">from</span> XPlaneGym <span class="keyword">import</span> XPlaneEnv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 尝试创建环境实例</span></span><br><span class="line">env = gym.make(<span class="string">&quot;XPlane-v0&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;环境创建成功！&quot;</span>)</span><br><span class="line">env.close()</span><br></pre></td></tr></table></figure><h3 id="32-x-plane配置"><a class="markdownIt-Anchor" href="#32-x-plane配置"></a> 3.2 X-Plane配置</h3><p>   正确的X-Plane配置是确保通信正常工作的前提。启动X-Plane后，需要进行以下设置：首先进入设置菜单，找到“网络”（Network）设置部分；在UDP设置中，确保端口号设置为49000（这是默认值）；确认UDP广播功能处于启用状态。</p><p>   为了获得稳定的训练体验，建议在X-Plane中设置合适的天气条件和飞行时长。默认的天气是晴朗无风，这适合大多数基准测试任务。对于特定的训练场景，如暴风雨条件下的飞行控制，可以相应调整天气设置。飞行时长的设置取决于具体的训练任务——较短的飞行时长有助于加快迭代速度，而较长的飞行时长则允许智能体学习更复杂的任务。</p><h3 id="33-第一个训练脚本"><a class="markdownIt-Anchor" href="#33-第一个训练脚本"></a> 3.3 第一个训练脚本</h3><p>   让我们创建一个最简单的训练脚本来体验整个流程。这个脚本将使用随机策略作为基线，虽然不会学到有意义的策略，但能够验证整个系统的端到端工作流程：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">from</span> XPlaneGym <span class="keyword">import</span> XPlaneEnv</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建环境，配置为连续动作空间</span></span><br><span class="line">env = gym.make(</span><br><span class="line">    <span class="string">&quot;XPlane-v0&quot;</span>,</span><br><span class="line">    ip=<span class="string">&#x27;127.0.0.1&#x27;</span>,           <span class="comment"># X-Plane IP地址</span></span><br><span class="line">    port=<span class="number">49000</span>,               <span class="comment"># UDP端口</span></span><br><span class="line">    timeout=<span class="number">1.0</span>,              <span class="comment"># 通信超时</span></span><br><span class="line">    pause_delay=<span class="number">0.05</span>,         <span class="comment"># 动作执行延迟</span></span><br><span class="line">    starting_latitude=<span class="number">37.558</span>, <span class="comment"># 初始纬度（首尔金浦国际机场附近）</span></span><br><span class="line">    starting_longitude=<span class="number">126.790</span>,</span><br><span class="line">    starting_altitude=<span class="number">3000.0</span>, <span class="comment"># 初始高度3000米</span></span><br><span class="line">    starting_velocity=<span class="number">100.0</span>,  <span class="comment"># 初始速度100节</span></span><br><span class="line">    starting_pitch_range=<span class="number">10.0</span>,</span><br><span class="line">    starting_roll_range=<span class="number">20.0</span>,</span><br><span class="line">    random_desired_state=<span class="literal">True</span>,</span><br><span class="line">    desired_pitch_range=<span class="number">5.0</span>,</span><br><span class="line">    desired_roll_range=<span class="number">10.0</span>,</span><br><span class="line">    continuous_actions=<span class="literal">True</span>   <span class="comment"># 使用连续动作空间</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行几个随机步骤来验证环境工作正常</span></span><br><span class="line">obs, info = env.reset()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;初始观察: <span class="subst">&#123;obs&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> episode <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">3</span>):</span><br><span class="line">    obs, info = env.reset()</span><br><span class="line">    total_reward = <span class="number">0</span></span><br><span class="line">    done = <span class="literal">False</span></span><br><span class="line">    step = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">not</span> done <span class="keyword">and</span> step &lt; <span class="number">100</span>:</span><br><span class="line">        <span class="comment"># 随机选择动作</span></span><br><span class="line">        action = env.action_space.sample()</span><br><span class="line">        obs, reward, terminated, truncated, info = env.step(action)</span><br><span class="line">        total_reward += reward</span><br><span class="line">        done = terminated <span class="keyword">or</span> truncated</span><br><span class="line">        step += <span class="number">1</span></span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Episode <span class="subst">&#123;episode + <span class="number">1</span>&#125;</span>: 奖励 = <span class="subst">&#123;total_reward:<span class="number">.2</span>f&#125;</span>, 步数 = <span class="subst">&#123;step&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">env.close()</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;测试完成！&quot;</span>)</span><br></pre></td></tr></table></figure><p>   运行脚本之前，请确保X-Plane已经启动并处于飞行状态。脚本会尝试连接X-Plane并开始交互。如果一切配置正确，应该能够看到X-Plane中的飞机按照随机控制输入运动，同时脚本输出各回合的奖励信息。</p><h3 id="34-使用示例智能体训练"><a class="markdownIt-Anchor" href="#34-使用示例智能体训练"></a> 3.4 使用示例智能体训练</h3><p>   项目自带了一个完整的DQN（Deep Q-Network）训练示例，展示了如何将标准的强化学习算法应用于飞行控制任务。该示例位于agent_examples/dqn_example目录下，包含了训练脚本和必要的配置文件。</p><p>运行DQN示例的基本步骤如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> agent_examples/dqn_example</span><br><span class="line">python train_dqn.py</span><br></pre></td></tr></table></figure><p>   训练过程可能需要相当长的时间，取决于硬件性能和设置的训练轮数。训练过程中，智能体会逐渐学习如何控制飞机达到目标姿态。训练完成后，模型会自动保存到指定目录，可以用于后续的评估和部署。</p><h2 id="四-深度使用教程"><a class="markdownIt-Anchor" href="#四-深度使用教程"></a> 四、深度使用教程</h2><h3 id="41-创建自定义环境"><a class="markdownIt-Anchor" href="#41-创建自定义环境"></a> 4.1 创建自定义环境</h3><p>   对于大多数研究应用来说，现成的XPlane-v0环境可能无法满足特定需求。XPlaneGymEnvs的设计允许开发者通过继承XPlaneEnv基类来创建完全自定义的环境。以下是创建自定义环境的基本流程：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> XPlaneGym <span class="keyword">import</span> XPlaneEnv</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyCustomXPlaneEnv</span>(<span class="title class_ inherited__">XPlaneEnv</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;自定义飞行控制环境&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(**kwargs)</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_compute_reward</span>(<span class="params">self, state, desired_state</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        自定义奖励函数</span></span><br><span class="line"><span class="string">        可以根据任务需求设计不同的奖励机制</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 计算状态差异</span></span><br><span class="line">        pitch_diff = <span class="built_in">abs</span>(state[<span class="number">2</span>] - desired_state[<span class="number">0</span>])</span><br><span class="line">        roll_diff = <span class="built_in">abs</span>(state[<span class="number">3</span>] - desired_state[<span class="number">1</span>])</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 高度保持奖励</span></span><br><span class="line">        altitude_reward = -<span class="built_in">abs</span>(state[<span class="number">1</span>] - <span class="number">3000.0</span>) * <span class="number">0.01</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 姿态控制奖励（越接近目标姿态，奖励越高）</span></span><br><span class="line">        attitude_reward = -<span class="number">0.5</span> * (pitch_diff + roll_diff)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 达到目标的额外奖励</span></span><br><span class="line">        <span class="keyword">if</span> pitch_diff &lt; <span class="number">2.0</span> <span class="keyword">and</span> roll_diff &lt; <span class="number">2.0</span>:</span><br><span class="line">            attitude_reward += <span class="number">10.0</span></span><br><span class="line">            </span><br><span class="line">        <span class="keyword">return</span> altitude_reward + attitude_reward</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_get_observation</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">        自定义观察空间</span></span><br><span class="line"><span class="string">        可以选择包含或排除特定的状态分量</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 使用自定义的状态表示</span></span><br><span class="line">        obs = np.array([</span><br><span class="line">            <span class="variable language_">self</span>.current_state.altitude / <span class="number">10000.0</span>,      <span class="comment"># 归一化高度</span></span><br><span class="line">            <span class="variable language_">self</span>.current_state.velocity / <span class="number">500.0</span>,       <span class="comment"># 归一化速度</span></span><br><span class="line">            <span class="variable language_">self</span>.current_state.pitch / <span class="number">90.0</span>,           <span class="comment"># 归一化俯仰角</span></span><br><span class="line">            <span class="variable language_">self</span>.current_state.roll / <span class="number">180.0</span>,           <span class="comment"># 归一化滚转角</span></span><br><span class="line">            <span class="variable language_">self</span>.current_state.heading / <span class="number">360.0</span>,       <span class="comment"># 归一化航向</span></span><br><span class="line">        ], dtype=np.float32)</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> obs</span><br><span class="line"></span><br><span class="line"><span class="comment"># 注册自定义环境</span></span><br><span class="line">gym.register(</span><br><span class="line">    <span class="built_in">id</span>=<span class="string">&#x27;XPlane-custom-v0&#x27;</span>,</span><br><span class="line">    entry_point=<span class="string">&#x27;__main__:MyCustomXPlaneEnv&#x27;</span>,</span><br><span class="line">    kwargs=&#123;...&#125;  <span class="comment"># 传递给构造函数的参数</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="42-环境参数详解"><a class="markdownIt-Anchor" href="#42-环境参数详解"></a> 4.2 环境参数详解</h3><p>   XPlaneGymEnvs提供了丰富的配置参数，用于控制环境的各个方面。深入理解这些参数的作用对于有效地使用该环境至关重要。</p><p>   <strong>通信参数</strong>包括ip和port，分别指定X-Plane的IP地址和UDP端口。默认值127.0.0.1:49000适用于本地运行的情况。如果X-Plane运行在另一台机器上，需要相应修改IP地址。timeout参数控制UDP通信的超时时间，在网络环境不佳时可以适当增大该值。</p><p>   <strong>初始状态参数</strong>控制智能体开始时的飞行状态。starting_latitude和starting_longitude设置初始位置，默认值指向首尔金浦国际机场附近区域，这是一个适合进行飞行训练的地点。starting_altitude设置初始高度（单位为米），starting_velocity设置初始空速（单位为节）。starting_pitch_range和starting_roll_range控制初始姿态的随机范围，增加这些值会使初始状态更加多样化，从而提高智能体的泛化能力。</p><p><strong>   目标状态参数</strong>定义智能体需要达成的目标。random_desired_state参数决定是否在每个回合开始时随机生成目标状态。当设置为True时，desired_pitch_range和desired_roll_range控制目标姿态的随机范围。这种随机化目标的设计有助于训练更加鲁棒的智能体。</p><p><strong>   动作空间参数</strong>通过continuous_actions布尔值切换离散和连续动作模式。在连续模式下，智能体输出的是连续的控制量；在离散模式下，智能体从有限的动作集合中选择。</p><h2 id="五-代码实现分析"><a class="markdownIt-Anchor" href="#五-代码实现分析"></a> 五、代码实现分析</h2><h3 id="51-核心环境类"><a class="markdownIt-Anchor" href="#51-核心环境类"></a> 5.1 核心环境类</h3><p>   XPlaneEnv类是整个项目的核心，它封装了与X-Plane通信的所有逻辑以及强化学习环境的标准接口。深入理解这个类的实现对于有效使用和扩展该环境非常有帮助。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">XPlaneEnv</span>(gym.Env):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;X-Plane强化学习环境主类&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    metadata = &#123;<span class="string">&#x27;render_modes&#x27;</span>: [<span class="string">&#x27;human&#x27;</span>]&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, ...</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 初始化UDP通信</span></span><br><span class="line">        <span class="variable language_">self</span>.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</span><br><span class="line">        <span class="variable language_">self</span>.socket.settimeout(timeout)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 初始化环境参数</span></span><br><span class="line">        <span class="variable language_">self</span>.ip = ip</span><br><span class="line">        <span class="variable language_">self</span>.port = port</span><br><span class="line">        <span class="comment"># ... 其他参数初始化</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 定义观察空间和动作空间</span></span><br><span class="line">        <span class="variable language_">self</span>._define_spaces()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">reset</span>(<span class="params">self, seed=<span class="literal">None</span>, options=<span class="literal">None</span></span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;重置环境到初始状态&quot;&quot;&quot;</span></span><br><span class="line">        <span class="built_in">super</span>().reset(seed=seed)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 随机设置初始状态</span></span><br><span class="line">        <span class="variable language_">self</span>._set_initial_conditions()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 等待环境稳定</span></span><br><span class="line">        <span class="variable language_">self</span>._wait_for_stable_state()</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._get_observation(), &#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">step</span>(<span class="params">self, action</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;执行一个时间步&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 发送控制指令到X-Plane</span></span><br><span class="line">        <span class="variable language_">self</span>._send_control(action)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 等待状态更新</span></span><br><span class="line">        time.sleep(<span class="variable language_">self</span>.pause_delay)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 获取新状态</span></span><br><span class="line">        state = <span class="variable language_">self</span>._get_current_state()</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 计算奖励</span></span><br><span class="line">        reward = <span class="variable language_">self</span>._compute_reward(state, <span class="variable language_">self</span>.desired_state)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 检查是否终止</span></span><br><span class="line">        terminated = <span class="variable language_">self</span>._check_termination(state)</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 获取观察</span></span><br><span class="line">        obs = <span class="variable language_">self</span>._get_observation(state)</span><br><span class="line">        </span><br><span class="line">        info = &#123;<span class="string">&#x27;state&#x27;</span>: state, <span class="string">&#x27;desired&#x27;</span>: <span class="variable language_">self</span>.desired_state&#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> obs, reward, terminated, <span class="literal">False</span>, info</span><br></pre></td></tr></table></figure><p>   这个实现遵循了Gymnasium的标准接口规范，使得它能够与各种强化学习库无缝兼容。</p><h3 id="52-udp通信实现"><a class="markdownIt-Anchor" href="#52-udp通信实现"></a> 5.2 UDP通信实现</h3><p>   UDP通信的实现涉及数据包的发送和接收两个方向。以下是简化版的通信逻辑：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_send_control</span>(<span class="params">self, action</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;发送控制指令到X-Plane&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="variable language_">self</span>.continuous_actions:</span><br><span class="line">        <span class="comment"># 连续动作：直接使用浮点值</span></span><br><span class="line">        pitch_input = action[<span class="number">0</span>]</span><br><span class="line">        roll_input = action[<span class="number">1</span>]</span><br><span class="line">        yaw_input = action[<span class="number">2</span>] <span class="keyword">if</span> <span class="built_in">len</span>(action) &gt; <span class="number">2</span> <span class="keyword">else</span> <span class="number">0.0</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># 离散动作：映射到具体控制量</span></span><br><span class="line">        action_mapping = &#123;</span><br><span class="line">            <span class="number">0</span>: [<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>],      <span class="comment"># 无操作</span></span><br><span class="line">            <span class="number">1</span>: [<span class="number">0.1</span>, <span class="number">0</span>, <span class="number">0</span>],    <span class="comment"># 俯仰向上</span></span><br><span class="line">            <span class="number">2</span>: [-<span class="number">0.1</span>, <span class="number">0</span>, <span class="number">0</span>],   <span class="comment"># 俯仰向下</span></span><br><span class="line">            <span class="number">3</span>: [<span class="number">0</span>, <span class="number">0.1</span>, <span class="number">0</span>],    <span class="comment"># 向左滚转</span></span><br><span class="line">            <span class="number">4</span>: [<span class="number">0</span>, -<span class="number">0.1</span>, <span class="number">0</span>],   <span class="comment"># 向右滚转</span></span><br><span class="line">            <span class="comment"># ... 其他动作映射</span></span><br><span class="line">        &#125;</span><br><span class="line">        pitch_input, roll_input, yaw_input = action_mapping[action]</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 构造并发送UDP数据包</span></span><br><span class="line">    control_data = <span class="variable language_">self</span>._build_control_packet(pitch_input, roll_input, yaw_input)</span><br><span class="line">    <span class="variable language_">self</span>.socket.sendto(control_data, (<span class="variable language_">self</span>.ip, <span class="variable language_">self</span>.port))</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_receive_state</span>(<span class="params">self</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;从X-Plane接收状态数据&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        data, addr = <span class="variable language_">self</span>.socket.recvfrom(<span class="number">4096</span>)</span><br><span class="line">        state = <span class="variable language_">self</span>._parse_state_packet(data)</span><br><span class="line">        <span class="keyword">return</span> state</span><br><span class="line">    <span class="keyword">except</span> socket.timeout:</span><br><span class="line">        <span class="comment"># 超时处理：返回最后已知状态或默认值</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable language_">self</span>._get_default_state()</span><br></pre></td></tr></table></figure><h3 id="53-状态解析"><a class="markdownIt-Anchor" href="#53-状态解析"></a> 5.3 状态解析</h3><p>   X-Plane发送的UDP数据包采用特定的二进制格式。解析这些数据是环境实现中最复杂的部分之一。以下是关键的状态解析逻辑：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_parse_state_packet</span>(<span class="params">self, data</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;解析X-Plane状态数据包&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># X-Plane UDP数据包格式：</span></span><br><span class="line">    <span class="comment"># 前5个字节是头部信息，之后是多个数据索引-值对</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 跳过头部</span></span><br><span class="line">    offset = <span class="number">5</span></span><br><span class="line">    </span><br><span class="line">    state = XPlaneState()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">while</span> offset &lt; <span class="built_in">len</span>(data) - <span class="number">8</span>:</span><br><span class="line">        <span class="comment"># 读取数据索引</span></span><br><span class="line">        index = <span class="built_in">int</span>.from_bytes(data[offset:offset+<span class="number">4</span>], byteorder=<span class="string">&#x27;big&#x27;</span>)</span><br><span class="line">        offset += <span class="number">4</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 读取数据值</span></span><br><span class="line">        value = struct.unpack(<span class="string">&#x27;f&#x27;</span>, data[offset:offset+<span class="number">4</span>])[<span class="number">0</span>]</span><br><span class="line">        offset += <span class="number">4</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 根据索引提取所需数据</span></span><br><span class="line">        <span class="comment"># X-Plane数据索引定义请参考官方文档</span></span><br><span class="line">        <span class="keyword">if</span> index == <span class="number">0</span>:  <span class="comment"># 海平面气压高度</span></span><br><span class="line">            state.altitude = value</span><br><span class="line">        <span class="keyword">elif</span> index == <span class="number">17</span>:  <span class="comment"># 真空速</span></span><br><span class="line">            state.velocity = value</span><br><span class="line">        <span class="keyword">elif</span> index == <span class="number">11</span>:  <span class="comment"># 俯仰角</span></span><br><span class="line">            state.pitch = value</span><br><span class="line">        <span class="keyword">elif</span> index == <span class="number">12</span>:  <span class="comment"># 滚转角</span></span><br><span class="line">            state.roll = value</span><br><span class="line">        <span class="keyword">elif</span> index == <span class="number">13</span>:  <span class="comment"># 航向角</span></span><br><span class="line">            state.heading = value</span><br><span class="line">        <span class="comment"># ... 其他状态字段</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> state</span><br></pre></td></tr></table></figure><p>   这部分代码展示了如何从原始UDP数据包中提取飞行状态信息。实际实现中需要处理更多的数据索引和可能的异常情况。</p><h2 id="六-实践案例"><a class="markdownIt-Anchor" href="#六-实践案例"></a> 六、实践案例</h2><h3 id="61-姿态控制任务"><a class="markdownIt-Anchor" href="#61-姿态控制任务"></a> 6.1 姿态控制任务</h3><p>   姿态控制是飞行模拟中最基础也是最重要的任务之一。目标是训练一个智能体，能够将飞机的俯仰角和滚转角控制到指定的目标值。以下是针对这个任务的完整解决方案：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> gymnasium <span class="keyword">as</span> gym</span><br><span class="line"><span class="keyword">from</span> stable_baselines3 <span class="keyword">import</span> PPO</span><br><span class="line"><span class="keyword">from</span> XPlaneGym <span class="keyword">import</span> XPlaneEnv</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建姿态控制环境</span></span><br><span class="line">env = gym.make(</span><br><span class="line">    <span class="string">&quot;XPlane-v0&quot;</span>,</span><br><span class="line">    continuous_actions=<span class="literal">True</span>,</span><br><span class="line">    starting_altitude=<span class="number">3000.0</span>,</span><br><span class="line">    starting_velocity=<span class="number">150.0</span>,</span><br><span class="line">    random_desired_state=<span class="literal">True</span>,</span><br><span class="line">    desired_pitch_range=<span class="number">15.0</span>,</span><br><span class="line">    desired_roll_range=<span class="number">30.0</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用PPO算法进行训练</span></span><br><span class="line">model = PPO(</span><br><span class="line">    <span class="string">&quot;MlpPolicy&quot;</span>,</span><br><span class="line">    env,</span><br><span class="line">    learning_rate=<span class="number">3e-4</span>,</span><br><span class="line">    n_steps=<span class="number">2048</span>,</span><br><span class="line">    batch_size=<span class="number">64</span>,</span><br><span class="line">    n_epochs=<span class="number">10</span>,</span><br><span class="line">    gamma=<span class="number">0.99</span>,</span><br><span class="line">    verbose=<span class="number">1</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 训练100000步</span></span><br><span class="line">model.learn(total_timesteps=<span class="number">100000</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存模型</span></span><br><span class="line">model.save(<span class="string">&quot;ppo_xplane_attitude&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 评估模型</span></span><br><span class="line">eval_env = gym.make(<span class="string">&quot;XPlane-v0&quot;</span>, continuous_actions=<span class="literal">True</span>)</span><br><span class="line">obs, _ = eval_env.reset()</span><br><span class="line">rewards = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> _ <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">    action, _ = model.predict(obs, deterministic=<span class="literal">True</span>)</span><br><span class="line">    obs, reward, terminated, truncated, _ = eval_env.step(action)</span><br><span class="line">    rewards.append(reward)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> terminated <span class="keyword">or</span> truncated:</span><br><span class="line">        obs, _ = eval_env.reset()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;平均奖励: <span class="subst">&#123;<span class="built_in">sum</span>(rewards)/<span class="built_in">len</span>(rewards):<span class="number">.2</span>f&#125;</span>&quot;</span>)</span><br><span class="line">eval_env.close()</span><br></pre></td></tr></table></figure><h3 id="62-路径跟踪任务"><a class="markdownIt-Anchor" href="#62-路径跟踪任务"></a> 6.2 路径跟踪任务</h3><p>   更复杂的任务包括让智能体沿着预定的飞行路径飞行。这需要修改奖励函数来包含位置误差项：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PathFollowingEnv</span>(<span class="title class_ inherited__">XPlaneEnv</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;路径跟踪环境&quot;&quot;&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, waypoints=<span class="literal">None</span>, **kwargs</span>):</span><br><span class="line">        <span class="built_in">super</span>().__init__(**kwargs)</span><br><span class="line">        <span class="variable language_">self</span>.waypoints = waypoints <span class="keyword">or</span> <span class="variable language_">self</span>._default_waypoints()</span><br><span class="line">        <span class="variable language_">self</span>.current_waypoint_index = <span class="number">0</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_default_waypoints</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;默认航点序列（经度，纬度，高度）&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> [</span><br><span class="line">            (<span class="number">126.790</span>, <span class="number">37.558</span>, <span class="number">3000</span>),  <span class="comment"># 起点</span></span><br><span class="line">            (<span class="number">126.850</span>, <span class="number">37.600</span>, <span class="number">3500</span>),</span><br><span class="line">            (<span class="number">126.900</span>, <span class="number">37.550</span>, <span class="number">3000</span>),</span><br><span class="line">            (<span class="number">126.850</span>, <span class="number">37.500</span>, <span class="number">2500</span>),</span><br><span class="line">        ]</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">_compute_reward</span>(<span class="params">self, state, desired_state</span>):</span><br><span class="line">        <span class="string">&quot;&quot;&quot;路径跟踪奖励函数&quot;&quot;&quot;</span></span><br><span class="line">        <span class="comment"># 当前航点</span></span><br><span class="line">        target = <span class="variable language_">self</span>.waypoints[<span class="variable language_">self</span>.current_waypoint_index]</span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 计算位置误差</span></span><br><span class="line">        lat_diff = <span class="built_in">abs</span>(state.latitude - target[<span class="number">1</span>])</span><br><span class="line">        lon_diff = <span class="built_in">abs</span>(state.longitude - target[<span class="number">0</span>])</span><br><span class="line">        alt_diff = <span class="built_in">abs</span>(state.altitude - target[<span class="number">2</span>])</span><br><span class="line">        </span><br><span class="line">        position_error = -(lat_diff + lon_diff) * <span class="number">100</span> - alt_diff * <span class="number">0.1</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 检查是否到达当前航点</span></span><br><span class="line">        <span class="keyword">if</span> lat_diff &lt; <span class="number">0.001</span> <span class="keyword">and</span> lon_diff &lt; <span class="number">0.001</span> <span class="keyword">and</span> alt_diff &lt; <span class="number">50</span>:</span><br><span class="line">            <span class="variable language_">self</span>.current_waypoint_index = (<span class="variable language_">self</span>.current_waypoint_index + <span class="number">1</span>) % <span class="built_in">len</span>(<span class="variable language_">self</span>.waypoints)</span><br><span class="line">            position_error += <span class="number">100</span>  <span class="comment"># 到达奖励</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment"># 姿态保持奖励</span></span><br><span class="line">        attitude_penalty = -<span class="built_in">abs</span>(state.pitch - desired_state.pitch) * <span class="number">0.5</span></span><br><span class="line">        attitude_penalty -= <span class="built_in">abs</span>(state.roll - desired_state.roll) * <span class="number">0.5</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> position_error + attitude_penalty</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://veridianbyte.github.io/2025/06/22/xplane_gym/</id>
    <link href="https://veridianbyte.github.io/2025/06/22/xplane_gym/"/>
    <published>2025-06-22T06:36:45.000Z</published>
    <summary>
      <![CDATA[<p>基于X-Plane12飞行模拟的强化学习Open AI Gymnasium深度强化学习环境</p>]]>
    </summary>
    <title>XPlaneGymEnvs：X-Plane 12 深度强化学习环境详解</title>
    <updated>2026-03-17T01:56:11.021Z</updated>
  </entry>
  <entry>
    <author>
      <name>白菜</name>
    </author>
    <category term="raspberry4" scheme="https://veridianbyte.github.io/categories/raspberry4/"/>
    <category term="Ubuntu 2204 server" scheme="https://veridianbyte.github.io/categories/raspberry4/Ubuntu-2204-server/"/>
    <category term="计算机网络" scheme="https://veridianbyte.github.io/categories/raspberry4/Ubuntu-2204-server/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="iptables" scheme="https://veridianbyte.github.io/tags/iptables/"/>
    <category term="NAT" scheme="https://veridianbyte.github.io/tags/NAT/"/>
    <category term="clash" scheme="https://veridianbyte.github.io/tags/clash/"/>
    <category term="DHCP" scheme="https://veridianbyte.github.io/tags/DHCP/"/>
    <category term="netplan" scheme="https://veridianbyte.github.io/tags/netplan/"/>
    <content>
      <![CDATA[<p>拿树莓派做一个能代理网络家庭网关！</p><span id="more"></span><h1 id="你能学到"><a class="markdownIt-Anchor" href="#你能学到"></a> 你能学到</h1><ol><li>从零搭建树莓派网关</li><li>树莓派系统烧录（<strong>UHS-1</strong> 及以上性能TF卡）</li><li>树莓派（ubuntu 2204 server）的网络设置</li></ol><h1 id="工具准备"><a class="markdownIt-Anchor" href="#工具准备"></a> 工具准备</h1><ol><li>树莓派</li><li>千兆+网线</li><li>无线AP</li><li>tf卡（<strong>UHS-1</strong> 及以上性能）</li></ol><h1 id="写入系统"><a class="markdownIt-Anchor" href="#写入系统"></a> 写入系统</h1><p>安装raspberry imager：<a href="https://www.raspberrypi.com/software/">https://www.raspberrypi.com/software/</a><br />使用默认设置安装即可，路径根据自己的需要更改，完成后按照以下步骤进行：<br /><img src="file-20260314140018552.png" alt="" /><br /><img src="file-20260314140031189.png" alt="" /><br /><img src="file-20260314140211599.png" alt="" /><img src="file-20260314140229621.png" alt="" /><br /><img src="file-20260314140251566.png" alt="" /><br /><img src="file-20260314140308143.png" alt="" /><br /><img src="file-20260314140320431.png" alt="" /></p><h1 id="系统配置"><a class="markdownIt-Anchor" href="#系统配置"></a> 系统配置</h1><h2 id="设置root用户"><a class="markdownIt-Anchor" href="#设置root用户"></a> 设置root用户</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> passwd root</span><br></pre></td></tr></table></figure><h2 id="禁用cloud-init"><a class="markdownIt-Anchor" href="#禁用cloud-init"></a> 禁用<strong>Cloud-init</strong></h2><p>第一次启动会卡在<code>Reached target Cloud-init</code>是正常的，这里需要等待10分钟左右，完成后就会提示登陆。</p><blockquote><p><strong>Cloud-init</strong>用于在首次启动时自动配置 Linux 系统的标准工具。在云服务器上，它用来设置主机名、注入 SSH 密钥、配置网络等。在树莓派上，官方镜像（特别是 Ubuntu Server 和部分 Raspberry Pi OS）也集成了它，用于执行首次启动的脚本（如扩展文件系统、设置默认用户密码、配置 Wi-Fi 等）。</p></blockquote><p>如果等不及了可以使用<code>Ctrl + Alt + F2</code> 或 <code>F3</code> 尝试切换到另一个虚拟终端进行登录。<br />启动完成后可以直接将<code>Cloud-init</code>禁用，后续不会有什么影响：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">touch</span> /etc/cloud/cloud-init.disabled</span><br></pre></td></tr></table></figure><p>登陆完成后，需要使用<code>ifconfig</code>查看IP用于远程SSH登陆：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install net-tools</span><br></pre></td></tr></table></figure><h2 id="禁用自动更新"><a class="markdownIt-Anchor" href="#禁用自动更新"></a> 禁用自动更新</h2><p>强烈建议系统自带的自动更新功能（<code>unattended-upgr</code>），这玩意会导致dpkg死锁，有时候卡很久重启也不一定行：<br />检查自动更新服务状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl status unattended-upgrades</span><br></pre></td></tr></table></figure><p>停止unattended-upgrades服务：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl stop unattended-upgrades</span><br></pre></td></tr></table></figure><p>禁用开机自启：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">disable</span> unattended-upgrades</span><br></pre></td></tr></table></figure><p>禁用之后需要偶尔手动更新以保持系统正常运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt upgrade -y</span><br></pre></td></tr></table></figure><h2 id="禁用systemd-networkd-wait-online"><a class="markdownIt-Anchor" href="#禁用systemd-networkd-wait-online"></a> 禁用systemd-networkd-wait-online</h2><p>开机会一直等待网络连接：<code>a start job is running for wait for network</code>，这一功能只对于<strong>开机就必须联网的服务</strong>有用，一般情况反而会拖慢开机速度，解决办法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">disable</span> systemd-networkd-wait-online.service</span><br><span class="line"><span class="built_in">sudo</span> systemctl mask systemd-networkd-wait-online.service</span><br></pre></td></tr></table></figure><h2 id="禁用needrestart"><a class="markdownIt-Anchor" href="#禁用needrestart"></a> 禁用needrestart</h2><p>进行<code>apt</code>更新后出现这个：</p><blockquote><p>Failed to check for processor microcode upgrades.</p><p>No services need to be restarted.</p><p>No containers need to be restarted.</p><p>No user sessions are running outdated binaries.</p><p>No VM guests are running outdated hypervisor (qemu) binaries on this host.</p></blockquote><p>那么可以通过禁用needrestart解决，按需选择是否禁用。</p><blockquote><p><strong>needrestart</strong> 是一个在 Linux 系统（特别是基于 Debian/Ubuntu 的发行版）中广泛使用的实用工具，主要用于<strong>检测并提示哪些系统服务（守护进程）在库文件更新后需要重启</strong>，以确保它们加载最新版本的共享库。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt remove needrestart -y</span><br></pre></td></tr></table></figure><h1 id="网络配置"><a class="markdownIt-Anchor" href="#网络配置"></a> 网络配置</h1><h2 id="基础网络配置fishrosros"><a class="markdownIt-Anchor" href="#基础网络配置fishrosros"></a> 基础网络配置（fishrosROS）</h2><ul><li>配置系统源与Python源：5 &amp;&amp; 13</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget http://fishros.com/install -O fishros &amp;&amp; . fishros</span><br></pre></td></tr></table></figure><ul><li>安装iw</li></ul><blockquote><p><strong><code>iw</code></strong> 是 Linux 系统中用于配置和管理 <strong>无线网卡（Wi-Fi）</strong> 的命令行工具。<br />它是旧版工具 <code>iwconfig</code>（属于 <code>wireless-tools</code> 包）的现代替代品，专门设计用于支持新的 <strong>nl80211</strong> 内核接口。如果你在使用较新的 Linux 发行版或较新的无线网卡，<code>iw</code> 是首选甚至唯一的配置工具。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install iw -y</span><br></pre></td></tr></table></figure><ul><li>安装NetworkManager</li></ul><blockquote><p><strong>NetworkManager</strong> 是 Linux 系统中广泛使用的<strong>网络连接管理守护进程和服务</strong>。它的主要目标是简化网络配置，使连接网络（包括 Wi-Fi、以太网、移动宽带、VPN 等）变得动态、自动且对用户友好。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install network-manager -y</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> NetworkManager</span><br><span class="line"><span class="built_in">sudo</span> systemctl start NetworkManager</span><br></pre></td></tr></table></figure><ul><li>配置NetworkManager接管网络接口</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/NetworkManager/NetworkManager.conf</span><br></pre></td></tr></table></figure><p>将<code>managed</code>置为<code>true</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl restart NetworkManager</span><br></pre></td></tr></table></figure><h2 id="配置netplan"><a class="markdownIt-Anchor" href="#配置netplan"></a> 配置netplan</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.back</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/netplan/50-cloud-init.yaml</span><br></pre></td></tr></table></figure><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">network:</span></span><br><span class="line">  <span class="attr">version:</span> <span class="number">2</span></span><br><span class="line">  <span class="attr">renderer:</span> <span class="string">NetworkManager</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">ethernets:</span></span><br><span class="line">    <span class="attr">eth0:</span></span><br><span class="line">      <span class="attr">dhcp4:</span> <span class="literal">no</span></span><br><span class="line">      <span class="attr">addresses:</span></span><br><span class="line">        <span class="bullet">-</span> <span class="number">192.168</span><span class="number">.50</span><span class="number">.1</span><span class="string">/24</span></span><br><span class="line">      <span class="attr">optional:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">wifis:</span></span><br><span class="line">    <span class="attr">wlan0:</span></span><br><span class="line">      <span class="attr">dhcp4:</span> <span class="literal">true</span></span><br><span class="line">      <span class="attr">access-points:</span></span><br><span class="line">        <span class="string">&quot;用户WiFi&quot;</span><span class="string">:</span></span><br><span class="line">          <span class="attr">password:</span> <span class="string">&quot;用户WiFi密码&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> netplan generate</span><br><span class="line"><span class="built_in">sudo</span> netplan try</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> netplan apply</span><br></pre></td></tr></table></figure><h2 id="设置网关与透明代理"><a class="markdownIt-Anchor" href="#设置网关与透明代理"></a> 设置网关与透明代理</h2><blockquote><p>说明：无论clashon或者clashoff只要mihomo.service正常运行，树莓派WiFi都是有代理的，clashon或者clashoff只是影响本机代理。</p></blockquote><blockquote><p>说明：这一部分是对通过网线连接无线AP方式进行的配置，USB网卡AP请见最后一章的说明</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br></pre></td></tr></table></figure><h3 id="设置clash"><a class="markdownIt-Anchor" href="#设置clash"></a> 设置clash</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> network &amp;&amp; <span class="built_in">cd</span> network </span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> --branch master --depth 1 https://gh-proxy.org/https://github.com/nelvko/clash-for-linux-install.git \</span><br><span class="line">  &amp;&amp; <span class="built_in">cd</span> clash-for-linux-install \</span><br><span class="line">  &amp;&amp; bash install.sh</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="设置路由"><a class="markdownIt-Anchor" href="#设置路由"></a> 设置路由</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip route</span><br></pre></td></tr></table></figure><p>如果有其他路由则删去，只保留wlan0，若有其他路由，以eth0为例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ip route del default via 192.168.137.1 dev eth0</span><br></pre></td></tr></table></figure><h3 id="开启ipv4转发"><a class="markdownIt-Anchor" href="#开启ipv4转发"></a> 开启ipv4转发</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/sysctl.conf</span><br></pre></td></tr></table></figure><p>在文末添加：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">net.ipv4.ip_forward=1</span><br><span class="line">net.ipv6.conf.all.disable_ipv6=1</span><br><span class="line">net.ipv6.conf.default.disable_ipv6=1</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> sysctl -p</span><br></pre></td></tr></table></figure><p>验证，返回1则成功开启</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cat</span> /proc/sys/net/ipv4/ip_forward</span><br><span class="line"><span class="built_in">cat</span> /proc/sys/net/ipv6/conf/all/disable_ipv6</span><br></pre></td></tr></table></figure><h3 id="关闭systemd-resolved"><a class="markdownIt-Anchor" href="#关闭systemd-resolved"></a> 关闭systemd-resolved</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">disable</span> systemd-resolved</span><br><span class="line"><span class="built_in">sudo</span> systemctl stop systemd-resolved</span><br></pre></td></tr></table></figure><p>确认已停止，应显示 inactive (dead)， disabled，exited</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl status systemd-resolved</span><br></pre></td></tr></table></figure><h3 id="修改hosts"><a class="markdownIt-Anchor" href="#修改hosts"></a> 修改hosts</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> /etc/hosts /etc/hosts.bak</span><br></pre></td></tr></table></figure><p>终端输入<code>hostname</code>查询本机hostname</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/hosts</span><br></pre></td></tr></table></figure><p>写入：（注意看你的hostname是不是ubuntu）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1 localhost</span><br><span class="line">127.0.1.1 ubuntu</span><br><span class="line"><span class="comment"># The following lines are desirable for IPv6 capable hosts</span></span><br><span class="line">::1 ip6-localhost ip6-loopback</span><br><span class="line">fe00::0 ip6-localnet</span><br><span class="line">ff00::0 ip6-mcastprefix</span><br><span class="line">ff02::1 ip6-allnodes</span><br><span class="line">ff02::2 ip6-allrouters</span><br><span class="line">ff02::3 ip6-allhosts</span><br></pre></td></tr></table></figure><h3 id="创建静态-resolvconf"><a class="markdownIt-Anchor" href="#创建静态-resolvconf"></a> 创建静态 resolv.conf</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> -d /etc/resolv.conf /etc/resolv.conf.bak</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> /etc/resolv.conf &amp;&amp; <span class="built_in">sudo</span> vim /etc/resolv.conf</span><br></pre></td></tr></table></figure><p>写入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nameserver 8.8.8.8</span><br><span class="line">nameserver 114.114.114.114</span><br></pre></td></tr></table></figure><blockquote><p>-i是取消保护，+i后即便是root也无法修改</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> chattr +i /etc/resolv.conf</span><br></pre></td></tr></table></figure><h3 id="安装-dhcp-服务器dnsmasq"><a class="markdownIt-Anchor" href="#安装-dhcp-服务器dnsmasq"></a> 安装 DHCP 服务器（dnsmasq）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt install dnsmasq -y</span><br></pre></td></tr></table></figure><ul><li>备份原配置</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">mv</span> /etc/dnsmasq.conf /etc/dnsmasq.conf.bak</span><br></pre></td></tr></table></figure><ul><li>新建最小配置</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/dnsmasq.conf</span><br></pre></td></tr></table></figure><p>写入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Listen on this network interface</span></span><br><span class="line">interface=eth0</span><br><span class="line">bind-dynamic</span><br><span class="line"></span><br><span class="line"><span class="comment"># Core modification: Disable dnsmasq&#x27;s DNS service functionality (release port 53)</span></span><br><span class="line">port=0</span><br><span class="line"></span><br><span class="line"><span class="comment"># IPv4 DHCP settings</span></span><br><span class="line">dhcp-range=192.168.50.100,192.168.50.200,12h</span><br><span class="line"><span class="comment"># Inform clients: The gateway is the Raspberry Pi (IP of eth0)</span></span><br><span class="line">dhcp-option=3,192.168.50.1</span><br><span class="line"><span class="comment"># Inform clients: The DNS server is the Raspberry Pi (Mihomo will listen on port 53)</span></span><br><span class="line">dhcp-option=6,192.168.50.1</span><br><span class="line"></span><br><span class="line"><span class="comment"># Keep logging enabled for troubleshooting</span></span><br><span class="line">log-dhcp</span><br></pre></td></tr></table></figure><ul><li>启动服务并设置开机自启</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl start dnsmasq</span><br><span class="line"><span class="built_in">sudo</span> systemctl status dnsmasq</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> dnsmasq</span><br><span class="line"><span class="built_in">sudo</span> systemctl is-enabled dnsmasq</span><br></pre></td></tr></table></figure><h3 id="配置-nat"><a class="markdownIt-Anchor" href="#配置-nat"></a> 配置 NAT</h3><p>出口接口是 wlan0，经典旁路由NAT规则：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -F</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -F</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -F</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -A FORWARD -i eth0 -o wlan0 -j ACCEPT</span><br><span class="line"><span class="built_in">sudo</span> iptables -A FORWARD -i wlan0 -o eth0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT</span><br></pre></td></tr></table></figure><p>配置tcp</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A FORWARD \</span><br><span class="line">-p tcp --tcp-flags SYN,RST SYN \</span><br><span class="line">-j TCPMSS --clamp-mss-to-pmtu</span><br></pre></td></tr></table></figure><h3 id="配置透明代理"><a class="markdownIt-Anchor" href="#配置透明代理"></a> 配置透明代理</h3><blockquote><p>注意：配置完成后不得再打开tun模式</p></blockquote><ul><li>加载内核模块，用于启用 透明代理：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> modprobe xt_TPROXY</span><br><span class="line"><span class="built_in">sudo</span> modprobe nf_tproxy_ipv4</span><br><span class="line"><span class="built_in">sudo</span> modprobe nf_nat</span><br><span class="line"><span class="built_in">sudo</span> modprobe xt_socket</span><br></pre></td></tr></table></figure><ul><li>配置clash<br />将clash进程写进systemctl</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/systemd/system/mihomo.service</span><br></pre></td></tr></table></figure><p>检查自己的路径是否正确</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Mihomo Transparent Proxy</span><br><span class="line">After=network-online.target</span><br><span class="line">Wants=network-online.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">Type=simple</span><br><span class="line">User=root</span><br><span class="line">ExecStart=/your/path/to/clashctl/bin/mihomo -d /your/path/to/clashctl/resources -f /your/path/to/clashctl/resources/runtime.yaml</span><br><span class="line">Restart=always</span><br><span class="line">RestartSec=5</span><br><span class="line">LimitNOFILE=524288</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure><p>重启后检查策略路由是否正常运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip rule show | grep 100</span><br></pre></td></tr></table></figure><ul><li>启动服务</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> mihomo</span><br><span class="line"><span class="built_in">sudo</span> systemctl start mihomo</span><br><span class="line"><span class="built_in">sudo</span> systemctl status mihomo</span><br></pre></td></tr></table></figure><ul><li>设置端口代理：</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /your/path/to/clashctl/resources/mixin.yaml</span><br></pre></td></tr></table></figure><p>在mixin.yaml将这些替代原有的参数</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">mixed-port:</span> <span class="number">7890</span></span><br><span class="line"><span class="attr">socks-port:</span> <span class="number">7891</span></span><br><span class="line"><span class="attr">redir-port:</span> <span class="number">7892</span></span><br><span class="line"><span class="attr">tproxy-port:</span> <span class="number">7893</span></span><br><span class="line"><span class="attr">bind-address:</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span></span><br><span class="line"><span class="attr">mode:</span> <span class="string">rule</span></span><br><span class="line"><span class="attr">log-level:</span> <span class="string">info</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="attr">allow-lan:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="attr">secret:</span> <span class="string">你想设置什么都行，用于登陆clash后台</span></span><br></pre></td></tr></table></figure><p>在mixin.yaml替换DNS</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">dns:</span></span><br><span class="line">  <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">listen:</span> <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span><span class="string">:53</span></span><br><span class="line">  <span class="attr">ipv6:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">enhanced-mode:</span> <span class="string">fake-ip</span></span><br><span class="line">  <span class="attr">fake-ip-range:</span> <span class="number">198.18</span><span class="number">.0</span><span class="number">.1</span><span class="string">/16</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">fake-ip-filter:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.lan&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.local&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.arpa&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;msftconnecttest.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.msftconnecttest.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;www.msftconnecttest.com&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;msftncsi.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.msftncsi.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;dns.msftncsi.com&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;connectivitycheck.gstatic.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.gstatic.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;captive.apple.com&quot;</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;*.apple.com&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">nameserver:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">223.5</span><span class="number">.5</span><span class="number">.5</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">119.29</span><span class="number">.29</span><span class="number">.29</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">fallback:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">1.1</span><span class="number">.1</span><span class="number">.1</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">8.8</span><span class="number">.8</span><span class="number">.8</span></span><br><span class="line">    <span class="bullet">-</span> <span class="number">9.9</span><span class="number">.9</span><span class="number">.9</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">fallback-filter:</span></span><br><span class="line">    <span class="attr">geoip:</span> <span class="literal">true</span></span><br><span class="line">    <span class="attr">geoip-code:</span> <span class="string">CN</span></span><br></pre></td></tr></table></figure><p>由于<code>mihomo.service</code>设置的是root权限，所以需要进入root模式才能使用clash的各项功能。</p><ul><li>配置root的<code>.bashrc</code></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">su root &amp;&amp; vim ~/.bashrc</span><br></pre></td></tr></table></figure><p>文末写入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># clashctl START</span></span><br><span class="line"><span class="comment"># 加载 clashctl 命令</span></span><br><span class="line">. /your/path/to/clashctl/scripts/cmd/clashctl.sh</span><br><span class="line"><span class="comment"># 自动开启代理环境</span></span><br><span class="line">watch_proxy</span><br><span class="line"><span class="comment"># clashctl END</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><ul><li>重新加载代理并重启<code>mihomo.service</code></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">clashctl sub update</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart mihomo</span><br></pre></td></tr></table></figure><p>此时，7891、7892、7893、53应当都已被已监听。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ss -lntp | grep mihomo</span><br></pre></td></tr></table></figure><p>出现：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">LISTEN 0      4096               *:53              *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">8</span>))</span><br><span class="line">LISTEN 0      4096               *:9090            *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">6</span>))</span><br><span class="line">LISTEN 0      4096               *:7890            *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">3</span>))</span><br><span class="line">LISTEN 0      4096               *:7891            *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">9</span>))</span><br><span class="line">LISTEN 0      4096               *:7892            *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">11</span>))</span><br><span class="line">LISTEN 0      4096               *:7893            *:*    <span class="built_in">users</span>:((&quot;mihomo&quot;,pid=<span class="number">68272</span>,fd=<span class="number">13</span>))</span><br></pre></td></tr></table></figure><ul><li>创建 TCP TPROXY 规则</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">su your_username</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -t nat -N CLASH</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 0.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 10.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 127.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 169.254.0.0/16 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 172.16.0.0/12 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 192.168.0.0/16 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 224.0.0.0/4 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -d 240.0.0.0/4 -j RETURN</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A CLASH -p tcp -j REDIRECT --to-ports 7892</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A PREROUTING -i eth0 -p tcp -j CLASH</span><br></pre></td></tr></table></figure><ul><li>创建 UDP TPROXY 规则</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -t mangle -N CLASH</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 0.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 10.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 127.0.0.0/8 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 169.254.0.0/16 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 172.16.0.0/12 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 192.168.0.0/16 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 224.0.0.0/4 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -d 240.0.0.0/4 -j RETURN</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A CLASH -p udp -j TPROXY --on-port 7893 --tproxy-mark 1</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A PREROUTING -i eth0 -m socket -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t mangle -A PREROUTING -i eth0 -p udp -j CLASH</span><br></pre></td></tr></table></figure><ul><li>劫持DNS<br />保留本地直连放行:</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> iptables -t nat -A OUTPUT -p udp --dport 53 -j RETURN</span><br><span class="line"><span class="built_in">sudo</span> iptables -t nat -A OUTPUT -p tcp --dport 53 -j RETURN</span><br></pre></td></tr></table></figure><h3 id="保存iptables-规则"><a class="markdownIt-Anchor" href="#保存iptables-规则"></a> 保存iptables 规则</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt install iptables-persistent -y</span><br><span class="line"><span class="built_in">sudo</span> netfilter-persistent save</span><br></pre></td></tr></table></figure><p>弹窗都选择yes即可</p><h1 id="组装"><a class="markdownIt-Anchor" href="#组装"></a> 组装</h1><p>将网线插入树莓派网口，将另一端插入无线AP即可。<br />如果你使用的带有无线AP功能的USB网卡或者你还有另一个<code>eth1</code>，那么你需要将网络配置中NAT规则中的<code>eth0</code>改为你的网卡ID，例如<code>wlan1</code>或者<code>wlx001122334455</code>。</p>]]>
    </content>
    <id>https://veridianbyte.github.io/2025/03/01/clashctl_nat/</id>
    <link href="https://veridianbyte.github.io/2025/03/01/clashctl_nat/"/>
    <published>2025-03-01T07:33:43.000Z</published>
    <summary>
      <![CDATA[<p>拿树莓派做一个能代理网络家庭网关！</p>]]>
    </summary>
    <title>Ubuntu树莓派实现家庭网络代理中继</title>
    <updated>2026-03-17T10:53:08.464Z</updated>
  </entry>
  <entry>
    <author>
      <name>白菜</name>
    </author>
    <category term="Windows11" scheme="https://veridianbyte.github.io/categories/Windows11/"/>
    <category term="个人博客" scheme="https://veridianbyte.github.io/categories/Windows11/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/"/>
    <category term="hexo" scheme="https://veridianbyte.github.io/tags/hexo/"/>
    <category term="obsidian" scheme="https://veridianbyte.github.io/tags/obsidian/"/>
    <content>
      <![CDATA[<p>win11下配置obsidian联动hexo更新博客</p><span id="more"></span><h1 id="安装hexo与nodejs"><a class="markdownIt-Anchor" href="#安装hexo与nodejs"></a> 安装Hexo与Node.js</h1><h2 id="nodejs安装"><a class="markdownIt-Anchor" href="#nodejs安装"></a> Node.js安装</h2><p>在 <a href="https://nodejs.org/en/download/">https://nodejs.org/en/download/</a> 选择Windows Installer（.msi）根据个人需要调整安装选项。<br />安装完成后在终端中输入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">node -v</span><br><span class="line">npm -v</span><br></pre></td></tr></table></figure><p>应当正常显示node与npm版本。</p><h2 id="安装hexo"><a class="markdownIt-Anchor" href="#安装hexo"></a> 安装Hexo</h2><p>在终端中输入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g hexo-cli</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo init my-blog/</span><br></pre></td></tr></table></figure><p>在<code>my-blog</code>目录下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install</span><br></pre></td></tr></table></figure><p>这里也可以直接安装其他主题，根据你想安装的主题而定：<a href="https://hexo.io/themes/">Hexo-Themes</a></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-theme-reimu --save</span><br></pre></td></tr></table></figure><p>安装完成后即可正常使用Hexo。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo s</span><br></pre></td></tr></table></figure><h2 id="git配置"><a class="markdownIt-Anchor" href="#git配置"></a> Git配置</h2><p>选择适合版本的git <a href="https://git-scm.com/install/windows">https://git-scm.com/install/windows</a><br />安装完成后，在博客根目录的<code>_config.yml</code>中配置你的账户：</p><figure class="highlight yaml"><figcaption><span>_config.yml</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Deployment</span></span><br><span class="line"><span class="comment">## Docs: https://hexo.io/docs/one-command-deployment</span></span><br><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">https://Github</span> <span class="string">ID@github.com/Github名/仓库名.git</span></span><br><span class="line">  <span class="attr">branch:</span> <span class="string">填写仓库的主分支</span></span><br></pre></td></tr></table></figure><h1 id="obsidian配置"><a class="markdownIt-Anchor" href="#obsidian配置"></a> obsidian配置</h1><h2 id="安装obsidian"><a class="markdownIt-Anchor" href="#安装obsidian"></a> 安装obsidian</h2><p>在 <a href="https://obsidian.md/download">https://obsidian.md/download</a> 下载并安装，完成后打开obsidian选择my-blog作为本地仓库打开：<br /><img src="file-20260224000544649.png" alt="" /></p><h2 id="安装插件"><a class="markdownIt-Anchor" href="#安装插件"></a> 安装插件</h2><p>在设置的第三方插件处，关闭安全模式<br /><img src="file-20260224000926054.png" alt="" /><br />在社区插件市场安装 <code>File Tree Alternative</code>、<code>floating toc</code>、<code>Git</code>、<code>Shell commands</code>、<code>Custom Attachment Location</code>。</p><h3 id="配置shell-commands"><a class="markdownIt-Anchor" href="#配置shell-commands"></a> 配置Shell commands</h3><p>选择<code>New shell command</code>，添加<code>start deploy.sh</code>和<code>start server.sh</code><br /><img src="file-20260224000959106.png" alt="" /><br />在my-blog根目录中添加<code>deploy.sh</code>与<code>server.sh</code></p><figure class="highlight bash"><figcaption><span>deploy.sh</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成静态文件</span></span><br><span class="line">hexo clean &amp;&amp; hexo generate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 部署到 GitHub Pages</span></span><br><span class="line">hexo deploy</span><br></pre></td></tr></table></figure><figure class="highlight bash"><figcaption><span>server.sh</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 生成静态文件</span></span><br><span class="line">hexo clean &amp;&amp; hexo generate</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打开本地服务</span></span><br><span class="line">hexo s</span><br></pre></td></tr></table></figure><p>在命令面板中搜索<code>start deploy.sh</code>或<code>start server.sh</code>即可同步GitHub或本地预览</p><blockquote><p>这里记得把sh文件的默认打开方式改为Git for Windows</p></blockquote><p><img src="file-20260224114840209.png" alt="" /></p><h3 id="配置custom-attachment-location"><a class="markdownIt-Anchor" href="#配置custom-attachment-location"></a> 配置Custom Attachment Location</h3><p>新附件位置填入<code>./${noteFileName}</code>，如下图所示：<br /><img src="file-20260224113023247.png" alt="" /></p><h2 id="配置hexo的_configyml"><a class="markdownIt-Anchor" href="#配置hexo的_configyml"></a> 配置Hexo的_config.yml</h2><p>将博客根目录<code>_config.yml</code>文件的<code>post_asset_folder</code>值设置为<code>true</code></p><figure class="highlight yaml"><figcaption><span>_config.yml</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">post_asset_folder:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h2 id="obsidian设置"><a class="markdownIt-Anchor" href="#obsidian设置"></a> obsidian设置</h2><p>打开设置在文件与连接选项中按图中设置。</p><blockquote><p><code>附件文件夹路径</code>不用填，由Custom Attachment Location插件自动生成。</p></blockquote><p><img src="file-20260224112143944.png" alt="" /><br /><img src="file-20260224131410824.png" alt="" /></p><ul><li>此时obsidian中插入图片的格式应当为：<code>![](file-xxxxxxxxx.png)</code><br />假设博客位置为：<code>source\_posts\text.md</code>则图片应当存储在<code>source\_posts\text\file-xxxxxxxxx.png</code></li></ul>]]>
    </content>
    <id>https://veridianbyte.github.io/2025/02/23/hexo_obsidian/</id>
    <link href="https://veridianbyte.github.io/2025/02/23/hexo_obsidian/"/>
    <published>2025-02-23T07:20:47.000Z</published>
    <summary>win配置obsidian+hexo</summary>
    <title>hexo-reimu主题联动obsidian更新博客</title>
    <updated>2026-03-17T02:02:20.985Z</updated>
  </entry>
  <entry>
    <author>
      <name>白菜</name>
    </author>
    <category term="raspberry4" scheme="https://veridianbyte.github.io/categories/raspberry4/"/>
    <category term="Ubuntu 2204 server" scheme="https://veridianbyte.github.io/categories/raspberry4/Ubuntu-2204-server/"/>
    <category term="Pi-Imager" scheme="https://veridianbyte.github.io/tags/Pi-Imager/"/>
    <content>
      <![CDATA[<p>tf卡又慢寿命又低，性价比堪忧，使用nvme硬盘盒将老旧固态利用起来给树莓派使正正好。</p><span id="more"></span><h1 id="你能学到"><a class="markdownIt-Anchor" href="#你能学到"></a> 你能学到</h1><ol><li>通过USB安装树莓派系统</li><li>初始化系统设置并优化</li><li>解决硬盘兼容性问题</li></ol><h1 id="工具准备"><a class="markdownIt-Anchor" href="#工具准备"></a> 工具准备</h1><ol><li>树莓派</li><li>空tf卡</li><li>一根micro hdmi线</li><li>一个带独立供电的移动硬盘盒</li><li>树莓派电源</li><li>支持HDMI的显示器</li><li>c2a口usb数据线</li></ol><blockquote><p>如果嫌独立供电硬盘盒麻烦可以用蓝口U盘，后续步骤一样！</p></blockquote><p>安装树莓派官方镜像烧录工具：<a href="https://www.raspberrypi.com/software/">https://www.raspberrypi.com/software/</a></p><h1 id="格式化usb存储设备"><a class="markdownIt-Anchor" href="#格式化usb存储设备"></a> 格式化USB存储设备</h1><p><img src="file-20260313203911450.png" alt="" /><br />选择自己的设备然后下下下下一步，确认写入即可：<br /><img src="file-20260313204002359.png" alt="" /><br />这里重复一遍将空闲tf卡也这样格式化一遍。</p><h1 id="更新与配置-eeprom"><a class="markdownIt-Anchor" href="#更新与配置-eeprom"></a> 更新与配置 EEPROM</h1><p>安装好tf卡，正确插入电脑后：<br /><img src="file-20260313204305970.png" alt="" /><br /><img src="file-20260313204313544.png" alt="" /><br /><img src="file-20260313204320567.png" alt="" /><br /><img src="file-20260313204328964.png" alt="" /><br /><img src="file-20260313204335789.png" alt="" /><br />后续如果想要换回默认sd卡启动那就同样的步骤选择SD Card Boot烧录即可！<br />将该sd卡插入树莓派并通电。</p><blockquote><p>树莓派的绿灯快速、规律地闪烁（每秒约 2-4 次），或者hdmi屏幕变绿。完成配置！</p></blockquote><p>现在请断开树莓派电源，拔出tf卡，这张tf卡可以放一边存着了。</p><h1 id="写入系统"><a class="markdownIt-Anchor" href="#写入系统"></a> 写入系统</h1><p><img src="file-20260313204305970.png" alt="" /><br /><img src="file-20260313204944249.png" alt="" /><br /><img src="file-20260313204949821.png" alt="" /><br />这里hostname最好还是写ubuntu就好了，看个人吧。<br /><img src="file-20260313204959441.png" alt="" /><br /><img src="file-20260313205026957.png" alt="" /><br /><img src="file-20260313205038393.png" alt="" /><br /><img src="file-20260313205045896.png" alt="" /><br /><img src="file-20260313205051332.png" alt="" /><br />完成后拔出，插入树莓派的USB3.0蓝口即可，带独立供电的记得插上硬盘盒供电。<br />插上后观察显示器，显示树莓派LOGO，正常启动！</p><h1 id="建议优化"><a class="markdownIt-Anchor" href="#建议优化"></a> 建议优化</h1><h2 id="禁用cloud-init"><a class="markdownIt-Anchor" href="#禁用cloud-init"></a> 禁用<strong>Cloud-init</strong></h2><p>第一次启动会卡在<code>Reached target Cloud-init</code>是正常的，这里需要等待10分钟左右，完成后就会提示登陆。</p><blockquote><p><strong>Cloud-init</strong>用于在首次启动时自动配置 Linux 系统的标准工具。在云服务器上，它用来设置主机名、注入 SSH 密钥、配置网络等。在树莓派上，官方镜像（特别是 Ubuntu Server 和部分 Raspberry Pi OS）也集成了它，用于执行首次启动的脚本（如扩展文件系统、设置默认用户密码、配置 Wi-Fi 等）。</p></blockquote><p>如果等不及了可以使用<code>Ctrl + Alt + F2</code> 或 <code>F3</code> 尝试切换到另一个虚拟终端进行登录。<br />启动完成后可以直接将<code>Cloud-init</code>禁用，后续不会有什么影响：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">touch</span> /etc/cloud/cloud-init.disabled</span><br></pre></td></tr></table></figure><p>登陆完成后，需要使用<code>ifconfig</code>查看IP用于远程SSH登陆：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install net-tools</span><br></pre></td></tr></table></figure><h2 id="禁用自动更新"><a class="markdownIt-Anchor" href="#禁用自动更新"></a> 禁用自动更新</h2><p>强烈建议系统自带的自动更新功能（<code>unattended-upgr</code>），这玩意会导致dpkg死锁，有时候卡很久重启也不一定行：<br />检查自动更新服务状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl status unattended-upgrades</span><br></pre></td></tr></table></figure><p>停止unattended-upgrades服务：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl stop unattended-upgrades</span><br></pre></td></tr></table></figure><p>禁用开机自启：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">disable</span> unattended-upgrades</span><br></pre></td></tr></table></figure><p>禁用之后需要偶尔手动更新以保持系统正常运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt upgrade -y</span><br></pre></td></tr></table></figure><h2 id="禁用systemd-networkd-wait-online"><a class="markdownIt-Anchor" href="#禁用systemd-networkd-wait-online"></a> 禁用systemd-networkd-wait-online</h2><p>开机会一直等待网络连接：<code>a start job is running for wait for network</code>，这一功能在我的目标功能上并不实用，反而会拖慢开机速度，解决办法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">disable</span> systemd-networkd-wait-online.service</span><br><span class="line"><span class="built_in">sudo</span> systemctl mask systemd-networkd-wait-online.service</span><br></pre></td></tr></table></figure><h2 id="禁用needrestart"><a class="markdownIt-Anchor" href="#禁用needrestart"></a> 禁用needrestart</h2><p>如果经常出现这个：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Failed to check <span class="keyword">for</span> processor microcode upgrades.</span><br><span class="line"></span><br><span class="line">No services need to be restarted.</span><br><span class="line"></span><br><span class="line">No containers need to be restarted.</span><br><span class="line"></span><br><span class="line">No user sessions are running outdated binaries.</span><br><span class="line"></span><br><span class="line">No VM guests are running outdated hypervisor (qemu) binaries on this host.</span><br></pre></td></tr></table></figure><p>那么可以通过禁用needrestart解决，按需选择是否禁用。</p><blockquote><p><strong>needrestart</strong> 是一个在 Linux 系统（特别是基于 Debian/Ubuntu 的发行版）中广泛使用的实用工具，主要用于<strong>检测并提示哪些系统服务（守护进程）在库文件更新后需要重启</strong>，以确保它们加载最新版本的共享库。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt remove needrestart -y</span><br></pre></td></tr></table></figure><h2 id="进行基础网络配置"><a class="markdownIt-Anchor" href="#进行基础网络配置"></a> 进行基础网络配置</h2><p>万能鱼香ROS脚本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget http://fishros.com/install -O fishros &amp;&amp; . fishros</span><br></pre></td></tr></table></figure><p>配置系统源和python源即可，剩下的按需求使用。</p><h1 id="usb存储设备兼容性问题"><a class="markdownIt-Anchor" href="#usb存储设备兼容性问题"></a> USB存储设备兼容性问题</h1><blockquote><p>出现<code>Input/output error</code></p></blockquote><p>这就是掉盘了，树莓派无法写入硬盘数据，主要原因有：</p><h2 id="供电不足"><a class="markdownIt-Anchor" href="#供电不足"></a> 供电不足</h2><p>如果是自带供电的硬盘盒，那么需要检查电源线是否原装，树莓派电源线是否是官方指定的<code>5.1V3A</code>规格电源。U盘设备一般不会出现供电不足的问题，考虑是否是U盘损坏。<br />通过优化树莓派供电可以适当缓解：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /boot/firmware/config.txt</span><br></pre></td></tr></table></figure><p>文末添加：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">max_usb_current=1</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><h2 id="兼容性问题"><a class="markdownIt-Anchor" href="#兼容性问题"></a> 兼容性问题</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lsusb</span><br></pre></td></tr></table></figure><p>查看输出中的ID，以<code>0bda:9210</code>为例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /boot/firmware/cmdline.txt</span><br></pre></td></tr></table></figure><p>在文件顶格添加：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">usb-storage.quirks=0bda:9210:u  </span><br></pre></td></tr></table></figure><blockquote><p>这里一定不要换行，记得用空格隔开。</p></blockquote><p>文末添加：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">usbcore.autosuspend=-1</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://veridianbyte.github.io/2025/02/20/raspi_usb_boot/</id>
    <link href="https://veridianbyte.github.io/2025/02/20/raspi_usb_boot/"/>
    <published>2025-02-20T01:06:21.000Z</published>
    <summary>
      <![CDATA[<p>tf卡又慢寿命又低，性价比堪忧，使用nvme硬盘盒将老旧固态利用起来给树莓派使正正好。</p>]]>
    </summary>
    <title>通过USB硬盘盒启动树莓派</title>
    <updated>2026-03-17T02:25:48.375Z</updated>
  </entry>
  <entry>
    <author>
      <name>白菜</name>
    </author>
    <category term="日常" scheme="https://veridianbyte.github.io/categories/%E6%97%A5%E5%B8%B8/"/>
    <content>
      <![CDATA[<p>我们所经历的日常…</p><span id="more"></span><p>日记从上到下，从新到旧</p><h1 id="二零二六年"><a class="markdownIt-Anchor" href="#二零二六年"></a> 二零二六年</h1><h2 id="一月"><a class="markdownIt-Anchor" href="#一月"></a> 一月</h2><h3 id="二日"><a class="markdownIt-Anchor" href="#二日"></a> 二日</h3><p>  换了一个新的评论区系统waline，这个更好看而且功能也更完善，缺点就是正好碰到 LeanCloud 即将停止服务。不想迁移评论区了，目前我用<a href="https://waline.js.org/guide/get-started/">waline官方教程</a>遇到个问题，必须得开代理才能正常使用评论，进一步了解后发现可能得自建数据库服务器了，等有空再弄吧，开代理用也不是不行 。 。 。 。 。 。 。</p><h1 id="二零二五年"><a class="markdownIt-Anchor" href="#二零二五年"></a> 二零二五年</h1><h2 id="八月"><a class="markdownIt-Anchor" href="#八月"></a> 八月</h2><h3 id="二十日"><a class="markdownIt-Anchor" href="#二十日"></a> 二十日</h3><p>   成绩出来啦，我全国二等奖（全国第十三名）、另外一组全国一等奖（全国第三）。无论那一组，这都是我们一起调试的小车，足以证明我们的实力。</p><h3 id="四日"><a class="markdownIt-Anchor" href="#四日"></a> 四日</h3><p>   正式比赛了，我们一共两个进国赛的队伍共用一套设备，由于场地调试时间有限，小车的导航点还有一个不是很确定，只能等比赛牺牲一个队伍来测试了。</p><p>   不幸的是刚好就是我这一组，我已经尽力了，最后小车碰了一下柱子还好之前没有完全关闭小车的避障，最终完成了比赛给下一组获取了最后一个导航点的坐标。</p><p>   另外一组果然没出问题，完美完成比赛，只花了四十秒，大概率是全国一等奖了。还剩一天赶紧去苏州玩玩！</p><h3 id="三日"><a class="markdownIt-Anchor" href="#三日"></a> 三日</h3><p>   苏州真的很漂亮，制造业很发达，气候也挺不错的。决赛的场地是最值得吐槽的，障碍物密集，通道狭小，光线还特别暗！每个队的调试时间还很少！！！</p><h2 id="七月"><a class="markdownIt-Anchor" href="#七月"></a> 七月</h2><h3 id="二十五日"><a class="markdownIt-Anchor" href="#二十五日"></a> 二十五日</h3><p>   我的<a href="https://github.com/VeridianByte/XPlaneGymEnvs">XPlaneGymEnvs</a>也做出来了，技术力有限，可能存在不少问题，但是没时间维护了。</p><blockquote><p><a href="https://veridianbyte.github.io/2025/06/22/xplane_gym/">基于X-Plane12飞行模拟的强化学习Open AI Gymnasium深度强化学习环境</a></p></blockquote><h3 id="二十日-2"><a class="markdownIt-Anchor" href="#二十日-2"></a> 二十日</h3><p>   全国预赛很轻松的就过了，小车+无人机协同完成比赛就花了40s，成功率几乎百分百。苏州我来了！！！</p><h3 id="三日-2"><a class="markdownIt-Anchor" href="#三日-2"></a> 三日</h3><p>   又被老师拉去参加一个什么全国无人机创新技能大赛，实际上就是飞手技能比赛，练练怎么飞无人机  。  。</p><h2 id="六月"><a class="markdownIt-Anchor" href="#六月"></a> 六月</h2><h3 id="二十二日"><a class="markdownIt-Anchor" href="#二十二日"></a> 二十二日</h3><p>  无人机工程上的优化已经到我们的极限了，无人机的策略实际上有很大的问题，改改策略完成任务的时间又要快不少。我观察到无人机在转弯时过的很慢，查看ego-planner节点发现当前的bspline_opt避障和动力学约束极大的限制了无人机的速度，我们这把路径写死了为了<code>提速</code>避障实际上没什么必要了。<br />  动力学约束实际上也比较鸡肋，这无人机就在这点大地方，加速度是我在程序里面可以直接控制的，去掉后无人机完成任务只需要十几秒。接下来也改改小车的策略。</p><h3 id="十八日"><a class="markdownIt-Anchor" href="#十八日"></a> 十八日</h3><p>  延庆虽然偏没什么玩的，但是和大家在一起努力又何尝不是一种快乐。每天和无人机打交道，<code>稳定性</code>和<code>安全性</code>始终还是最重要的，我们比赛这无人机前面装了一根长针，一旦失控扎进人群后果不堪设想。</p><h3 id="七日"><a class="markdownIt-Anchor" href="#七日"></a> 七日</h3><p>  拿下北京省二。准备国赛，我定要去苏州看看！</p><h3 id="一日"><a class="markdownIt-Anchor" href="#一日"></a> 一日</h3><p>  熬了个通宵，在会场的会议室睡了两小时，新场地地面摩擦力还有光线差别都很大，尤其是光线问题，场地特别暗（疑似故意），机哥关键时候可不能掉链子啊！<br /><img src="%E6%97%A0%E4%BA%BA%E6%9C%BA120.jpg" alt="" /></p><blockquote><p>中途有惊无险，没有出错！省赛应该是稳了</p></blockquote><h2 id="五月"><a class="markdownIt-Anchor" href="#五月"></a> 五月</h2><h3 id="二十三日"><a class="markdownIt-Anchor" href="#二十三日"></a> 二十三日</h3><p>  小车雷达问题被牛逼队友解决了，加上回调就解决了。<br />  Hit节点反复排查了很久，可以定位到是<code>Hit::getVel()</code>，我仔细看了看实际上是线程<code>Hit</code> 节点读取 <code>depth_buffer_</code> 时，与 ROS 回调线程同时修改该数据结构，导致迭代器/指针失效并访问非法内存，从而触发 <code>SIGSEGV (signal 11)</code> 崩溃。给<code>depth_buffer_</code>加锁即可解决。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lock</span><span class="params">(detph_mtx)</span></span>;</span><br></pre></td></tr></table></figure><p>  <code>鬼影</code>问题目前没找到稳定复现的情况，暂时无法解决。</p><h3 id="二十日-3"><a class="markdownIt-Anchor" href="#二十日-3"></a> 二十日</h3><p>  月底初赛就要开始了，小车依旧龟速，飞机依旧龟速，甚至还会撞墙。。。<br />目前遇到了好几个问题：</p><ol><li>小车旋转时雷达SLAM建图会偏移</li><li>无人机是视觉SLAM，摄像头用的Intel D435i，这摄像头一到晚上就会出现<code>鬼影</code></li><li>无人机进入Hit节点（刺气球）时会：“revc stop hover command”然后die<br /><img src="file-20260314100814051.png" alt="" /></li></ol><h3 id="十七日"><a class="markdownIt-Anchor" href="#十七日"></a> 十七日</h3><p>  延庆是真的偏啊，本部往返得坐六小时的车 。  。  。</p><h3 id="一日-2"><a class="markdownIt-Anchor" href="#一日-2"></a> 一日</h3><p>  开被大导拉去参加无人机竞赛，中国机器人及人工智能大赛（无人机空地协同）看起来还不错，先快速学学基础知识。。还好ROS以前大学就用过。<br /><img src="file-20260314100417595.png" alt="" /></p><h2 id="四月"><a class="markdownIt-Anchor" href="#四月"></a> 四月</h2><h3 id="二十日-4"><a class="markdownIt-Anchor" href="#二十日-4"></a> 二十日</h3><p>这个工具还真没什么人做过，目前在网上找到了一个开源项目<a href="%5BXPlaneConnectX%5D(https://github.com/sisl/XPlaneConnectX)">XPlaneConnectX</a>是通过Python或Julia调用X-Plane的UDP接口来实现。太好了，用这个就能打通Gym与Xplane的通信问题了。</p><h3 id="十日"><a class="markdownIt-Anchor" href="#十日"></a> 十日</h3><p>  小导给安排了个xplane12固定翼飞机模拟器，让做个能最优化耗油的飞行姿态控制。直接上OpenAI的<strong>Gymnasium</strong>。但是没有适合xplane12软件的gymEnv，自己做一个吧。<br /><img src="file-20260314094413211.png" alt="" /></p><h3 id="三日-3"><a class="markdownIt-Anchor" href="#三日-3"></a> 三日</h3><p>  开始学习强化学习！西湖大学赵世钰的课程很值得推荐！哔哩哔哩就有官方的课程。<br />  在学过强化学习后与这个算法产生了深深的共鸣，你说，我每天是不是也在做马尔可夫最优决策？</p><blockquote><p><a href="https://zh.wikipedia.org/zh-cn/%E9%A6%AC%E5%8F%AF%E5%A4%AB%E6%B1%BA%E7%AD%96%E9%81%8E%E7%A8%8B">马尔可夫决策过程</a></p></blockquote><h2 id="三月"><a class="markdownIt-Anchor" href="#三月"></a> 三月</h2><h3 id="二十日-5"><a class="markdownIt-Anchor" href="#二十日-5"></a> 二十日</h3><p>  主要目标是提高ISTA-Net的反演效果、考虑用WGAN-网络对SR-ISTA-Net进行了改进。</p><h3 id="十日-2"><a class="markdownIt-Anchor" href="#十日-2"></a> 十日</h3><p>  跟了个小导，目前很push，急速学习SAR相关知识，</p><h3 id="一日-3"><a class="markdownIt-Anchor" href="#一日-3"></a> 一日</h3><p>  今天将树莓派配置成了一个软路由，树莓派网口接上网上买的随身WiFi路由，现在能实现网络的转发，并通过路由将实现了局域网的代理。代理统一在树莓派管理，局域网内的设备也不需要单独再配置代理了，对于某些配置代理麻烦的设备十分便利。</p><h2 id="二月"><a class="markdownIt-Anchor" href="#二月"></a> 二月</h2><h3 id="二十五日-2"><a class="markdownIt-Anchor" href="#二十五日-2"></a> 二十五日</h3><p>  今天继续更新了网页的元素，太喜欢这个背景了。</p><h3 id="二十三日-2"><a class="markdownIt-Anchor" href="#二十三日-2"></a> 二十三日</h3><p>  过年忙来忙去，一转眼又得开学了，终究还是得面对各种搁置已久的事情。今天花了一点时间参考网上的教程，利用obsidian同步hexo博客，写博客还是方便了不少。</p><blockquote><p><a href="https://veridianbyte.github.io/2025/02/23/hexo_obsidian/">hexo-reimu主题联动obsidian更新博客</a></p></blockquote><p>  今天还学到个小技巧，原来键盘alt+a也能截屏。<br />  写博客时若直接写可执行文件名居然会被解析为超链接，今天写的<code>deploy.s</code>在网页端一直是超链接，一时还搞不清楚为什么，折腾半天才知道是这样原因！！！</p><h3 id="十七日-2"><a class="markdownIt-Anchor" href="#十七日-2"></a> 十七日</h3><p>  花了几天的时间终于差不多将网站整理好了，加入了许多自己喜欢的元素。至于动机，总之是想要记录记录自己走过的路。说起来我是一个不记事的人，有啥事情转眼就忘了，昨天在做什么今天差不多就快忘记了，一周前的事情基本上就已经记不住了。有时候我常常觉得自己是个没有过去的人。</p><p>  最近在学习树莓派的相关知识，为了点屏学习学习了framebuffer相关知识，还看了看LVGL，光是搞清楚这些东西就花了不少时间。感觉放假还在昨天，悲~</p>]]>
    </content>
    <id>https://veridianbyte.github.io/2025/02/17/diary/</id>
    <link href="https://veridianbyte.github.io/2025/02/17/diary/"/>
    <published>2025-02-17T13:32:44.000Z</published>
    <summary>我们所经历的日常..</summary>
    <title>《日常》</title>
    <updated>2026-03-17T02:26:50.594Z</updated>
  </entry>
</feed>
