<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Dreamer Island</title>
  
  
  <link href="https://dreamer198.top/atom.xml" rel="self"/>
  
  <link href="https://dreamer198.top/"/>
  <updated>2026-08-20T04:01:15.792Z</updated>
  <id>https://dreamer198.top/</id>
  
  <author>
    <name>Dreamer</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Jetson Orin NX 双网口同时启用教程</title>
    <link href="https://dreamer198.top/2026/08/11/Jetson-Orin-NX%E5%8F%8C%E7%BD%91%E5%8F%A3%E5%90%8C%E6%97%B6%E5%90%AF%E7%94%A8/"/>
    <id>https://dreamer198.top/2026/08/11/Jetson-Orin-NX%E5%8F%8C%E7%BD%91%E5%8F%A3%E5%90%8C%E6%97%B6%E5%90%AF%E7%94%A8/</id>
    <published>2026-08-11T08:16:05.000Z</published>
    <updated>2026-08-20T04:01:15.792Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>这次在 Jetson Orin NX 上同时连接普通设备和 Livox Mid-360S 时，连续遇到了两个问题：</p><ol><li>主板有两个物理网口，但系统只能看到一个网口。</li><li>第二个网口修好后，两个网口仍然只能连接一个，连接第二个时第一个会断开。</li></ol><p>最后确认并不是 Jetson 的两个网口硬件互斥，而是第二个 Realtek 网卡缺少驱动，同时两个网口错误地共用了同一个 NetworkManager 配置。由于两个网口还在同一个 <code>192.168.1.0/24</code> 网段，又产生了路由冲突。</p><p>本文先给出可以直接使用的一键脚本，再解释问题原因和手动排查方法。</p><blockquote><p>适用边界：下面的脚本是针对本文记录的 Jetson Orin NX、Realtek RTL8125/RTL8168 网卡和同网段地址规划验证的。其他载板、内核版本、网卡芯片或 IP 规划需要先修改配置并核对驱动，不应直接照搬。</p></blockquote><span id="more"></span><h2 id="一、当前设备配置">一、当前设备配置</h2><p>这台 Jetson 的网络规划如下：</p><table><thead><tr><th>用途</th><th>网口</th><th>Jetson IP</th><th>对端设备 IP</th></tr></thead><tbody><tr><td>普通设备网</td><td><code>enP7p1s0</code></td><td><code>192.168.1.123/24</code></td><td>根据实际设备确定</td></tr><tr><td>Mid-360S 专用网</td><td><code>enP8p1s0</code></td><td><code>192.168.1.102/24</code></td><td><code>192.168.1.199</code></td></tr><tr><td>SSH 和默认网络</td><td><code>wlP1p1s0</code></td><td>DHCP</td><td>路由器</td></tr></tbody></table><p>实际网卡控制器：</p><figure class="highlight text"><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">enP7p1s0 -&gt; Realtek RTL8125      -&gt; r8125</span><br><span class="line">enP8p1s0 -&gt; Realtek RTL8111/8168 -&gt; r8168</span><br></pre></td></tr></table></figure><blockquote><p>执行脚本会重新连接两个有线网口。如果当前 SSH 正通过其中一个有线口连接，脚本会停止。建议通过 Wi-Fi、显示器或串口执行。</p></blockquote><h2 id="二、一键解决脚本">二、一键解决脚本</h2><p>在 Jetson 上创建脚本：</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">nano ~/enable_jetson_dual_eth.sh</span><br></pre></td></tr></table></figure><p>复制下面的全部内容。如果自己的网口名或 IP 不同，只修改最上方的“需要修改的配置”部分。</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><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><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="built_in">set</span> -Eeuo pipefail</span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================================================</span></span><br><span class="line"><span class="comment"># 需要修改的配置</span></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">GENERAL_IF=<span class="string">&quot;enP7p1s0&quot;</span></span><br><span class="line">GENERAL_IP=<span class="string">&quot;192.168.1.123/24&quot;</span></span><br><span class="line">GENERAL_PROFILE=<span class="string">&quot;device-enP7&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Mid-360S 专用网口</span></span><br><span class="line">LIDAR_IF=<span class="string">&quot;enP8p1s0&quot;</span></span><br><span class="line">LIDAR_HOST_IP=<span class="string">&quot;192.168.1.102/24&quot;</span></span><br><span class="line">LIDAR_PROFILE=<span class="string">&quot;102&quot;</span></span><br><span class="line">LIDAR_IP=<span class="string">&quot;192.168.1.199&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ============================================================</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$EUID</span> -ne 0 ]]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;请使用 sudo 运行：sudo bash <span class="variable">$0</span>&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> command_name <span class="keyword">in</span> lspci ip nmcli awk grep modprobe; <span class="keyword">do</span></span><br><span class="line">  <span class="keyword">if</span> ! <span class="built_in">command</span> -v <span class="string">&quot;<span class="variable">$command_name</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;缺少命令：<span class="variable">$command_name</span>&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span> == <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span> ]]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;两个用途不能配置到同一个网口&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;普通设备网口：<span class="variable">$GENERAL_IF</span> -&gt; <span class="variable">$GENERAL_IP</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;雷达专用网口：<span class="variable">$LIDAR_IF</span> -&gt; <span class="variable">$LIDAR_HOST_IP</span>&quot;</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Mid-360S：<span class="variable">$LIDAR_IP</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 当前 SSH 如果正经过目标有线口，直接重连可能导致脚本执行一半时断线。</span></span><br><span class="line"><span class="keyword">if</span> [[ -n <span class="string">&quot;<span class="variable">$&#123;SSH_CONNECTION:-&#125;</span>&quot;</span> ]]; <span class="keyword">then</span></span><br><span class="line">  SSH_CLIENT_IP=<span class="string">&quot;<span class="variable">$&#123;SSH_CONNECTION%% *&#125;</span>&quot;</span></span><br><span class="line">  SSH_ROUTE=<span class="string">&quot;<span class="subst">$(ip -4 route get <span class="string">&quot;<span class="variable">$SSH_CLIENT_IP</span>&quot;</span> 2&gt;/dev/null | head -n 1 || true)</span>&quot;</span></span><br><span class="line">  <span class="keyword">if</span> [[ <span class="string">&quot;<span class="variable">$SSH_ROUTE</span>&quot;</span> == *<span class="string">&quot; dev <span class="variable">$GENERAL_IF</span> &quot;</span>* || <span class="string">&quot;<span class="variable">$SSH_ROUTE</span>&quot;</span> == *<span class="string">&quot; dev <span class="variable">$LIDAR_IF</span> &quot;</span>* ]]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;当前 SSH 正通过待重配网口连接：<span class="variable">$SSH_ROUTE</span>&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;请改用 Wi-Fi、显示器或串口后重新运行&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[1/5] 检查 RTL8168 驱动&quot;</span></span><br><span class="line"></span><br><span class="line">RTL8168_INFO=<span class="string">&quot;<span class="subst">$(lspci -nnk -d 10ec:8168 2&gt;/dev/null || true)</span>&quot;</span></span><br><span class="line"><span class="keyword">if</span> [[ -n <span class="string">&quot;<span class="variable">$RTL8168_INFO</span>&quot;</span> ]] &amp;&amp; ! grep -q <span class="string">&quot;Kernel driver in use:&quot;</span> &lt;&lt;&lt;<span class="string">&quot;<span class="variable">$RTL8168_INFO</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;发现 RTL8168，但没有驱动，开始安装 r8168-dkms&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> [[ ! -e <span class="string">&quot;/lib/modules/<span class="subst">$(uname -r)</span>/build&quot;</span> ]]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;缺少与 <span class="subst">$(uname -r)</span> 匹配的 Jetson 内核头文件&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;请先安装当前 L4T 对应的 nvidia-l4t-kernel-headers&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  apt-get update</span><br><span class="line">  <span class="built_in">env</span> DEBIAN_FRONTEND=noninteractive apt-get install -y r8168-dkms</span><br><span class="line">  modprobe r8168</span><br><span class="line">  udevadm settle || <span class="literal">true</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;RTL8168 已经有驱动，跳过安装&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> interface_name <span class="keyword">in</span> <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span> <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span>; <span class="keyword">do</span></span><br><span class="line">  <span class="keyword">if</span> [[ ! -d <span class="string">&quot;/sys/class/net/<span class="variable">$interface_name</span>&quot;</span> ]]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;找不到网口：<span class="variable">$interface_name</span>&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;当前网口如下，请修改脚本顶部的网口名：&quot;</span></span><br><span class="line">    ip -br <span class="built_in">link</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[2/5] 备份 NetworkManager 配置&quot;</span></span><br><span class="line"></span><br><span class="line">BACKUP_DIR=<span class="string">&quot;/var/backups/jetson-dual-ethernet/<span class="subst">$(date +%Y%m%d-%H%M%S)</span>&quot;</span></span><br><span class="line"><span class="built_in">mkdir</span> -p <span class="string">&quot;<span class="variable">$BACKUP_DIR</span>&quot;</span></span><br><span class="line"><span class="keyword">if</span> [[ -d /etc/NetworkManager/system-connections ]]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">cp</span> -a /etc/NetworkManager/system-connections <span class="string">&quot;<span class="variable">$BACKUP_DIR</span>/&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;配置已备份到：<span class="variable">$BACKUP_DIR</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">configure_profile</span></span>() &#123;</span><br><span class="line">  <span class="built_in">local</span> profile_name=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">  <span class="built_in">local</span> interface_name=<span class="string">&quot;<span class="variable">$2</span>&quot;</span></span><br><span class="line">  <span class="built_in">local</span> address=<span class="string">&quot;<span class="variable">$3</span>&quot;</span></span><br><span class="line">  <span class="built_in">local</span> route_metric=<span class="string">&quot;<span class="variable">$4</span>&quot;</span></span><br><span class="line">  <span class="built_in">local</span> autoconnect_priority=<span class="string">&quot;<span class="variable">$5</span>&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> ! nmcli connection show <span class="string">&quot;<span class="variable">$profile_name</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    nmcli connection add \</span><br><span class="line">      <span class="built_in">type</span> ethernet \</span><br><span class="line">      ifname <span class="string">&quot;<span class="variable">$interface_name</span>&quot;</span> \</span><br><span class="line">      con-name <span class="string">&quot;<span class="variable">$profile_name</span>&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  nmcli connection modify <span class="string">&quot;<span class="variable">$profile_name</span>&quot;</span> \</span><br><span class="line">    connection.interface-name <span class="string">&quot;<span class="variable">$interface_name</span>&quot;</span> \</span><br><span class="line">    802-3-ethernet.mac-address <span class="string">&quot;&quot;</span> \</span><br><span class="line">    connection.autoconnect <span class="built_in">yes</span> \</span><br><span class="line">    connection.autoconnect-priority <span class="string">&quot;<span class="variable">$autoconnect_priority</span>&quot;</span> \</span><br><span class="line">    ipv4.method manual \</span><br><span class="line">    ipv4.addresses <span class="string">&quot;<span class="variable">$address</span>&quot;</span> \</span><br><span class="line">    ipv4.gateway <span class="string">&quot;&quot;</span> \</span><br><span class="line">    ipv4.dns <span class="string">&quot;&quot;</span> \</span><br><span class="line">    ipv4.ignore-auto-dns <span class="built_in">yes</span> \</span><br><span class="line">    ipv4.never-default <span class="built_in">yes</span> \</span><br><span class="line">    ipv4.route-metric <span class="string">&quot;<span class="variable">$route_metric</span>&quot;</span> \</span><br><span class="line">    ipv4.routes <span class="string">&quot;&quot;</span> \</span><br><span class="line">    ipv6.method disabled</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[3/5] 为两个网口创建独立配置&quot;</span></span><br><span class="line"></span><br><span class="line">nmcli device <span class="built_in">set</span> <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span> managed <span class="built_in">yes</span></span><br><span class="line">nmcli device <span class="built_in">set</span> <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span> managed <span class="built_in">yes</span></span><br><span class="line"></span><br><span class="line">configure_profile <span class="string">&quot;<span class="variable">$GENERAL_PROFILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span> <span class="string">&quot;<span class="variable">$GENERAL_IP</span>&quot;</span> 100 50</span><br><span class="line">configure_profile <span class="string">&quot;<span class="variable">$LIDAR_PROFILE</span>&quot;</span> <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span> <span class="string">&quot;<span class="variable">$LIDAR_HOST_IP</span>&quot;</span> 500 100</span><br><span class="line"></span><br><span class="line"><span class="comment"># 两个网口处于同一 /24 网段时，强制 Mid-360S 从雷达专用口访问。</span></span><br><span class="line">nmcli connection modify <span class="string">&quot;<span class="variable">$LIDAR_PROFILE</span>&quot;</span> \</span><br><span class="line">  ipv4.routes <span class="string">&quot;<span class="variable">$LIDAR_IP</span>/32 0.0.0.0 10&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[4/5] 激活两个网口&quot;</span></span><br><span class="line"></span><br><span class="line">nmcli connection reload</span><br><span class="line">nmcli connection down <span class="string">&quot;<span class="variable">$GENERAL_PROFILE</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1 || <span class="literal">true</span></span><br><span class="line">nmcli connection down <span class="string">&quot;<span class="variable">$LIDAR_PROFILE</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1 || <span class="literal">true</span></span><br><span class="line">nmcli connection up <span class="string">&quot;<span class="variable">$GENERAL_PROFILE</span>&quot;</span> ifname <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span></span><br><span class="line">nmcli connection up <span class="string">&quot;<span class="variable">$LIDAR_PROFILE</span>&quot;</span> ifname <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;[5/5] 验证结果&quot;</span></span><br><span class="line"></span><br><span class="line">nmcli -f DEVICE,TYPE,STATE,CONNECTION device status</span><br><span class="line">ip -4 -br address show dev <span class="string">&quot;<span class="variable">$GENERAL_IF</span>&quot;</span></span><br><span class="line">ip -4 -br address show dev <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Mid-360S 实际选路：&quot;</span></span><br><span class="line">ip -4 route get <span class="string">&quot;<span class="variable">$LIDAR_IP</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> ping -I <span class="string">&quot;<span class="variable">$LIDAR_IF</span>&quot;</span> -c 3 -W 1 <span class="string">&quot;<span class="variable">$LIDAR_IP</span>&quot;</span>; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;配置成功：两个网口已独立启用，Mid-360S 可以访问&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;两个网口配置已完成，但 Mid-360S 没有响应 ping&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;请检查雷达电源、网线和 LIDAR_IP&quot;</span></span><br><span class="line">  <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></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"><span class="built_in">chmod</span> +x ~/enable_jetson_dual_eth.sh</span><br><span class="line"><span class="built_in">sudo</span> ~/enable_jetson_dual_eth.sh</span><br></pre></td></tr></table></figure><p>脚本成功后，最后应看到：</p><figure class="highlight text"><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">enP7p1s0  ethernet  connected  device-enP7</span><br><span class="line">enP8p1s0  ethernet  connected  102</span><br><span class="line"></span><br><span class="line">192.168.1.199 dev enP8p1s0 src 192.168.1.102</span><br></pre></td></tr></table></figure><p>脚本可以重复运行。每次修改前都会将 NetworkManager 配置备份到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/var/backups/jetson-dual-ethernet/</span><br></pre></td></tr></table></figure><h2 id="三、问题一：为什么第二个网口无法打开">三、问题一：为什么第二个网口无法打开</h2><p>先查看 PCI 网卡和驱动：</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">lspci -nnk -d 10ec:</span><br></pre></td></tr></table></figure><p>当时可以看到两个控制器：</p><figure class="highlight text"><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">RTL8125 2.5GbE Controller</span><br><span class="line">RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller</span><br></pre></td></tr></table></figure><p>第一个控制器有：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Kernel driver in use: r8125</span><br></pre></td></tr></table></figure><p>第二个 RTL8168 却没有 <code>Kernel driver in use</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">ip -br <span class="built_in">link</span></span><br></pre></td></tr></table></figure><p>这说明 PCIe 已经识别到硬件，但内核没有为第二个控制器创建网络接口。此时执行 <code>ip link set up</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> apt-get install -y r8168-dkms</span><br><span class="line"><span class="built_in">sudo</span> modprobe r8168</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></pre></td><td class="code"><pre><span class="line">dkms status | grep r8168</span><br><span class="line">lspci -nnk -d 10ec:8168</span><br><span class="line">ethtool -i enP8p1s0</span><br></pre></td></tr></table></figure><p>这台 Jetson 的最终结果为：</p><figure class="highlight text"><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">r8168/8.049.02, 5.15.148-tegra, aarch64: installed</span><br><span class="line">Kernel driver in use: r8168</span><br><span class="line">driver: r8168</span><br></pre></td></tr></table></figure><h2 id="四、问题二：为什么打开一个网口，另一个就断开">四、问题二：为什么打开一个网口，另一个就断开</h2><p>驱动安装完成后，两个网口的 <code>carrier</code> 都是 <code>1</code>，说明物理连接正常：</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 class="keyword">for</span> nic <span class="keyword">in</span> enP7p1s0 enP8p1s0; <span class="keyword">do</span></span><br><span class="line">  <span class="built_in">printf</span> <span class="string">&#x27;%s carrier=&#x27;</span> <span class="string">&quot;<span class="variable">$nic</span>&quot;</span></span><br><span class="line">  <span class="built_in">cat</span> <span class="string">&quot;/sys/class/net/<span class="variable">$nic</span>/carrier&quot;</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>继续查看 NetworkManager 配置：</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">nmcli -f NAME,TYPE,DEVICE,AUTOCONNECT connection show</span><br><span class="line">nmcli -f \</span><br><span class="line">connection.id,connection.interface-name,802-3-ethernet.mac-address,ipv4.addresses \</span><br><span class="line">  connection show 102</span><br></pre></td></tr></table></figure><p>原来的 <code>102</code> 配置没有绑定网口：</p><figure class="highlight text"><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">connection.interface-name: --</span><br><span class="line">802-3-ethernet.mac-address: --</span><br></pre></td></tr></table></figure><p>NetworkManager 会把这个配置应用到任意一个兼容的有线网口。插入另一根网线时，同一个配置就在两个接口之间迁移，因此表现为“连接一个，另一个断开”。</p><p>正确做法是让每个接口拥有独立配置，并通过接口名固定：</p><figure class="highlight text"><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">device-enP7 -&gt; enP7p1s0 -&gt; 192.168.1.123/24</span><br><span class="line">102         -&gt; enP8p1s0 -&gt; 192.168.1.102/24</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">nmcli connection modify device-enP7 connection.interface-name enP7p1s0</span><br><span class="line">nmcli connection modify 102 connection.interface-name enP8p1s0</span><br></pre></td></tr></table></figure><h2 id="五、两个网口在同一网段时的路由问题">五、两个网口在同一网段时的路由问题</h2><p>两个有线口都使用 <code>192.168.1.0/24</code>，系统会产生两条相同网段的路由：</p><figure class="highlight text"><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">192.168.1.0/24 dev enP7p1s0 src 192.168.1.123 metric 100</span><br><span class="line">192.168.1.0/24 dev enP8p1s0 src 192.168.1.102 metric 500</span><br></pre></td></tr></table></figure><p>Linux 默认会选择 metric 更小的 <code>enP7p1s0</code>。这样即使 Mid-360S 接在 <code>enP8p1s0</code>，发往 <code>192.168.1.199</code> 的数据仍可能走错网口。</p><p>解决方法是添加一条更精确的 <code>/32</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> nmcli connection modify 102 \</span><br><span class="line">  ipv4.routes <span class="string">&quot;192.168.1.199/32 0.0.0.0 10&quot;</span></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 route get 192.168.1.199</span><br></pre></td></tr></table></figure><p>必须看到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">192.168.1.199 dev enP8p1s0 src 192.168.1.102</span><br></pre></td></tr></table></figure><p>如果可以修改设备网段，更推荐让两个网口使用不同网段，例如：</p><figure class="highlight text"><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">普通设备网：192.168.1.0/24</span><br><span class="line">雷达设备网：192.168.2.0/24</span><br></pre></td></tr></table></figure><p>不同网段不会产生这种路由歧义。</p><h2 id="六、Mid-360S-配置同步">六、Mid-360S 配置同步</h2><p>网络修好后，还要把 <code>MID360s_config.json</code> 中的 <code>host_ip</code> 改成雷达专用网口地址：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;host_ip&quot;</span><span class="punctuation">:</span> <span class="string">&quot;192.168.1.102&quot;</span></span><br></pre></td></tr></table></figure><p>雷达地址保持：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;ip&quot;</span><span class="punctuation">:</span> <span class="string">&quot;192.168.1.199&quot;</span></span><br></pre></td></tr></table></figure><p>如果这里仍然写成另一个网口的 <code>192.168.1.123</code>，可能出现能够 ping 通雷达，但 ROS 驱动收不到点云的情况。</p><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">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</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">ros2 topic hz /livox/lidar --window 20</span><br><span class="line">ros2 topic hz /livox/imu --window 100</span><br></pre></td></tr></table></figure><p>这次实际测试结果为：</p><figure class="highlight text"><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">/livox/lidar  约 10 Hz</span><br><span class="line">/livox/imu    约 200 Hz</span><br></pre></td></tr></table></figure><h2 id="七、快速排查命令">七、快速排查命令</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. PCIe 是否识别到两个控制器，驱动是否已经绑定</span></span><br><span class="line">lspci -nnk -d 10ec:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 内核是否创建了两个网口</span></span><br><span class="line">ip -br <span class="built_in">link</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 两个接口分别使用什么驱动</span></span><br><span class="line">ethtool -i enP7p1s0</span><br><span class="line">ethtool -i enP8p1s0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 网线是否真正连接</span></span><br><span class="line"><span class="built_in">cat</span> /sys/class/net/enP7p1s0/carrier</span><br><span class="line"><span class="built_in">cat</span> /sys/class/net/enP8p1s0/carrier</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 两个接口是否分别连接到独立 profile</span></span><br><span class="line">nmcli -f DEVICE,TYPE,STATE,CONNECTION device status</span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 查看地址和路由</span></span><br><span class="line">ip -4 -br address</span><br><span class="line">ip -4 route</span><br><span class="line"></span><br><span class="line"><span class="comment"># 7. 确认 Mid-360S 最终走哪个网口</span></span><br><span class="line">ip route get 192.168.1.199</span><br><span class="line"></span><br><span class="line"><span class="comment"># 8. 强制从雷达口测试</span></span><br><span class="line">ping -I enP8p1s0 192.168.1.199</span><br></pre></td></tr></table></figure><h2 id="八、常见问题">八、常见问题</h2><h3 id="1-安装-r8168-dkms-时报找不到内核头文件">1. 安装 r8168-dkms 时报找不到内核头文件</h3><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">ls</span> -ld <span class="string">&quot;/lib/modules/<span class="subst">$(uname -r)</span>/build&quot;</span></span><br></pre></td></tr></table></figure><p>需要安装与当前 Jetson Linux/L4T 版本匹配的 <code>nvidia-l4t-kernel-headers</code>。不要使用与当前 <code>*-tegra</code> 内核不匹配的 generic 头文件。</p><h3 id="2-设置-MAC-后提示没有合适的设备">2. 设置 MAC 后提示没有合适的设备</h3><p>运行时 MAC、克隆 MAC 和永久 MAC 可能不同：</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">nmcli -f GENERAL.HWADDR,GENERAL.PERM-HWADDR device show enP7p1s0</span><br><span class="line">ethtool -P enP7p1s0</span><br></pre></td></tr></table></figure><p>如果接口名稳定，直接使用 <code>connection.interface-name</code> 绑定即可。本文脚本会清空 profile 中的 MAC 限制，避免当前 MAC 和永久 MAC 不一致导致连接失败。</p><h3 id="3-两个网口都显示-connected，但雷达仍然不通">3. 两个网口都显示 connected，但雷达仍然不通</h3><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 route get 192.168.1.199</span><br></pre></td></tr></table></figure><p>如果没有走 <code>enP8p1s0</code>，重新添加 <code>/32</code> 路由并激活配置：</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> nmcli connection modify 102 \</span><br><span class="line">  ipv4.routes <span class="string">&quot;192.168.1.199/32 0.0.0.0 10&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> nmcli connection up 102 ifname enP8p1s0</span><br></pre></td></tr></table></figure><h3 id="4-重启后又恢复原状">4. 重启后又恢复原状</h3><p>检查两个 profile 是否开启自动连接并绑定正确接口：</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></pre></td><td class="code"><pre><span class="line">nmcli -f \</span><br><span class="line">connection.id,connection.interface-name,connection.autoconnect,ipv4.addresses,ipv4.routes \</span><br><span class="line">  connection show device-enP7</span><br><span class="line"></span><br><span class="line">nmcli -f \</span><br><span class="line">connection.id,connection.interface-name,connection.autoconnect,ipv4.addresses,ipv4.routes \</span><br><span class="line">  connection show 102</span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>本次问题由三个环节共同造成：</p><figure class="highlight text"><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">RTL8168 没有驱动</span><br><span class="line">  -&gt; 第二个网口没有出现在系统中</span><br><span class="line"></span><br><span class="line">两个网口共用一个未绑定接口的 NetworkManager profile</span><br><span class="line">  -&gt; 连接一个时另一个断开</span><br><span class="line"></span><br><span class="line">两个网口处于相同的 192.168.1.0/24 网段</span><br><span class="line">  -&gt; 发往 Mid-360S 的数据可能走错网口</span><br></pre></td></tr></table></figure><p>对应的解决方法是：</p><ol><li>安装并加载 <code>r8168-dkms</code>。</li><li>为两个网口创建独立 profile，并通过接口名绑定。</li><li>不给设备专用网口配置默认网关。</li><li>使用 <code>/32</code> 路由将 <code>192.168.1.199</code> 固定到 <code>enP8p1s0</code>。</li><li>将 Livox 配置中的 <code>host_ip</code> 修改为 <code>192.168.1.102</code>。</li></ol><p>完成后，两个网口可以同时连接各自设备，并在 Jetson 重启后自动恢复。</p>]]></content>
    
    
    <summary type="html">使用一个脚本解决 Jetson Orin NX 第二个网口没有驱动、两个网口只能连接一个以及同网段路由冲突的问题。</summary>
    
    
    
    <category term="传感器硬件" scheme="https://dreamer198.top/categories/%E4%BC%A0%E6%84%9F%E5%99%A8%E7%A1%AC%E4%BB%B6/"/>
    
    
    <category term="Jetson" scheme="https://dreamer198.top/tags/Jetson/"/>
    
    <category term="NetworkManager" scheme="https://dreamer198.top/tags/NetworkManager/"/>
    
    <category term="双网口" scheme="https://dreamer198.top/tags/%E5%8F%8C%E7%BD%91%E5%8F%A3/"/>
    
    <category term="Livox" scheme="https://dreamer198.top/tags/Livox/"/>
    
  </entry>
  
  <entry>
    <title>UAV Autonomy All-in-One：统一仿真、真机与多规划器的无人机自主飞行框架</title>
    <link href="https://dreamer198.top/2026/08/06/UAV-Autonomy-All-in-One%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D/"/>
    <id>https://dreamer198.top/2026/08/06/UAV-Autonomy-All-in-One%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D/</id>
    <published>2026-08-06T14:13:35.000Z</published>
    <updated>2026-08-06T14:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>在复现无人机路径规划算法时，我逐渐发现，真正困难的不只是把某个算法编译起来，而是让定位、规划、控制、任务和飞控组成一条稳定、可复用的完整链路。不同项目的依赖和接口各不相同，切换算法时经常需要重新整理环境。</p><p>为了解决这些问题，我开发了 <a href="https://github.com/dreamer198/UAV-Autonomy-All-in-One">UAV Autonomy All-in-One</a>。这是一个基于 ROS1 Noetic、PX4 和 Gazebo Classic 的无人机自主飞行框架，希望用同一套工程结构连接仿真与真机，并让不同规划器能够以相对独立、平等的方式接入。</p><span id="more"></span><h2 id="项目定位">项目定位</h2><p>这个项目并不是新的路径规划算法，而是一个面向无人机自主飞行的工程框架。它主要关注：</p><ul><li>统一仿真与真机的定位、任务、规划和控制链路；</li><li>隔离不同开源算法的依赖，减少相互影响；</li><li>使用一致的方式选择和运行不同规划器；</li><li>保留必要的状态检查、故障处理、日志和飞行数据；</li><li>为后续接入新的规划器和传感器预留清晰边界。</li></ul><p>仿真侧由 PX4 SITL、Gazebo、模拟 MID-360、规划器和控制器组成；真机侧使用 PX4、MID-360、FAST-LIO、规划器和控制器。两套环境在底层传感器上有所不同，但上层任务和自主飞行逻辑尽量保持一致。</p><h2 id="当前支持的规划器">当前支持的规划器</h2><p><code>main</code> 分支目前支持四个规划器：</p><table><thead><tr><th>规划器</th><th>简介</th></tr></thead><tbody><tr><td>Diff-Planner</td><td>面向局部自主飞行的轨迹规划方法</td></tr><tr><td>Fast-Planner Kino</td><td>基于 Kinodynamic 搜索的 Fast-Planner 模式</td></tr><tr><td>Fast-Planner Topo</td><td>加入拓扑路径搜索的 Fast-Planner 模式</td></tr><tr><td>SUPER</td><td>强调安全性与局部重规划的规划器</td></tr></tbody></table><p>这些规划器分别保留自己的地图、搜索和轨迹生成方式，但通过公共框架接入任务、控制与可视化链路。不同算法在启动前选择，当前不支持飞行过程中切换或多个规划器同时控制飞机。</p><h2 id="仿真场景">仿真场景</h2><p>项目内置室内和森林场景。下面是 forest 测试环境：</p><figure>  <img src="/img/uav-autonomy-all-in-one/forest.png" alt="UAV Autonomy All-in-One forest 仿真测试场景">  <figcaption style="text-align:center">forest 仿真场景</figcaption></figure><p>以下演示来自项目仓库，均为原录像的 4 倍速。视频会静音循环播放，并在进入可视区域后按需加载：</p><table>  <tr>    <th>Diff-Planner</th>    <th>Fast Kino</th>  </tr>  <tr>    <td><video class="planner-demo-video" data-autoplay muted loop playsinline preload="none" aria-label="Diff-Planner 仿真效果"><source src="/img/uav-autonomy-all-in-one/diff.webm" type="video/webm">你的浏览器暂不支持 WebM 视频。</video></td>    <td><video class="planner-demo-video" data-autoplay muted loop playsinline preload="none" aria-label="Fast Kino 仿真效果"><source src="/img/uav-autonomy-all-in-one/fast-kino.webm" type="video/webm">你的浏览器暂不支持 WebM 视频。</video></td>  </tr></table><table>  <tr>    <th>Fast Topo</th>    <th>SUPER</th>  </tr>  <tr>    <td><video class="planner-demo-video" data-autoplay muted loop playsinline preload="none" aria-label="Fast Topo 仿真效果"><source src="/img/uav-autonomy-all-in-one/fast-topo.webm" type="video/webm">你的浏览器暂不支持 WebM 视频。</video></td>    <td><video class="planner-demo-video" data-autoplay muted loop playsinline preload="none" aria-label="SUPER 仿真效果"><source src="/img/uav-autonomy-all-in-one/super.webm" type="video/webm">你的浏览器暂不支持 WebM 视频。</video></td>  </tr></table><h2 id="项目特点">项目特点</h2><p>相比单独维护多个算法仓库，这个项目更关注完整系统的一致性：</p><ul><li>各规划器使用隔离的构建环境，避免同名依赖相互覆盖；</li><li>仿真与真机复用任务执行和安全检查逻辑；</li><li>统一展示环境、目标、规划轨迹和实际飞行路径；</li><li>自动保存运行日志和 rosbag，方便回放与排查；</li><li>人工接管、定位异常和规划故障具有明确的处理边界。</li></ul><h2 id="当前边界">当前边界</h2><p>框架已经覆盖仿真和真机的软件链路，但“能够构建和启动”不等于已经完成真实飞行验证。真机使用前仍需要针对具体机体完成传感器外参、定位、控制器、PX4 failsafe 和遥控接管测试。</p><p>当前项目面向单机、单规划器执行，暂不提供空中切换、自动规划器回退、多规划器并行控制、地图共享或统一的动态障碍预测接口。</p><p>更详细的环境说明、使用方法、参数配置和安全注意事项请查看项目仓库：</p><p><a href="https://github.com/dreamer198/UAV-Autonomy-All-in-One">dreamer198/UAV-Autonomy-All-in-One</a></p>]]></content>
    
    
    <summary type="html">在复现无人机路径规划算法时，我逐渐发现，真正困难的不只是把某个算法编译起来，而是让定位、规划、控制、任务和飞控组成一条稳定、可复用的完整链路。不同项目的依赖和接口各不相同，切换算法时经常需要重新整理环境。 为了解决这些问题，我开发了 UAV Autonomy All-in-One。这是一个基于 ROS1 N…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="路径规划" scheme="https://dreamer198.top/tags/%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="Docker" scheme="https://dreamer198.top/tags/Docker/"/>
    
    <category term="ROS1" scheme="https://dreamer198.top/tags/ROS1/"/>
    
    <category term="Gazebo" scheme="https://dreamer198.top/tags/Gazebo/"/>
    
    <category term="无人机自主飞行" scheme="https://dreamer198.top/tags/%E6%97%A0%E4%BA%BA%E6%9C%BA%E8%87%AA%E4%B8%BB%E9%A3%9E%E8%A1%8C/"/>
    
  </entry>
  
  <entry>
    <title>Docker 容器内 Fast-Planner 下载、编译与测试</title>
    <link href="https://dreamer198.top/2026/07/13/Fast-Planner%E5%A4%8D%E7%8E%B0%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/07/13/Fast-Planner%E5%A4%8D%E7%8E%B0%E6%95%99%E7%A8%8B/</id>
    <published>2026-07-13T02:00:00.000Z</published>
    <updated>2026-07-13T04:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文是<a href="/2026/04/30/Docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/">《Docker使用教程》</a>的后续，默认已经按照其中的步骤创建了以下环境：</p><ul><li>容器名：<code>ros_noetic</code></li><li>基础镜像：<code>osrf/ros:noetic-desktop-full</code></li><li>宿主机 <code>~/docker/ros_root</code> 已挂载到容器 <code>/root</code></li><li>X11 目录和认证文件已经挂载，容器可以运行 RViz</li></ul><p>这里的“全新容器”是指容器已经创建，但还没有下载 Fast-Planner，也没有安装项目依赖。本文不再重复安装 Docker、拉取 ROS 镜像或创建容器，只介绍如何在这个容器中下载、编译和测试 <a href="https://github.com/dreamer198/Fast-Planner.git">dreamer198/Fast-Planner</a>。</p><span id="more"></span><p>本文默认容器内使用 <code>root</code> 用户，因此 <code>~</code> 就是 <code>/root</code>，安装命令不需要 <code>sudo</code>。没有特别说明时，命令都在容器内执行；需要在宿主机执行的命令会单独标出。</p><p>Fast-Planner 官方验证过 Ubuntu 20.04 + ROS Noetic。本文只运行项目自带的轻量仿真，不包含 PX4、Gazebo 或真机控制。</p><h2 id="一、进入已经创建的容器">一、进入已经创建的容器</h2><p>如果创建容器后已经停在 <code>root@...</code> 提示符，可以直接进入下一节。</p><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">docker start -ai ros_noetic</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">docker <span class="built_in">exec</span> -it ros_noetic bash</span><br></pre></td></tr></table></figure><p>进入后确认 ROS 版本：</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">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">rosversion -d</span><br></pre></td></tr></table></figure><p>输出应为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">noetic</span><br></pre></td></tr></table></figure><h2 id="二、安装编译依赖">二、安装编译依赖</h2><p>ROS Noetic Desktop Full 已经包含 ROS 和 RViz，不需要再次安装。只补充源码编译需要的工具和库：</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></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  git \</span><br><span class="line">  build-essential \</span><br><span class="line">  cmake \</span><br><span class="line">  libarmadillo-dev \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  libpcl-dev \</span><br><span class="line">  qtbase5-dev \</span><br><span class="line">  ros-noetic-cmake-modules \</span><br><span class="line">  ros-noetic-cv-bridge \</span><br><span class="line">  ros-noetic-dynamic-reconfigure \</span><br><span class="line">  ros-noetic-image-transport \</span><br><span class="line">  ros-noetic-pcl-ros</span><br></pre></td></tr></table></figure><h3 id="安装-NLopt-2-7-1">安装 NLopt 2.7.1</h3><p>Fast-Planner 的 <code>bspline_opt</code> 会从 <code>/usr/local/include</code> 和 <code>/usr/local/lib/libnlopt.so</code> 查找 NLopt。按照项目 README 指定的 v2.7.1 编译安装：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /root/deps</span><br><span class="line"><span class="built_in">cd</span> /root/deps</span><br><span class="line">git <span class="built_in">clone</span> --branch v2.7.1 --depth 1 https://github.com/stevengj/nlopt.git</span><br><span class="line"></span><br><span class="line">cmake -S nlopt -B nlopt/build \</span><br><span class="line">  -DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">  -DCMAKE_INSTALL_PREFIX=/usr/local \</span><br><span class="line">  -DCMAKE_INSTALL_LIBDIR=lib</span><br><span class="line">cmake --build nlopt/build -j<span class="string">&quot;<span class="subst">$(nproc)</span>&quot;</span></span><br><span class="line">cmake --install nlopt/build</span><br><span class="line">ldconfig</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"><span class="built_in">test</span> -f /usr/local/include/nlopt.hpp &amp;&amp; <span class="built_in">echo</span> <span class="string">&quot;nlopt.hpp: OK&quot;</span></span><br><span class="line"><span class="built_in">test</span> -f /usr/local/lib/libnlopt.so &amp;&amp; <span class="built_in">echo</span> <span class="string">&quot;libnlopt.so: OK&quot;</span></span><br></pre></td></tr></table></figure><p>两行都显示 <code>OK</code> 后再继续。</p><h2 id="三、下载-Fast-Planner">三、下载 Fast-Planner</h2><p>新建独立的 catkin 工作空间，并将代码克隆到 <code>src</code> 中：</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">mkdir</span> -p /root/fast_planner_ws/src</span><br><span class="line"><span class="built_in">cd</span> /root/fast_planner_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/dreamer198/Fast-Planner.git</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /root/fast_planner_ws/src/Fast-Planner</span><br><span class="line">git remote get-url origin</span><br><span class="line">git rev-parse --short HEAD</span><br></pre></td></tr></table></figure><p>第一条命令应输出：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://github.com/dreamer198/Fast-Planner.git</span><br></pre></td></tr></table></figure><p>记录第二条命令输出的 commit，后续遇到问题时可以确认使用的是不是同一版代码。</p><h2 id="四、编译工作空间">四、编译工作空间</h2><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">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">cd</span> /root/fast_planner_ws</span><br><span class="line">catkin_make -DCMAKE_BUILD_TYPE=Release -j<span class="string">&quot;<span class="subst">$(nproc)</span>&quot;</span></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">catkin_make -DCMAKE_BUILD_TYPE=Release -j2</span><br></pre></td></tr></table></figure><p>编译完成后加载工作空间，并确认 ROS 能找到核心包：</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">source</span> /root/fast_planner_ws/devel/setup.bash</span><br><span class="line">rospack find plan_manage</span><br></pre></td></tr></table></figure><p>正常情况下会输出：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/root/fast_planner_ws/src/Fast-Planner/fast_planner/plan_manage</span><br></pre></td></tr></table></figure><p>项目没有提供覆盖这些规划流程的自动化单元测试，因此下面通过仓库自带的仿真 Demo 完成功能测试。</p><h2 id="五、运行-Kinodynamic-规划测试">五、运行 Kinodynamic 规划测试</h2><p>该测试需要两个终端，它们都进入同一个 <code>ros_noetic</code> 容器。第一个终端运行 RViz，第二个终端运行规划器和轻量仿真器。</p><h3 id="1-终端-1：启动-RViz">1. 终端 1：启动 RViz</h3><p>如果 RViz 还没有图形显示权限，先在宿主机执行：</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">xhost +SI:localuser:root</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">docker <span class="built_in">exec</span> -it \</span><br><span class="line">  -e DISPLAY=<span class="string">&quot;<span class="variable">$DISPLAY</span>&quot;</span> \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  ros_noetic bash</span><br></pre></td></tr></table></figure><p>在容器内启动 RViz：</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">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> /root/fast_planner_ws/devel/setup.bash</span><br><span class="line">roslaunch plan_manage rviz.launch</span><br></pre></td></tr></table></figure><h3 id="2-终端-2：启动规划器">2. 终端 2：启动规划器</h3><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">docker <span class="built_in">exec</span> -it ros_noetic bash</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> /root/fast_planner_ws/devel/setup.bash</span><br><span class="line">roslaunch plan_manage kino_replan.launch</span><br></pre></td></tr></table></figure><h3 id="3-在-RViz-中发送目标">3. 在 RViz 中发送目标</h3><p>等待 RViz 中出现随机障碍物和无人机模型，然后：</p><ol><li>确认 RViz 的 <code>Fixed Frame</code> 为 <code>world</code>。</li><li>点击工具栏中的 <code>2D Nav Goal</code>。</li><li>在地图内没有障碍物的位置点击并拖动，发送一个目标点。</li></ol><p>当前仓库的 Kinodynamic Demo 默认使用手动目标模式。目标的 X、Y 坐标来自点击位置，飞行高度默认为 1 m。正常情况下会立即生成 B-spline 轨迹，无人机随后沿轨迹飞向目标。</p><h2 id="六、检查测试结果">六、检查测试结果</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">docker <span class="built_in">exec</span> -it ros_noetic bash</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"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> /root/fast_planner_ws/devel/setup.bash</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">rosnode list</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">rostopic hz /state_ukf/odom</span><br></pre></td></tr></table></figure><p>看到稳定频率后按 <code>Ctrl+C</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">rostopic hz /pcl_render_node/cloud</span><br></pre></td></tr></table></figure><p>点云同样应持续更新，看到频率后按 <code>Ctrl+C</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">rostopic <span class="built_in">echo</span> -n 1 /planning/bspline</span><br></pre></td></tr></table></figure><p>这条命令会等待下一条轨迹消息。保持命令运行，回到 RViz 再发送一次目标；终端打印出 B-spline 消息并自动退出，说明规划器已经生成轨迹。</p><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">rostopic hz /planning/pos_cmd</span><br></pre></td></tr></table></figure><p>如果能看到持续更新的频率，说明“目标输入 -&gt; 路径规划 -&gt; 轨迹下发”链路已经跑通。</p><h2 id="七、运行-Topological-规划测试">七、运行 Topological 规划测试</h2><p>先在运行 <code>kino_replan.launch</code> 的终端按 <code>Ctrl+C</code> 停止 Kinodynamic Demo，RViz 可以继续运行。不要同时启动两个 Demo，因为它们会创建同名节点并使用相同话题。</p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> /root/fast_planner_ws/devel/setup.bash</span><br><span class="line">roslaunch plan_manage topo_replan.launch</span><br></pre></td></tr></table></figure><p>这个仓库的 <code>topo_replan.launch</code> 已经默认使用 <code>flight_type=1</code>，可以直接在 RViz 中通过 <code>2D Nav Goal</code> 发送目标，不需要修改 launch 文件。看到多条拓扑候选路径和最终轨迹，并且无人机开始执行轨迹，即表示 Topological Demo 测试通过。</p><h2 id="八、后续重新运行">八、后续重新运行</h2><p>代码和编译结果都位于 <code>/root</code>。按照《Docker使用教程》的挂载方式，它们会同步保存在宿主机 <code>~/docker/ros_root</code> 中。<code>apt</code> 安装的软件和 <code>/usr/local</code> 下的 NLopt 属于容器文件系统；停止再启动同一个容器时仍然存在，但删除容器后不会由 <code>/root</code> 挂载保留。</p><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"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> /root/fast_planner_ws/devel/setup.bash</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">roslaunch plan_manage rviz.launch</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">roslaunch plan_manage kino_replan.launch</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">roslaunch plan_manage topo_replan.launch</span><br></pre></td></tr></table></figure><p>如果本次测试结束后不再运行容器中的图形程序，可以在宿主机撤销临时的 X11 授权：</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">xhost -SI:localuser:root</span><br></pre></td></tr></table></figure><h2 id="九、常见问题">九、常见问题</h2><h3 id="1-roslaunch-找不到-plan-manage">1. <code>roslaunch</code> 找不到 <code>plan_manage</code></h3><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">cd</span> /root/fast_planner_ws</span><br><span class="line">catkin_make -DCMAKE_BUILD_TYPE=Release -j2</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">rospack find plan_manage</span><br></pre></td></tr></table></figure><h3 id="2-链接时找不到-libnlopt-so">2. 链接时找不到 <code>libnlopt.so</code></h3><p>检查 NLopt 的安装位置并刷新缓存：</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 class="built_in">ls</span> -l /usr/local/include/nlopt.hpp</span><br><span class="line"><span class="built_in">ls</span> -l /usr/local/lib/libnlopt.so</span><br><span class="line">ldconfig</span><br><span class="line">ldconfig -p | grep nlopt</span><br></pre></td></tr></table></figure><p>项目明确查找 <code>/usr/local/lib/libnlopt.so</code>。如果动态库被安装到了 <code>/usr/local/lib64</code>，重新执行第二节的 CMake 命令，并保留 <code>-DCMAKE_INSTALL_LIBDIR=lib</code>。</p><h3 id="3-RViz-无法打开或提示-X11-权限错误">3. RViz 无法打开或提示 X11 权限错误</h3><p>先退出到宿主机，确认容器仍然按照《Docker使用教程》挂载了 <code>/tmp/.X11-unix</code> 和 <code>.Xauthority</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><p>再使用第五节中带 <code>DISPLAY</code> 的 <code>docker exec</code> 命令进入容器并启动 RViz。</p><h3 id="4-RViz-中有地图，但点击目标后没有轨迹">4. RViz 中有地图，但点击目标后没有轨迹</h3><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></pre></td><td class="code"><pre><span class="line">rostopic <span class="built_in">echo</span> -n 1 /move_base_simple/goal</span><br><span class="line">rostopic <span class="built_in">echo</span> -n 1 /waypoint_generator/waypoints</span><br><span class="line">rostopic <span class="built_in">echo</span> -n 1 /planning/bspline</span><br></pre></td></tr></table></figure><p>每条命令都应先运行，再回到 RViz 发送一次新目标。同时确认目标位于地图范围内、没有落在障碍物中，并且当前只运行了一个规划 Demo。</p><h2 id="参考资料">参考资料</h2><ul><li><a href="https://github.com/dreamer198/Fast-Planner">dreamer198/Fast-Planner</a></li><li><a href="https://github.com/HKUST-Aerial-Robotics/Fast-Planner">Fast-Planner 上游仓库</a></li><li><a href="https://github.com/stevengj/nlopt/tree/v2.7.1">NLopt v2.7.1</a></li></ul>]]></content>
    
    
    <summary type="html">前言 本文是《Docker使用教程》的后续，默认已经按照其中的步骤创建了以下环境： 容器名：rosnoetic 基础镜像：osrf/ros:noetic-desktop-full 宿主机 /docker/rosroot 已挂载到容器 /root X11 目录和认证文件已经挂载，容器可以运行 RViz 这里的…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="路径规划" scheme="https://dreamer198.top/tags/%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92/"/>
    
    <category term="Docker" scheme="https://dreamer198.top/tags/Docker/"/>
    
    <category term="ROS1" scheme="https://dreamer198.top/tags/ROS1/"/>
    
    <category term="Fast-Planner" scheme="https://dreamer198.top/tags/Fast-Planner/"/>
    
    <category term="RViz" scheme="https://dreamer198.top/tags/RViz/"/>
    
  </entry>
  
  <entry>
    <title>微分平坦性怎么判断：从质点、小车到四旋翼无人机</title>
    <link href="https://dreamer198.top/2026/06/09/%E5%BE%AE%E5%88%86%E5%B9%B3%E5%9D%A6%E6%80%A7%E4%B8%8E%E5%9B%9B%E6%97%8B%E7%BF%BC%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/"/>
    <id>https://dreamer198.top/2026/06/09/%E5%BE%AE%E5%88%86%E5%B9%B3%E5%9D%A6%E6%80%A7%E4%B8%8E%E5%9B%9B%E6%97%8B%E7%BF%BC%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/</id>
    <published>2026-06-09T14:30:00.000Z</published>
    <updated>2026-08-06T14:32:05.397Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>在学习无人机轨迹规划时，经常会看到一句话：<strong>四旋翼无人机是微分平坦系统</strong>。</p><p>这句话听起来很理论，但它对工程实现非常关键。因为很多规划器并不会直接优化四个电机转速，也不会直接优化滚转角、俯仰角和力矩，而是优先优化三维空间中的位置轨迹。这样做背后的原因，就是四旋翼的状态和控制输入可以由位置、偏航角及其有限阶导数恢复出来。</p><p>这篇文章先整理微分平坦性的判断标准，再通过质点、小车和四旋翼三个例子说明它到底是什么意思，最后结合 EGO-Planner、MINCO、minimum-snap 这类轨迹规划方法理解它的实际意义。</p><span id="more"></span><h2 id="一、微分平坦性到底在判断什么？">一、微分平坦性到底在判断什么？</h2><p>考虑一个一般的动力系统：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>u</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\dot{x}=f(x,u)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">u</span><span class="mclose">)</span></span></span></span></span></p><p>其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 是系统状态，例如位置、速度、姿态和角速度；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow><annotation encoding="application/x-tex">u</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span></span></span></span> 是控制输入，例如推力、力矩或加速度指令。</li></ul><p>如果系统有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> 个独立控制输入，要证明它具有微分平坦性，通常需要找到 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> 个相互独立的输出：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>y</mi><mo>=</mo><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>y</mi><mn>1</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>y</mi><mn>2</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mo lspace="0em" rspace="0em">⋯</mo></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>y</mi><mi>m</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">y =\begin{bmatrix}y_1 &amp; y_2 &amp; \cdots &amp; y_m\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="minner">⋯</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">m</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>使得系统的所有状态和控制输入都能表示成：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>x</mi><mo>=</mo><mi mathvariant="normal">Φ</mi><mo stretchy="false">(</mo><mi>y</mi><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>¨</mo></mover><mo separator="true">,</mo><mo>⋯</mo><mtext> </mtext><mo separator="true">,</mo><msup><mi>y</mi><mrow><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">x=\Phi(y,\dot{y},\ddot{y},\cdots,y^{(r)})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.188em;vertical-align:-0.25em;"></span><span class="mord">Φ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight" style="margin-right:0.0278em;">r</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>u</mi><mo>=</mo><mi mathvariant="normal">Ψ</mi><mo stretchy="false">(</mo><mi>y</mi><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>¨</mo></mover><mo separator="true">,</mo><mo>⋯</mo><mtext> </mtext><mo separator="true">,</mo><msup><mi>y</mi><mrow><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">u=\Psi(y,\dot{y},\ddot{y},\cdots,y^{(s)})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.188em;vertical-align:-0.25em;"></span><span class="mord">Ψ</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mathnormal mtight">s</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow><annotation encoding="application/x-tex">r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">r</span></span></span></span> 和 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow><annotation encoding="application/x-tex">s</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">s</span></span></span></span> 是有限整数。</p><p>这组输出 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span> 就叫做<strong>平坦输出</strong>。微分平坦理论的核心可以概括成一句话：</p><blockquote><p>如果能用一组平坦输出及其有限阶导数参数化系统的全部状态和控制输入，那么这个系统就是微分平坦系统。</p></blockquote><p>注意这里的关键不是“系统是不是线性的”，也不是“系统是不是完全驱动的”，而是能不能找到这样一组平坦输出。</p><h2 id="二、什么叫由平坦输出直接恢复？">二、什么叫由平坦输出直接恢复？</h2><p>所谓“直接恢复”，指的是只需要做有限次：</p><ul><li>代数运算；</li><li>三角函数运算；</li><li>求导；</li><li>代入动力学方程。</li></ul><p>不应该再需要：</p><ul><li>对未知变量积分；</li><li>求解新的微分方程；</li><li>依赖系统过去的完整运动历史。</li></ul><p>举个反例，如果选择某个变量 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span> 作为候选平坦输出，但恢复状态时出现：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>x</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mi>x</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mo>+</mo><msubsup><mo>∫</mo><mn>0</mn><mi>t</mi></msubsup><mi>y</mi><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>d</mi><mi>τ</mi></mrow><annotation encoding="application/x-tex">x(t)=x(0)+\int_0^t y(\tau)\,d\tau</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord">0</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:2.4554em;vertical-align:-0.9119em;"></span><span class="mop"><span class="mop op-symbol large-op" style="margin-right:0.4445em;position:relative;top:-0.0011em;">∫</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5435em;"><span style="top:-1.7881em;margin-left:-0.4445em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.8129em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">t</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9119em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span></span></span></span></span></p><p>那么仅靠当前时刻的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo separator="true">,</mo><mover accent="true"><mi>y</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">y,\dot{y},\ddot{y}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span> 等有限阶导数，并不能确定 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">x(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>。还必须知道初始值和历史积分。因此，这样的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span> 通常不能作为平坦输出。</p><p>这一区别很重要。微分平坦性强调的是<strong>当前轨迹点附近的有限阶导数信息足够恢复系统状态和输入</strong>，而不是“只要知道整条历史轨迹就能算出来”。</p><h2 id="三、最简单的例子：一维质点">三、最简单的例子：一维质点</h2><p>考虑一个一维质点模型：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo>=</mo><mi>v</mi></mrow><annotation encoding="application/x-tex">\dot{x}=v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>v</mi><mo>˙</mo></mover><mo>=</mo><mi>u</mi></mrow><annotation encoding="application/x-tex">\dot{v}=u</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span></span></span></span></span></p><p>其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> 是位置；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span></span></span></span> 是速度；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow><annotation encoding="application/x-tex">u</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span></span></span></span> 是加速度控制输入。</li></ul><p>选择位置作为平坦输出：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>y</mi><mo>=</mo><mi>x</mi></mrow><annotation encoding="application/x-tex">y=x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span></span></p><p>那么：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>x</mi><mo>=</mo><mi>y</mi></mrow><annotation encoding="application/x-tex">x=y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>v</mi><mo>=</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">v=\dot{y}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>u</mi><mo>=</mo><mover accent="true"><mi>y</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">u=\ddot{y}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></span></p><p>只要规划一条足够光滑的位置曲线 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>，就能直接得到速度和控制输入。因此，这个系统是微分平坦的。</p><p>这也解释了为什么很多轨迹规划算法喜欢用多项式、B 样条或 MINCO 这类参数化曲线描述位置轨迹：位置曲线一旦确定，速度、加速度以及更高阶导数都可以直接算出来。</p><h2 id="四、非完整约束系统也可能微分平坦">四、非完整约束系统也可能微分平坦</h2><p>微分平坦性并不要求系统能够直接向任意方向运动。</p><p>例如一辆非完整约束小车只能前进、后退和转向，不能像全向轮机器人一样横向平移。它的简化模型可以写成：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo>=</mo><mi>v</mi><mi>cos</mi><mo>⁡</mo><mi>θ</mi></mrow><annotation encoding="application/x-tex">\dot{x}=v\cos\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">cos</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo>=</mo><mi>v</mi><mi>sin</mi><mo>⁡</mo><mi>θ</mi></mrow><annotation encoding="application/x-tex">\dot{y}=v\sin\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mop">sin</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>θ</mi><mo>˙</mo></mover><mo>=</mo><mi>ω</mi></mrow><annotation encoding="application/x-tex">\dot{\theta}=\omega</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9313em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span></span></span></span></p><p>其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo separator="true">,</mo><mi>y</mi></mrow><annotation encoding="application/x-tex">x,y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span> 是车辆位置；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow><annotation encoding="application/x-tex">\theta</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span></span></span></span> 是车头朝向；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span></span></span></span> 是前进速度；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi></mrow><annotation encoding="application/x-tex">\omega</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span></span></span> 是转向角速度。</li></ul><p>虽然车辆不能直接横移，但可以选择平面位置作为平坦输出：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>y</mi><mi>f</mi></msub><mo>=</mo><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>x</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>y</mi></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">y_f =\begin{bmatrix}x &amp; y\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1076em;">f</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>由位置轨迹的一阶导数可以恢复车头朝向：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>θ</mi><mo>=</mo><mrow><mi mathvariant="normal">a</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">n</mi><mn>2</mn></mrow><mo stretchy="false">(</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo separator="true">,</mo><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta=\mathrm{atan2}(\dot{y},\dot{x})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathrm">atan2</span></span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>速度为：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>v</mi><mo>=</mo><msqrt><mrow><msup><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mn>2</mn></msup><mo>+</mo><msup><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mn>2</mn></msup></mrow></msqrt></mrow><annotation encoding="application/x-tex">v=\sqrt{\dot{x}^2+\dot{y}^2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.24em;vertical-align:-0.2333em;"></span><span class="mord sqrt"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0067em;"><span class="svg-align" style="top:-3.2em;"><span class="pstrut" style="height:3.2em;"></span><span class="mord" style="padding-left:1em;"><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-2.9667em;"><span class="pstrut" style="height:3.2em;"></span><span class="hide-tail" style="min-width:1.02em;height:1.28em;"><svg xmlns="http://www.w3.org/2000/svg" width="400em" height="1.28em" viewBox="0 0 400000 1296" preserveAspectRatio="xMinYMin slice"><path d="M263,681c0.7,0,18,39.7,52,119c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120c340,-704.7,510.7,-1060.3,512,-1067l0 -0c4.7,-7.3,11,-11,19,-11H40000v40H1012.3s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60zM1001 80h400000v40h-400000z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2333em;"><span></span></span></span></span></span></span></span></span></span></p><p>角速度可以由轨迹的一阶、二阶导数恢复：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>ω</mi><mo>=</mo><mfrac><mrow><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mover accent="true"><mi>y</mi><mo>¨</mo></mover><mo>−</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mover accent="true"><mi>x</mi><mo>¨</mo></mover></mrow><mrow><msup><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mn>2</mn></msup><mo>+</mo><msup><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mn>2</mn></msup></mrow></mfrac></mrow><annotation encoding="application/x-tex">\omega=\frac{\dot{x}\ddot{y}-\dot{y}\ddot{x}}{\dot{x}^2+\dot{y}^2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2253em;vertical-align:-0.8804em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3449em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.7401em;"><span style="top:-2.989em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1944em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2222em;"><span class="mord">¨</span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>所以，只要给出车辆在平面内的运动轨迹，就可以反推出车头方向和控制输入。</p><p>这个例子说明：<strong>系统有运动约束，甚至是欠驱动系统，并不代表它一定不是微分平坦系统。</strong></p><h2 id="五、四旋翼为什么是微分平坦系统？">五、四旋翼为什么是微分平坦系统？</h2><p>理想四旋翼模型通常可以选择：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>y</mi><mi>f</mi></msub><mo>=</mo><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>x</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>y</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>z</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>ψ</mi></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">y_f =\begin{bmatrix}x &amp; y &amp; z &amp; \psi\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1076em;">f</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>作为平坦输出，其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>z</mi></mrow><annotation encoding="application/x-tex">x,y,z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 是无人机质心位置；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ψ</mi></mrow><annotation encoding="application/x-tex">\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span> 是偏航角，也可以理解为机头朝向。</li></ul><p>四旋翼看起来很复杂，因为它包含位置、速度、姿态、角速度、总推力、力矩和四个电机转速。但在常见模型下，这些量都可以通过位置、偏航角及其有限阶导数恢复出来。</p><h3 id="1-四旋翼只有四个独立控制输入">1. 四旋翼只有四个独立控制输入</h3><p>四旋翼有四个电机。通过调节四个旋翼转速，可以形成：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>u</mi><mo>=</mo><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>f</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>x</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>y</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>z</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">u=\begin{bmatrix}f &amp; \tau_x &amp; \tau_y &amp; \tau_z\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow><annotation encoding="application/x-tex">f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span></span></span></span> 是总推力；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>τ</mi><mi>x</mi></msub></mrow><annotation encoding="application/x-tex">\tau_x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 是滚转力矩；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>τ</mi><mi>y</mi></msub></mrow><annotation encoding="application/x-tex">\tau_y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span> 是俯仰力矩；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>τ</mi><mi>z</mi></msub></mrow><annotation encoding="application/x-tex">\tau_z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 是偏航力矩。</li></ul><p>也就是说，虽然四旋翼在三维空间中具有六个自由度：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>z</mi><mo separator="true">,</mo><mi>ϕ</mi><mo separator="true">,</mo><mi>θ</mi><mo separator="true">,</mo><mi>ψ</mi></mrow><annotation encoding="application/x-tex">x,y,z,\phi,\theta,\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">ϕ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span></span></p><p>但它只有四个独立控制输入，是典型的欠驱动系统。</p><p>微分平坦性的特殊之处就在于：即使四旋翼是欠驱动系统，仍然可以用四个平坦输出描述完整运动。</p><h3 id="2-由位置轨迹恢复推力方向">2. 由位置轨迹恢复推力方向</h3><p>为了简化表达，假设：</p><ul><li>世界坐标系的竖直向上方向为 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>e</mi><mn>3</mn></msub><mo>=</mo><mo stretchy="false">[</mo><mn>0</mn><mo separator="true">,</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><msup><mo stretchy="false">]</mo><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">e_3=[0,0,1]^T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0913em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">1</span><span class="mclose"><span class="mclose">]</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span>；</li><li>重力方向向下；</li><li>无人机总推力沿机体坐标系的 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴方向；</li><li>暂时忽略空气阻力和电机动态。</li></ul><p>四旋翼的平移动力学方程可以写成：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>m</mi><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>=</mo><mo>−</mo><mi>m</mi><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo>+</mo><mi>f</mi><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">m\ddot{p}=-mg e_3+f b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">m</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.1944em;"></span><span class="mord">−</span><span class="mord mathnormal">m</span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>其中：</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>=</mo><mo stretchy="false">[</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>z</mi><msup><mo stretchy="false">]</mo><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">p=[x,y,z]^T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.0913em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mclose"><span class="mclose">]</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8413em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span> 是无人机位置；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> 是无人机质量；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow><annotation encoding="application/x-tex">f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span></span></span></span> 是总推力；</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 是机体 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴在世界坐标系下的方向。</li></ul><p>整理后得到：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><msub><mi>b</mi><mn>3</mn></msub><mo>=</mo><mi>m</mi><mo stretchy="false">(</mo><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f b_3=m(\ddot{p}+g e_3)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">m</span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>这条公式非常关键。只要给定位置轨迹 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">p</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>，就可以计算加速度 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\ddot{p}(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>，进而得到总推力大小：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo>=</mo><mi>m</mi><mrow><mo fence="true">∥</mo><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo fence="true">∥</mo></mrow></mrow><annotation encoding="application/x-tex">f=m\left\|\ddot{p}+g e_3\right\|</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">∥</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">∥</span></span></span></span></span></span></p><p>以及无人机机体 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴方向：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub><mo>=</mo><mfrac><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub></mrow><mrow><mo fence="true">∥</mo><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo fence="true">∥</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">b_3=\frac{\ddot{p}+g e_3}{\left\|\ddot{p}+g e_3\right\|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.2809em;vertical-align:-0.936em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3449em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="minner"><span class="mopen delimcenter" style="top:0em;">∥</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">∥</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p>因此，位置轨迹的二阶导数决定了无人机需要朝哪个方向倾斜，以及需要产生多大的总推力。</p><p>几个直观例子：</p><ul><li>如果无人机悬停，则 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\ddot{p}=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>，机体 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴竖直向上；</li><li>如果无人机需要向前加速，就必须向前倾斜；</li><li>如果无人机需要快速爬升，总推力必须大于重力。</li></ul><h3 id="3-为什么还需要偏航角？">3. 为什么还需要偏航角？</h3><p>由位置加速度只能确定机体 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴方向 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>，也就是确定无人机应该怎样倾斜。但是，无人机仍然可以绕自身 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span> 轴旋转。</p><p>可以想象一架正在悬停的无人机：它保持高度不变，但机头仍然可以朝东、朝西或缓慢转圈。</p><p>因此，仅使用 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>z</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">x(t),y(t),z(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span> 还不足以唯一确定完整姿态，还需要补充偏航角 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ψ</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\psi(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>。</p><p>先根据偏航角定义一个期望水平朝向：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>b</mi><mrow><mn>1</mn><mi>c</mi></mrow></msub><mo>=</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>cos</mi><mo>⁡</mo><mi>ψ</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>sin</mi><mo>⁡</mo><mi>ψ</mi></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">b_{1c}=\begin{bmatrix}\cos\psi \\\sin\psi \\0\end{bmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mord mathnormal mtight">c</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:3.6em;vertical-align:-1.55em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.05em;"><span class="pstrut" style="height:5.6em;"></span><span style="width:0.667em;height:3.6em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M403 1759 V84 H666 V0 H319 V1759 v0 v1759 v84 h347 v-84H403z M403 1759 V0 H319 V1759 v0 v1759 v84 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mop">cos</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mop">sin</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span><span style="top:-1.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.05em;"><span style="top:-4.05em;"><span class="pstrut" style="height:5.6em;"></span><span style="width:0.667em;height:3.6em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="3.6em" viewBox="0 0 667 3600"><path d="M347 1759 V0 H0 V84 H263 V1759 v0 v1759 H0 v84 H347zM347 1759 V0 H263 V1759 v0 v1759 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.55em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p>再结合 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 构造机体坐标系的另外两个轴：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>b</mi><mn>2</mn></msub><mo>=</mo><mfrac><mrow><msub><mi>b</mi><mn>3</mn></msub><mo>×</mo><msub><mi>b</mi><mrow><mn>1</mn><mi>c</mi></mrow></msub></mrow><mrow><mo fence="true">∥</mo><msub><mi>b</mi><mn>3</mn></msub><mo>×</mo><msub><mi>b</mi><mrow><mn>1</mn><mi>c</mi></mrow></msub><mo fence="true">∥</mo></mrow></mfrac></mrow><annotation encoding="application/x-tex">b_2=\frac{b_3\times b_{1c}}{\left\|b_3\times b_{1c}\right\|}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.3074em;vertical-align:-0.936em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="minner"><span class="mopen delimcenter" style="top:0em;">∥</span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mord mathnormal mtight">c</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">∥</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mord mathnormal mtight">c</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.936em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>b</mi><mn>1</mn></msub><mo>=</mo><msub><mi>b</mi><mn>2</mn></msub><mo>×</mo><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_1=b_2\times b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>于是姿态矩阵可以写成：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>R</mi><mo>=</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mn>1</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mn>2</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mn>3</mn></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">R=\begin{bmatrix}b_1 &amp; b_2 &amp; b_3\end{bmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.2em;vertical-align:-0.35em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span></span></span></span></span></p><p>也就是说：</p><blockquote><p>位置加速度决定无人机如何倾斜，偏航角决定机头朝向。二者结合后，可以恢复完整姿态。</p></blockquote><h3 id="4-更高阶导数恢复角速度和力矩">4. 更高阶导数恢复角速度和力矩</h3><p>到目前为止，根据平坦输出 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>z</mi><mo separator="true">,</mo><mi>ψ</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[x,y,z,\psi]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span><span class="mclose">]</span></span></span></span> 以及位置的二阶导数，已经得到了：</p><ul><li>位置 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span></span></span></span>；</li><li>速度 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">\dot{p}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span>；</li><li>姿态 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow><annotation encoding="application/x-tex">R</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span></span></span></span>；</li><li>总推力 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow><annotation encoding="application/x-tex">f</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span></span></span></span>。</li></ul><p>但无人机控制还需要角速度和力矩。</p><p>姿态矩阵与角速度之间满足：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>R</mi><mo>˙</mo></mover><mo>=</mo><mi>R</mi><mover accent="true"><mi>ω</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\dot{R}=R\hat{\omega}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9202em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9202em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span></span><span style="top:-3.2523em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.25em;"><span class="mord">^</span></span></span></span></span></span></span></span></span></span></span></p><p>因此：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>ω</mi><mo>^</mo></mover><mo>=</mo><msup><mi>R</mi><mi>T</mi></msup><mover accent="true"><mi>R</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\omega}=R^T\dot{R}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.25em;"><span class="mord">^</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.9202em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0077em;">R</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9202em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span></span><span style="top:-3.2523em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span></span></span></span></span></span></span></span></p><p>其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi></mrow><annotation encoding="application/x-tex">\omega</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span></span></span> 是机体角速度，<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>ω</mi><mo>^</mo></mover></mrow><annotation encoding="application/x-tex">\hat{\omega}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.25em;"><span class="mord">^</span></span></span></span></span></span></span></span></span></span> 是对应的反对称矩阵。</p><p>由于姿态 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow><annotation encoding="application/x-tex">R</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0077em;">R</span></span></span></span> 是由 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">\ddot{p}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span> 和 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ψ</mi></mrow><annotation encoding="application/x-tex">\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span> 构造出来的，所以对姿态继续求导后，角速度可以由位置三阶导数和偏航角一阶导数恢复：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></msup><mo separator="true">,</mo><mover accent="true"><mi>ψ</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">p^{(3)},\dot{\psi}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1324em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">3</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0278em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></span></p><p>位置的三阶导数称为 jerk。</p><p>继续求导可以得到角加速度 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>ω</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">\dot{\omega}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1389em;"><span class="mord">˙</span></span></span></span></span></span></span></span></span></span>，它与位置四阶导数和偏航角二阶导数有关：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mn>4</mn><mo stretchy="false">)</mo></mrow></msup><mo separator="true">,</mo><mover accent="true"><mi>ψ</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">p^{(4)},\ddot{\psi}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1324em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">4</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1389em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></span></p><p>位置的四阶导数称为 snap。</p><p>最后，根据转动动力学方程：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>τ</mi><mo>=</mo><mi>J</mi><mover accent="true"><mi>ω</mi><mo>˙</mo></mover><mo>+</mo><mi>ω</mi><mo>×</mo><mo stretchy="false">(</mo><mi>J</mi><mi>ω</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\tau=J\dot{\omega}+\omega\times(J\omega)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.7667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1389em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span><span class="mord mathnormal" style="margin-right:0.0359em;">ω</span><span class="mclose">)</span></span></span></span></span></p><p>就可以计算出控制力矩：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>τ</mi><mo>=</mo><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>x</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>y</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>z</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">\tau=\begin{bmatrix}\tau_x &amp; \tau_y &amp; \tau_z\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>其中 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow><annotation encoding="application/x-tex">J</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span></span></span></span> 是无人机的转动惯量矩阵。</p><h2 id="六、四旋翼平坦输出的恢复链条">六、四旋翼平坦输出的恢复链条</h2><p>从四个平坦输出出发，恢复关系可以概括为：</p><table><thead><tr><th>平坦输出及其导数</th><th>可以恢复的物理量</th></tr></thead><tbody><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span></span></span></span></td><td>位置</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">\dot{p}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></td><td>速度</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">\ddot{p}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></td><td>推力大小、推力方向、滚转和俯仰</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ψ</mi></mrow><annotation encoding="application/x-tex">\psi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span></td><td>偏航方向、完整姿态</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></msup><mo separator="true">,</mo><mover accent="true"><mi>ψ</mi><mo>˙</mo></mover></mrow><annotation encoding="application/x-tex">p^{(3)},\dot{\psi}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1257em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">3</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0278em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></td><td>角速度</td></tr><tr><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mn>4</mn><mo stretchy="false">)</mo></mrow></msup><mo separator="true">,</mo><mover accent="true"><mi>ψ</mi><mo>¨</mo></mover></mrow><annotation encoding="application/x-tex">p^{(4)},\ddot{\psi}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1257em;vertical-align:-0.1944em;"></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">4</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9313em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1389em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span></span></span></span></td><td>角加速度、控制力矩</td></tr></tbody></table><p>再通过四旋翼的混控矩阵，可以把总推力和三个力矩转换成四个电机的目标推力：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>f</mi></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>x</mi></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>y</mi></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>τ</mi><mi>z</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>A</mi><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>f</mi><mn>1</mn></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>f</mi><mn>2</mn></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>f</mi><mn>3</mn></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>f</mi><mn>4</mn></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{aligned}\begin{bmatrix}f \\\tau_x \\\tau_y \\\tau_z\end{bmatrix}&amp;=A\begin{bmatrix}f_1 \\f_2 \\f_3 \\f_4\end{bmatrix}\end{aligned}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:4.8em;vertical-align:-2.15em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:4.65em;"></span><span class="mord"><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:6.8em;"></span><span style="width:0.667em;height:4.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="4.8em" viewBox="0 0 667 4800"><path d="M403 1759 V84 H666 V0 H319 V1759 v1200 v1759 v84 h347 v-84H403z M403 1759 V0 H319 V1759 v1200 v1759 v84 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1132em;">τ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.1132em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:6.8em;"></span><span style="width:0.667em;height:4.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="4.8em" viewBox="0 0 667 4800"><path d="M347 1759 V0 H0 V84 H263 V1759 v1200 v1759 H0 v84 H347zM347 1759 V0 H263 V1759 v1200 v1759 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:4.65em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:6.8em;"></span><span style="width:0.667em;height:4.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="4.8em" viewBox="0 0 667 4800"><path d="M403 1759 V84 H666 V0 H319 V1759 v1200 v1759 v84 h347 v-84H403z M403 1759 V0 H319 V1759 v1200 v1759 v84 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.1076em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">4</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.65em;"><span style="top:-4.65em;"><span class="pstrut" style="height:6.8em;"></span><span style="width:0.667em;height:4.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.667em" height="4.8em" viewBox="0 0 667 4800"><path d="M347 1759 V0 H0 V84 H263 V1759 v1200 v1759 H0 v84 H347zM347 1759 V0 H263 V1759 v1200 v1759 h84z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.15em;"><span></span></span></span></span></span></span></span></span></span></span></span></p><p>只要混控矩阵 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow><annotation encoding="application/x-tex">A</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span></span></span></span> 可逆，就可以计算每个旋翼的推力。</p><p>完整链条可以写成：</p><figure class="highlight text"><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">[x, y, z, psi]</span><br><span class="line">    -&gt; 位置、速度、姿态、角速度</span><br><span class="line">    -&gt; 总推力、力矩</span><br><span class="line">    -&gt; 四个旋翼的推力</span><br></pre></td></tr></table></figure><p>整个过程中只涉及有限次求导和代数运算，不需要重新求解一组微分方程。这正是微分平坦性的定义。</p><h2 id="七、为什么轨迹规划常常优化-snap？">七、为什么轨迹规划常常优化 snap？</h2><p>这也解释了为什么经典四旋翼轨迹生成方法经常使用 <strong>minimum-snap trajectory</strong>，也就是最小化位置轨迹四阶导数的平方积分：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>J</mi><mo>=</mo><msubsup><mo>∫</mo><mn>0</mn><mi>T</mi></msubsup><msup><mrow><mo fence="true">∥</mo><msup><mi>p</mi><mrow><mo stretchy="false">(</mo><mn>4</mn><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo fence="true">∥</mo></mrow><mn>2</mn></msup><mi>d</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">J=\int_0^T \left\|p^{(4)}(t)\right\|^2 dt</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0962em;">J</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.5032em;vertical-align:-0.9119em;"></span><span class="mop"><span class="mop op-symbol large-op" style="margin-right:0.4445em;position:relative;top:-0.0011em;">∫</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.5912em;"><span style="top:-1.7881em;margin-left:-0.4445em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span style="top:-3.8129em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9119em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.15em;"><span style="top:-3.15em;"><span class="pstrut" style="height:3.8em;"></span><span style="width:0.556em;height:1.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.556em" height="1.8em" viewBox="0 0 556 1800"><path d="M145 15 v585 v600 v585 c2.667,10,9.667,15,21,15c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v600 v585 h43zM367 15 v585 v600 v585 c2.667,10,9.667,15,21,15c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v600 v585 h43z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.65em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal">p</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.938em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mopen mtight">(</span><span class="mord mtight">4</span><span class="mclose mtight">)</span></span></span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.15em;"><span style="top:-3.15em;"><span class="pstrut" style="height:3.8em;"></span><span style="width:0.556em;height:1.8em;"><svg xmlns="http://www.w3.org/2000/svg" width="0.556em" height="1.8em" viewBox="0 0 556 1800"><path d="M145 15 v585 v600 v585 c2.667,10,9.667,15,21,15c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v600 v585 h43zM367 15 v585 v600 v585 c2.667,10,9.667,15,21,15c10,0,16.667,-5,20,-15 v-585 v-600 v-585 c-2.667,-10,-9.667,-15,-21,-15c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v600 v585 h43z"/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.65em;"><span></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.354em;"><span style="top:-3.6029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span></span></span></span></span></p><p>因为 snap 与无人机的角加速度和控制力矩密切相关。限制 snap 可以让：</p><ul><li>姿态变化更加平滑；</li><li>控制力矩变化更加平缓；</li><li>电机指令变化更加连续；</li><li>轨迹更容易被真实无人机跟踪。</li></ul><p>Mellinger 和 Kumar 的经典四旋翼轨迹规划工作，就是利用这一性质，在三维位置和偏航角空间中生成可跟踪轨迹。</p><h2 id="八、微分平坦不等于任意轨迹都能飞">八、微分平坦不等于任意轨迹都能飞</h2><p>理论上，只要给定足够光滑的：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">[</mo><mi>x</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>z</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mi>ψ</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[x(t),y(t),z(t),\psi(t)]</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">[</span><span class="mord mathnormal">x</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.044em;">z</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)]</span></span></span></span></span></p><p>就可以计算出对应的状态和控制输入。</p><p>但这并不意味着任意轨迹都能被真实无人机执行。因为计算出来的控制量可能超过硬件能力，例如：</p><ul><li>总推力超过电机上限；</li><li>需要过大的倾斜角；</li><li>角速度过高；</li><li>控制力矩过大；</li><li>电机转速变化过快；</li><li>轨迹本身穿过障碍物。</li></ul><p>因此，轨迹规划仍然需要加入动力学约束和环境约束，例如：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mo fence="true">∥</mo><mover accent="true"><mi>p</mi><mo>˙</mo></mover><mo fence="true">∥</mo></mrow><mo>≤</mo><msub><mi>v</mi><mi>max</mi><mo>⁡</mo></msub></mrow><annotation encoding="application/x-tex">\left\|\dot{p}\right\|\le v_{\max}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">∥</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0556em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">∥</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mo fence="true">∥</mo><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo fence="true">∥</mo></mrow><mo>≤</mo><msub><mi>a</mi><mi>max</mi><mo>⁡</mo></msub></mrow><annotation encoding="application/x-tex">\left\|\ddot{p}\right\|\le a_{\max}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;">∥</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;">∥</span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">a</span><span class="mtight">x</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span></span></p><p>以及推力、姿态角、角速度、走廊安全距离等限制。</p><p>微分平坦性解决的是：</p><blockquote><p>如何从容易规划的位置轨迹恢复完整动力学状态和控制指令。</p></blockquote><p>它并不会自动保证轨迹一定满足硬件约束、控制约束和避障约束。</p><h2 id="九、工程上如何判断一个系统是否微分平坦？">九、工程上如何判断一个系统是否微分平坦？</h2><p>对于一个具体系统，可以按下面的思路判断。</p><h3 id="1-写出明确的动力学模型">1. 写出明确的动力学模型</h3><p>首先需要明确系统模型到底包含哪些状态和输入。例如四旋翼模型中，要说明是否考虑：</p><ul><li>空气阻力；</li><li>电机动态；</li><li>桨叶模型；</li><li>负载；</li><li>机械臂；</li><li>柔性绳索；</li><li>执行器饱和。</li></ul><p>微分平坦性是针对特定数学模型而言的。同一架无人机，如果采用不同复杂程度的动力学模型，结论可能不同。</p><h3 id="2-寻找少量候选输出">2. 寻找少量候选输出</h3><p>通常优先考虑能直接反映运动目标的变量，例如：</p><table><thead><tr><th>系统</th><th>常见候选平坦输出</th></tr></thead><tbody><tr><td>一维质点</td><td>位置</td></tr><tr><td>非完整约束小车</td><td>平面位置</td></tr><tr><td>四旋翼</td><td>三维位置 + 偏航角</td></tr><tr><td>机械臂</td><td>末端位姿或部分关节变量</td></tr></tbody></table><p>平坦输出的数量通常与独立控制输入数量相同。</p><h3 id="3-逐层求导并代入动力学方程">3. 逐层求导并代入动力学方程</h3><p>观察能否从候选输出的导数逐步恢复：</p><ul><li>位置；</li><li>速度；</li><li>姿态；</li><li>角速度；</li><li>推力；</li><li>力矩；</li><li>其他状态和输入。</li></ul><h3 id="4-检查是否引入积分或额外微分方程">4. 检查是否引入积分或额外微分方程</h3><p>如果所有变量都能通过有限次求导和代数计算得到，则系统在该模型下是微分平坦的。</p><p>如果必须依赖积分、初始条件或完整历史轨迹，说明当前候选输出不满足要求。此时可能需要：</p><ul><li>换一组候选平坦输出；</li><li>引入输入的动态扩展；</li><li>或者接受该系统在当前模型下不是微分平坦系统。</li></ul><h2 id="十、需要注意奇异状态">十、需要注意奇异状态</h2><p>很多系统只在一定范围内具有良好的平坦参数化。</p><p>例如小车模型中：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>θ</mi><mo>=</mo><mrow><mi mathvariant="normal">a</mi><mi mathvariant="normal">t</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">n</mi><mn>2</mn></mrow><mo stretchy="false">(</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo separator="true">,</mo><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\theta=\mathrm{atan2}(\dot{y},\dot{x})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0278em;">θ</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathrm">atan2</span></span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>当车辆速度为零时：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>x</mi><mo>˙</mo></mover><mo>=</mo><mover accent="true"><mi>y</mi><mo>˙</mo></mover><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\dot{x}=\dot{y}=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6679em;"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">x</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1111em;"><span class="mord">˙</span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0833em;"><span class="mord">˙</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span></span></p><p>车头朝向无法根据位置轨迹的一阶导数确定，此时就会出现奇异性。</p><p>四旋翼也有类似问题。例如当：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">\ddot{p}+g e_3=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span></span></p><p>时，推力方向 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 无法正常定义；当 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>3</mn></msub></mrow><annotation encoding="application/x-tex">b_3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 与期望水平朝向 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mrow><mn>1</mn><mi>c</mi></mrow></msub></mrow><annotation encoding="application/x-tex">b_{1c}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mord mathnormal mtight">c</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> 发生退化关系时，姿态构造也可能失效。</p><p>所以，更准确的说法通常是：</p><blockquote><p>系统在避开某些奇异状态的局部区域内具有微分平坦性。</p></blockquote><h2 id="十一、对-EGO-Planner-等无人机规划器的意义">十一、对 EGO-Planner 等无人机规划器的意义</h2><p>在 EGO-Planner、Fast-Planner、GCOPTER、MINCO 等无人机轨迹规划框架中，规划器通常不会直接优化：</p><ul><li>四个电机转速；</li><li>每个时刻的控制力矩；</li><li>完整姿态曲线；</li><li>飞控底层的混控输入。</li></ul><p>它们更常见的做法，是优化三维空间中的位置曲线 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">p(t)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">p</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mclose">)</span></span></span></span>，并约束速度、加速度、光滑性和障碍物距离。</p><p>原因就在于四旋翼的微分平坦性。规划器只需要生成一条足够光滑、满足约束的位置轨迹，轨迹跟踪控制器就可以根据期望位置、速度和加速度计算期望推力与姿态。更高性能的控制器还会使用 jerk 和 snap 构造前馈项，从而改善高速飞行时的跟踪效果。</p><p>可以把微分平坦性理解成一座桥：</p><figure class="highlight text"><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><br><span class="line">    -&gt; 平坦输出及其有限阶导数</span><br><span class="line">    -&gt; 复杂的无人机动力学状态和控制输入</span><br></pre></td></tr></table></figure><p>这就是为什么在无人机轨迹规划里，很多方法都重点研究“如何生成一条光滑、无碰撞、满足动力学约束的位置轨迹”，而不是从电机转速层面直接做全量优化。</p><h2 id="总结">总结</h2><p>一个系统具有微分平坦性的本质条件是：</p><blockquote><p>存在一组平坦输出，使系统的全部状态和控制输入都能够由这些输出及其有限阶导数直接恢复，并且不需要积分或额外求解微分方程。</p></blockquote><p>对于四旋翼无人机，常见平坦输出是：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>x</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>y</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>z</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>ψ</mi></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mi>T</mi></msup></mrow><annotation encoding="application/x-tex">\begin{bmatrix}x &amp; y &amp; z &amp; \psi\end{bmatrix}^{T}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.4312em;vertical-align:-0.35em;"></span><span class="minner"><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size1">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.044em;">z</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.85em;"><span style="top:-3.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0359em;">ψ</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.35em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size1">]</span></span></span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:1.0812em;"><span style="top:-3.3029em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span></span></span></span></span></span></span></span></span></span></span></span></span></p><p>其中最关键的物理关系是：</p><p><span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><msub><mi>b</mi><mn>3</mn></msub><mo>=</mo><mi>m</mi><mo stretchy="false">(</mo><mover accent="true"><mi>p</mi><mo>¨</mo></mover><mo>+</mo><mi>g</mi><msub><mi>e</mi><mn>3</mn></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f b_3=m(\ddot{p}+g e_3)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">m</span><span class="mopen">(</span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.6679em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">p</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.1667em;"><span class="mord">¨</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1944em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">g</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></span></p><p>位置加速度决定推力方向和大小，偏航角补足机头朝向，更高阶导数进一步恢复角速度和力矩。</p><p>因此，四旋翼虽然是欠驱动系统，但在常用模型下仍然是微分平坦系统。这一性质正是四旋翼轨迹规划能够主要围绕位置曲线展开的理论基础。</p>]]></content>
    
    
    <summary type="html">前言 在学习无人机轨迹规划时，经常会看到一句话：四旋翼无人机是微分平坦系统。 这句话听起来很理论，但它对工程实现非常关键。因为很多规划器并不会直接优化四个电机转速，也不会直接优化滚转角、俯仰角和力矩，而是优先优化三维空间中的位置轨迹。这样做背后的原因，就是四旋翼的状态和控制输入可以由位置、偏航角及其有限阶导…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="EGO-Planner" scheme="https://dreamer198.top/tags/EGO-Planner/"/>
    
    <category term="微分平坦性" scheme="https://dreamer198.top/tags/%E5%BE%AE%E5%88%86%E5%B9%B3%E5%9D%A6%E6%80%A7/"/>
    
    <category term="轨迹规划" scheme="https://dreamer198.top/tags/%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/"/>
    
    <category term="四旋翼" scheme="https://dreamer198.top/tags/%E5%9B%9B%E6%97%8B%E7%BF%BC/"/>
    
    <category term="控制" scheme="https://dreamer198.top/tags/%E6%8E%A7%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>NavRL 阅读笔记：动态环境下的无人机强化学习局部导航到底做了什么</title>
    <link href="https://dreamer198.top/2026/06/08/NavRL%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
    <id>https://dreamer198.top/2026/06/08/NavRL%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/</id>
    <published>2026-06-08T14:00:00.000Z</published>
    <updated>2026-08-13T08:17:01.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="NavRL-阅读笔记：动态环境下的无人机强化学习局部导航到底做了什么">NavRL 阅读笔记：动态环境下的无人机强化学习局部导航到底做了什么</h2><span id="more"></span><blockquote><p>论文：<strong>NavRL: Learning Safe Flight in Dynamic Environments</strong><br>期刊：<strong>IEEE RA-L 2025</strong><br>作者：<strong>Zhefan Xu, Xinming Han, Haoyu Shen, Hanyu Jin, Kenji Shimada</strong><br>代码：<a href="https://github.com/Zhefan-Xu/NavRL">https://github.com/Zhefan-Xu/NavRL</a></p></blockquote><p>这篇论文最值得看的地方，不是“用了 PPO”这件事，而是作者把一个能落到真机上的动态环境局部导航系统拆成了三层：<strong>可迁移的状态表示、可学习的局部速度策略、可解释的安全屏蔽层</strong>。它不是那种纯 end-to-end 的“图像进、动作出”工作，而是明显更偏工程部署导向。</p><p>对于<strong>面向动态未知环境的无人机强化学习局部导航与真实部署</strong>这一研究方向，这篇论文的相关性非常直接：它已经把“动态障碍感知 + RL 决策 + 安全兜底 + Jetson 真机部署”这条链路走通了一遍。它的长处和短板，也基本对应了后续最容易继续往前推进的切入点。</p><h2 id="一页速览">一页速览</h2><table><thead><tr><th>维度</th><th>结论</th></tr></thead><tbody><tr><td>研究任务</td><td>无人机在动态未知环境中做局部导航，既避开静态障碍，也避开移动障碍，并尽量实现零样本真机迁移。</td></tr><tr><td>方法骨架</td><td><code>状态表示</code> + <code>PPO 速度策略</code> + <code>VO-inspired safety shield</code></td></tr><tr><td>输入表示</td><td>目标相对状态、静态障碍射线距离图、动态障碍对象级状态</td></tr><tr><td>最值得借鉴的点</td><td>不直接吃原始图像，而是先把状态做成更利于 sim-to-real 的几何表示</td></tr><tr><td>最强结果</td><td>在动态和混合场景下碰撞次数明显低于对比方法，Jetson Orin NX 上可实时运行</td></tr><tr><td>最大短板</td><td>对动态障碍感知质量依赖强，安全屏蔽层在密集动态场景可能偏保守</td></tr><tr><td>对相关研究方向的意义</td><td>很适合作为“动态环境 RL 局部导航 + 真机部署”的复现基线与对标对象</td></tr></tbody></table><blockquote><p><strong>一句话判断：</strong> NavRL 不是最花哨的学习导航论文，但很像一个真正能上机的动态局部导航基线。</p></blockquote><h2 id="0-先说结论：这篇论文和相关研究方向有什么关系？">0. 先说结论：这篇论文和相关研究方向有什么关系？</h2><p>这篇论文和上述研究方向是<strong>强相关</strong>的，原因有四个：</p><ol><li>它研究的就是<strong>局部导航</strong>，不是全局路径规划，也不是纯控制跟踪。</li><li>它明确处理了<strong>动态障碍</strong>，而不是只在静态障碍里做强化学习。</li><li>它非常强调<strong>真实部署</strong>，包括 Jetson Orin NX、RealSense D435i、真实行人干扰和零样本 sim-to-real。</li><li>它已经意识到“纯神经网络不够安全”，所以加入了一个 <strong>VO-inspired safety shield</strong>。</li></ol><p>但它也没有把这个方向彻底做完。更准确地说，它提供了一个很好的<strong>可复现基线和问题清单</strong>：</p><ul><li>怎样设计状态表示，才能减少 sim-to-real gap；</li><li>怎样在动态障碍下让 RL 真的学到反应式避障；</li><li>怎样给黑盒策略加一个足够轻量、又能上机跑的安全层；</li><li>真机部署时，瓶颈到底在策略网络，还是在前端感知和安全约束。</li></ul><p>因此，如果后续要围绕这一方向继续做论文创新，这篇文章更像是一个<strong>很实用的起点</strong>，而不是终点。</p><h2 id="1-这篇论文解决什么问题？">1. 这篇论文解决什么问题？</h2><p>这篇论文关注的场景，是<strong>无人机在未知或部分未知的动态环境中进行局部自主导航</strong>：一边向目标点前进，一边避开静态障碍和移动障碍。现有方法里，传统优化/规划方法往往要把系统拆成预测、建图、轨迹优化等多个模块，参数多、调参重，对环境变化也比较敏感；而一些学习方法虽然有更强的适应性，但要么依赖图像直接输入导致 sim-to-real 很难，要么缺少安全机制，不适合直接上真机。作者想解决的核心痛点，其实就一句话：<strong>能不能训练一个强化学习局部导航策略，让它既能躲静态障碍，也能躲动态障碍，还能比较稳地迁移到真实无人机上，并且在策略失误时有一个轻量安全兜底。</strong></p><h2 id="2-方法的核心思路是什么？">2. 方法的核心思路是什么？</h2><h3 id="整体思路">整体思路</h3><p>NavRL 的整体方法并不复杂，但工程组合很有针对性。它先不用原始 RGB 图像直接喂给策略网络，而是把环境整理成两类中间表示：<strong>静态障碍的射线距离表示</strong>和<strong>动态障碍的目标级状态表示</strong>。然后用 PPO 学一个<strong>目标导向的局部速度策略</strong>，输出目标坐标系下的三维速度指令。最后在部署时，不直接相信网络输出，而是用一个基于 <strong>Velocity Obstacle</strong> 思想的安全屏蔽层，对不安全动作做一次线性规划投影。</p><p>换句话说，这篇论文的核心不是“更强的网络”，而是：<strong>先把状态表示做对，再让 RL 学反应式避障，最后用几何安全层兜底。</strong></p><h3 id="流程梳理">流程梳理</h3><p><strong>输入 -&gt; 关键模块 -&gt; 输出</strong></p><p><code>RGB-D + 自身状态 + 目标点</code><br>-&gt; <code>静态障碍建图与射线表示</code><br>-&gt; <code>动态障碍检测/跟踪/测速</code><br>-&gt; <code>状态编码 + PPO 策略网络</code><br>-&gt; <code>VO-inspired safety shield</code><br>-&gt; <code>无人机速度指令</code></p><table><thead><tr><th>环节</th><th>具体做法</th><th>作用</th><th>是否属于本文重点亮点</th></tr></thead><tbody><tr><td>静态障碍建模</td><td>occupancy voxel map + 3D ray casting</td><td>把复杂静态环境压缩成局部可通行性表示</td><td>是</td></tr><tr><td>动态障碍建模</td><td>检测、分类、跟踪、测速</td><td>显式保留动态目标的位置、速度和尺寸</td><td>是</td></tr><tr><td>策略学习</td><td>PPO + actor-critic + Beta 动作分布</td><td>学习目标驱动的局部速度控制</td><td>是</td></tr><tr><td>安全修正</td><td>VO-inspired action projection</td><td>对网络失误进行轻量几何纠偏</td><td>是</td></tr><tr><td>大规模训练</td><td>Isaac Sim 并行训练 1024 架四旋翼</td><td>加快收敛，增强数据多样性</td><td>是</td></tr></tbody></table><h3 id="每个关键模块的作用">每个关键模块的作用</h3><h4 id="1-静态障碍表示">1. 静态障碍表示</h4><p>作者先用深度图在线构建 3D occupancy voxel map，然后不把整张体素地图直接送进网络，而是从无人机位置做 3D ray casting，把各个方向上“离障碍有多远”编码成一个二维矩阵。</p><p>这个模块的作用是：<strong>把复杂的三维静态环境压缩成一个适合策略学习、又比较容易 sim-to-real 的几何表示。</strong></p><p>这部分是有价值的设计，因为它避免了直接依赖图像纹理，也避免了整张体素地图过大、过稀疏的问题。</p><h4 id="2-动态障碍表示">2. 动态障碍表示</h4><p>动态障碍不进 occupancy map，而是单独检测和跟踪。作者用 U-depth detector、DBSCAN detector、YOLO 分类器和 Kalman filter，最终得到每个动态障碍的<strong>相对位置、距离、速度、尺寸</strong>。</p><p>这个模块的作用是：<strong>把“会动的东西”显式建模成目标级对象状态，而不是混进静态地图里。</strong></p><p>这里要注意一个细节：这部分动态检测与跟踪主要继承自作者前一篇工作，并不是 NavRL 这篇 paper 的核心算法创新，但它对真实部署非常重要。</p><h4 id="3-状态编码与策略学习">3. 状态编码与策略学习</h4><p>静态障碍矩阵和动态障碍矩阵分别经过 CNN 提取特征，再和无人机内部状态拼接，送入 actor-critic 结构，用 PPO 训练。</p><p>这个模块的作用是：<strong>让网络学会在目标驱动下输出局部反应速度，而不是显式解轨迹优化问题。</strong></p><p>真正值得注意的创新，不是 PPO 本身，而是：</p><ul><li>状态分成静态/动态两条支路；</li><li>全部都转到 <strong>goal coordinate frame</strong>；</li><li>动作空间是<strong>有边界的速度命令</strong>，不是姿态、电机或离散动作。</li></ul><h4 id="4-安全屏蔽层">4. 安全屏蔽层</h4><p>网络输出一个候选速度 <code>V_rl</code>。如果这个速度不会落入任何 velocity obstacle 区域，就直接执行；如果会引发潜在碰撞，就求解一个小规模线性约束优化，把动作投影到最近的安全区域。</p><p>这个模块的作用是：<strong>在不重做完整规划的前提下，给黑盒 RL 策略加一个可解释、可实时运行的安全补丁。</strong></p><p>这一块是全文最有“部署味”的部分，也是最贴近上述研究方向的地方。</p><h3 id="哪些部分是作者真正的创新？">哪些部分是作者真正的创新？</h3><p>如果说得严格一点，这篇论文的创新更像<strong>系统创新</strong>而不是理论创新：</p><ul><li><p><strong>创新 1：面向 sim-to-real 的状态表示设计</strong><br>不用原始图像，改用静态射线图 + 动态目标状态。</p></li><li><p><strong>创新 2：面向局部导航的动作设计</strong><br>直接输出目标坐标系下的三维速度，并用 Beta 分布处理有界连续动作。</p></li><li><p><strong>创新 3：VO-inspired safety shield</strong><br>在 RL 输出之后做几何安全投影，降低黑盒策略失误带来的碰撞风险。</p></li><li><p><strong>创新 4：大规模并行训练 + 真机验证链路</strong><br>用 Isaac Sim 同时训练上千架四旋翼，并完成 zero-shot real deployment。</p></li></ul><p>反过来说，<strong>PPO、CNN 编码器、Kalman 跟踪</strong>本身都不是新东西。</p><h2 id="3-需要重点理解哪些公式？">3. 需要重点理解哪些公式？</h2><p>这篇论文的公式不算多，但有几组必须抓住。重点不是会不会推导，而是要明白这些公式在系统里各自干什么。</p><table><thead><tr><th>公式 / 对象</th><th>在系统里的作用</th><th>真正需要理解的点</th></tr></thead><tbody><tr><td>MDP 目标公式 <code>(1)</code></td><td>定义 RL 的总体优化目标</td><td>这是逐时刻局部决策，不是整条轨迹优化</td></tr><tr><td>内部状态 <code>(2)</code></td><td>告诉策略“目标在哪、自己怎么动”</td><td>用 goal frame 降低坐标系依赖，提升迁移性</td></tr><tr><td>动态障碍状态 <code>(3)(4)</code></td><td>显式描述最近的动态目标</td><td>关键不是看见障碍，而是知道障碍怎么动</td></tr><tr><td>静态障碍状态 <code>(5)</code></td><td>用射线长度编码局部几何</td><td>把地图变成局部可通行性表示</td></tr><tr><td>动作公式 <code>(6)</code></td><td>输出有界连续速度</td><td>动作是速度而不是低层控制，更适合 sim-to-real</td></tr><tr><td>奖励公式 <code>(7)-(12)</code></td><td>平衡前进、安全、平滑、高度约束</td><td>防止只会躲障碍、不往前走，或靠乱飞取巧</td></tr><tr><td>安全屏蔽公式 <code>(13)</code></td><td>把危险动作投影到安全区域</td><td>尽量少改 RL 动作，但不能让它撞上去</td></tr></tbody></table><h3 id="3-1-强化学习目标">3.1 强化学习目标</h3><p>论文把问题写成一个标准 MDP，目标是最大化累计折扣回报：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">π* = arg max E[ Σ γ^t R(s_t, a_t) ]</span><br></pre></td></tr></table></figure><p>这没有太多新意，但它说明作者把这个问题当成<strong>反应式局部决策</strong>来学，而不是轨迹优化问题。也就是说，网络不负责生成整条轨迹，而是每个时刻都根据当前感知状态输出一个速度动作。</p><h3 id="3-2-输入状态如何定义">3.2 输入状态如何定义</h3><p>作者设计的状态由三部分组成。</p><h4 id="1-无人机内部状态">1. 无人机内部状态</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S_int = [目标方向单位向量, 到目标距离, 当前速度]</span><br></pre></td></tr></table></figure><p>更具体地说，是把“机器人到目标点的相对位置”拆成<strong>方向 + 距离</strong>，再加上当前速度。这样做的实际意义是：网络不用自己从大坐标差值里再去学“方向归一化”这件事，优化会更稳定一些。</p><p>而且这些量都放在 <strong>goal coordinate frame</strong> 下。这个设计很关键，因为它弱化了绝对世界坐标的影响，让策略更像“朝目标前进时如何避障”的通用技能，更利于迁移。</p><h4 id="2-动态障碍状态">2. 动态障碍状态</h4><figure class="highlight text"><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">D_i = [相对方向单位向量, 相对距离, 障碍速度, 障碍尺寸]</span><br><span class="line">S_dyn = [D_1, D_2, ..., D_Nd]</span><br></pre></td></tr></table></figure><p>这意味着动态障碍不是一堆像素，而是一组带运动属性的对象。实际含义很直接：<strong>策略不仅知道前面有东西，还知道它往哪儿动、动多快、占多大。</strong></p><p>对这一研究方向来说，这个设计很值得借鉴，因为动态局部导航的关键往往不是“看到障碍”，而是“知道它在怎么动”。</p><h4 id="3-静态障碍状态">3. 静态障碍状态</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">S_stat = [R_θ0, R_θ1, ..., R_θNv]</span><br></pre></td></tr></table></figure><p>这里每个 <code>R_θi</code> 都是一组射线长度，表示不同方向上与静态障碍的距离。它本质上是一个<strong>稠密的局部几何可通行性描述</strong>。</p><p>这个表示的实际意义是：网络拿到的不是地图本身，而是“周围哪些方向更空旷”。这对局部反应式策略很友好。</p><h3 id="3-3-输出动作是什么">3.3 输出动作是什么</h3><p>作者不是直接让网络输出真实速度，而是先输出一个归一化速度，再映射到最大速度范围内：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">V_ctrl^G = v_lim * (2 * V_hat_ctrl^G - 1),    V_hat_ctrl^G ∈ [0, 1]</span><br></pre></td></tr></table></figure><p>这里最关键的不是这个线性映射，而是背后的动作设计思想：</p><ul><li>输出的是<strong>速度</strong>，不是低层控制量；</li><li>速度有明确边界；</li><li>策略是在目标坐标系下输出速度；</li><li>网络参数化的是 <strong>Beta distribution</strong>，适合有界连续动作空间。</li></ul><p>实际含义就是：作者希望动作既好学，又容易跨平台迁移，也方便人理解和监督。</p><h3 id="3-4-奖励函数在鼓励什么">3.4 奖励函数在鼓励什么</h3><p>总奖励写成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r = λ1 r_vel + λ2 r_ss + λ3 r_ds + λ4 r_smooth + λ5 r_height</span><br></pre></td></tr></table></figure><p>看起来简单，但每一项都对应一个很现实的飞行偏好。</p><h4 id="1-速度奖励-r-vel">1. 速度奖励 <code>r_vel</code></h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r_vel = goal方向单位向量 · 当前速度</span><br></pre></td></tr></table></figure><p>它鼓励无人机沿着目标方向高速前进。通俗讲，就是“别磨蹭，往目标方向走”。</p><h4 id="2-静态安全奖励-r-ss">2. 静态安全奖励 <code>r_ss</code></h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r_ss = 平均 log(射线距离)</span><br></pre></td></tr></table></figure><p>离静态障碍越远，奖励越大。这里用 log 而不是线性距离，意味着作者希望“离得很近时惩罚更敏感，离得已经够远时边际收益变小”。</p><h4 id="3-动态安全奖励-r-ds">3. 动态安全奖励 <code>r_ds</code></h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r_ds = 平均 log(与动态障碍的距离)</span><br></pre></td></tr></table></figure><p>它和静态安全奖励类似，但针对会动的障碍。实际含义是：策略会倾向于和动态目标保持间距。</p><h4 id="4-平滑奖励-r-smooth">4. 平滑奖励 <code>r_smooth</code></h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">r_smooth = - ||v_t - v_(t-1)||</span><br></pre></td></tr></table></figure><p>这在真实无人机上很重要。没有这项，RL 很容易学出抖动控制，看起来会“能躲，但飞得很难受”。</p><h4 id="5-高度奖励-r-height">5. 高度奖励 <code>r_height</code></h4><p>作者额外惩罚无人机过度上升，防止它通过“往高处飞”来投机避障。这个设计虽然简单，但很实用，因为很多 RL 策略确实会钻这种空子。</p><h3 id="3-5-安全约束在做什么">3.5 安全约束在做什么</h3><p>部署阶段最重要的是下面这个投影问题：</p><figure class="highlight text"><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">min ||V_safe - V_rl||</span><br><span class="line">s.t.  V_safe 落在各个 velocity obstacle 外侧</span><br><span class="line">      V_min &lt;= V_safe &lt;= V_max</span><br></pre></td></tr></table></figure><p>它的实际含义很清楚：<strong>尽量少改网络动作，但如果网络动作会撞，就把它推到最近的安全速度上。</strong></p><p>这里最值得理解的不是公式本身，而是它代表的安全哲学：</p><ul><li>RL 负责“好用”；</li><li>safety shield 负责“别出大事”；</li><li>两者不是互斥，而是串联。</li></ul><p>这很适合真实部署，因为真机系统里往往不敢把最后控制权完全交给神经网络。</p><h2 id="4-实验说明了什么？">4. 实验说明了什么？</h2><table><thead><tr><th>维度</th><th>内容</th></tr></thead><tbody><tr><td>训练平台</td><td>NVIDIA Isaac Sim，RTX 4090，约 10 小时，并行训练 1024 架四旋翼</td></tr><tr><td>仿真测试</td><td>Gazebo，包含走廊场景以及高密度静态 / 动态 / 混合障碍环境</td></tr><tr><td>真机平台</td><td>自定义四旋翼 + Jetson Orin NX + Intel RealSense D435i + LIO</td></tr><tr><td>对比方法</td><td>EGO-Planner、ViGO、NavRL without safety shield</td></tr><tr><td>核心指标</td><td>训练成功率、训练回报、平均碰撞次数、各模块时延</td></tr></tbody></table><h3 id="使用了哪些场景、平台和硬件？">使用了哪些场景、平台和硬件？</h3><p>作者的实验分三层：</p><ol><li><p><strong>训练</strong>：NVIDIA Isaac Sim<br>使用 RTX 4090，约训练 10 小时，同时并行训练 <strong>1024 架</strong>四旋翼。</p></li><li><p><strong>仿真测试</strong>：Gazebo<br>包括走廊类场景，以及高密度静态/动态/混合障碍环境。</p></li><li><p><strong>真实飞行测试</strong>：自定义四旋翼平台<br>机载计算为 <strong>Jetson Orin NX</strong>，传感器使用 <strong>Intel RealSense D435i</strong>，状态估计使用 <strong>LIO</strong>。</p></li></ol><h3 id="与哪些方法进行了对比？">与哪些方法进行了对比？</h3><p>主要对比了三类对象：</p><ul><li><strong>EGO-Planner</strong>：经典静态局部规划器；</li><li><strong>ViGO</strong>：作者之前做的 vision-aided dynamic planner；</li><li><strong>NavRL without safety shield</strong>：看安全屏蔽层是否真的有用。</li></ul><p>这个对比组合有意义，但也有明显不足：<strong>缺少更强的 RL baseline</strong>，所以它更像“证明自己能部署且比这两个工程基线更抗动态障碍”，而不是证明自己在 RL 领域绝对最强。</p><h3 id="使用了哪些指标？">使用了哪些指标？</h3><p>作者主要看了四类指标：</p><ol><li><p><strong>训练成功率</strong><br>定义为从起点安全到达目标点且无碰撞。</p></li><li><p><strong>训练回报曲线</strong></p></li><li><p><strong>平均碰撞次数</strong><br>在 20 次运行上统计。</p></li><li><p><strong>各模块运行时延</strong></p></li></ol><h3 id="最重要的实验结果是什么？">最重要的实验结果是什么？</h3><p>下面这些数字最值得记住。</p><table><thead><tr><th>结果点</th><th>关键数字</th><th>说明</th></tr></thead><tbody><tr><td>Curriculum learning 提升成功率</td><td><code>74.51% -&gt; 82.71%</code>、<code>62.30% -&gt; 80.96%</code>、<code>54.98% -&gt; 68.65%</code></td><td>动态障碍越多，课程学习越重要</td></tr><tr><td>动态环境碰撞数</td><td><code>ViGO 3.15</code> vs <code>NavRL 0.85</code></td><td>NavRL 在动态避障上明显更强</td></tr><tr><td>混合环境碰撞数</td><td><code>ViGO 4.40</code> vs <code>NavRL 2.10</code></td><td>说明对“静态 + 动态并存”更有优势</td></tr><tr><td>safety shield 增益</td><td><code>0.85 vs 2.70</code>、<code>2.10 vs 4.60</code></td><td>去掉安全层后，动态与混合环境碰撞显著变多</td></tr><tr><td>机载时延</td><td><code>15 ms + 27 ms + 7 ms + 16 ms</code></td><td>Orin NX 上总时延约 65 ms，约 15 Hz，可实时运行</td></tr></tbody></table><h4 id="1-Curriculum-learning-确实有效">1. Curriculum learning 确实有效</h4><p>训练环境大小为 <code>50m x 50m</code>，动态障碍数量从 <code>60 -&gt; 80 -&gt; 100 -&gt; 120</code> 逐步提升。<br>在固定训练时间下：</p><ul><li><code>dynamic=80</code> 时，成功率从 <code>74.51%</code> 提升到 <code>82.71%</code></li><li><code>dynamic=100</code> 时，从 <code>62.30%</code> 提升到 <code>80.96%</code></li><li><code>dynamic=120</code> 时，从 <code>54.98%</code> 提升到 <code>68.65%</code></li></ul><p>这说明对于动态障碍局部导航，<strong>课程学习不是装饰项，而是训练能不能稳定起来的重要条件。</strong></p><h4 id="2-大规模并行训练有用">2. 大规模并行训练有用</h4><p>作者比较了不同机器人数量下的训练回报，结论是：<strong>机器人越多，收敛越快，最终回报也越高。</strong></p><p>这个结果很有参考价值，因为如果后续也做 Isaac Sim 或 Omnidrones 一类的大规模并行训练，训练吞吐量很可能决定能不能把“动态交互策略”真正学出来。</p><h4 id="3-真正的优势体现在动态和混合环境">3. 真正的优势体现在动态和混合环境</h4><p>在 <code>20m x 40m</code> 的高密度测试图上，平均碰撞次数如下：</p><ul><li><p><strong>静态环境</strong></p><ul><li>EGO: <code>0.45</code></li><li>ViGO: <code>0.80</code></li><li>NavRL w/o shield: <code>0.95</code></li><li>NavRL: <code>0.65</code></li></ul></li><li><p><strong>纯动态环境</strong></p><ul><li>ViGO: <code>3.15</code></li><li>NavRL w/o shield: <code>2.70</code></li><li>NavRL: <code>0.85</code></li></ul></li><li><p><strong>混合环境</strong></p><ul><li>ViGO: <code>4.40</code></li><li>NavRL w/o shield: <code>4.60</code></li><li>NavRL: <code>2.10</code></li></ul></li></ul><p>这个结果非常说明问题：<strong>NavRL 的主要价值不是把静态环境做到极致，而是在动态和混合场景里显著降低碰撞。</strong></p><h4 id="4-Safety-shield-不是可有可无">4. Safety shield 不是可有可无</h4><p>从上面的数字也能看出来，去掉 safety shield 后：</p><ul><li>纯动态环境碰撞从 <code>0.85</code> 上升到 <code>2.70</code></li><li>混合环境碰撞从 <code>2.10</code> 上升到 <code>4.60</code></li></ul><p>这说明仅靠 RL 策略本身还不够稳，安全屏蔽层在动态环境里几乎是<strong>决定性组件</strong>。</p><h4 id="5-真机运行时延是可以接受的">5. 真机运行时延是可以接受的</h4><p>Jetson Orin NX 上的运行时间：</p><ul><li>Static perception: <code>15 ms</code></li><li>Dynamic perception: <code>27 ms</code></li><li>RL policy: <code>7 ms</code></li><li>Safety shield: <code>16 ms</code></li></ul><p>总和大约 <code>65 ms</code>，对应约 <code>15 Hz</code>。这说明它确实可以上机实时跑，但也说明一个事实：<strong>瓶颈并不在策略网络，而在前端感知和安全层。</strong></p><h3 id="是否有真实无人机实验？">是否有真实无人机实验？</h3><p>有。作者做了真实室内飞行实验，设置静态障碍，并让行人朝无人机运动，测试其避障与到达目标能力。论文给出的结论是：<strong>无人机能够零样本从仿真迁移到真实平台，并在有行人扰动的场景中安全到达目标。</strong></p><h3 id="实验是否足以支持作者的结论？">实验是否足以支持作者的结论？</h3><p>本文认为，它<strong>足以支持“这是一个可部署、在动态环境中有效的 RL 局部导航系统”<strong>这个结论，但</strong>还不足以支持“这是动态无人机导航的通用最优方案”</strong>。</p><p>原因是：</p><ul><li>baseline 不够丰富，特别缺少更强的 RL 和 hybrid 方法；</li><li>真实实验展示了可行性，但规模仍然有限；</li><li>主要指标是碰撞次数，缺少更细的效率、舒适性、死锁率、TTC 裕度等分析；</li><li>最大速度只有 <code>2.0 m/s</code>，更偏安全导航，不是高速敏捷飞行。</li></ul><h2 id="5-这个方法有哪些局限性？">5. 这个方法有哪些局限性？</h2><table><thead><tr><th>局限性</th><th>为什么会出现</th><th>可能失败的场景</th><th>是否可验证</th></tr></thead><tbody><tr><td>对前端感知质量依赖强</td><td>策略直接依赖动态障碍检测、关联与速度估计</td><td>多人交错、遮挡、低光、深度噪声大、目标突然加速</td><td>可以，做漏检率、测速噪声、感知时延消融</td></tr><tr><td>动态障碍模型过于简化</td><td>主要把障碍抽象成 bounding box / sphere，并用局部几何近似处理</td><td>非刚体目标、人群交互、急转弯目标、多障碍博弈</td><td>可以，构造不同运动模型场景测试 shield 触发率与碰撞率</td></tr><tr><td>Safety shield 偏保守</td><td>只做局部动作投影，不理解长期通行结构</td><td>密集人群、窄门会车、短时堵塞环境</td><td>可以，统计 deadlock rate、等待时间、到达时间</td></tr><tr><td>状态表示压缩掉部分信息</td><td>射线图和对象级状态高效，但丢失纹理、语义与细几何</td><td>复杂立体场景、细长障碍、语义约束强的环境</td><td>可以，与 richer map / semantic state 做消融</td></tr><tr><td>实验覆盖面仍有限</td><td>论文重点是先证明能飞、能避障、能上机</td><td>室外强光、大场景长航程、高速飞行、密集近距离交互</td><td>可以，扩展到更复杂真实环境和长期测试</td></tr></tbody></table><blockquote><p><strong>最关键的一条：</strong> 这篇论文最大的风险不是“策略学不会”，而是“真实部署时前端感知和安全层一起决定了上限”。</p></blockquote><h2 id="6-可以如何在此基础上创新？">6. 可以如何在此基础上创新？</h2><p>如果后续想围绕这篇论文做研究，优先考虑那些<strong>真的能解决部署问题</strong>、而且<strong>能靠实验验证</strong>的方向，而不是只换个 backbone。</p><table><thead><tr><th>方向</th><th>针对原方法的不足</th><th>可以采用的思路</th><th>难度</th><th>是否适合作为论文创新点</th></tr></thead><tbody><tr><td>感知不确定性感知的安全屏蔽层</td><td>shield 默认感知可信，但真实检测和测速都有噪声</td><td>把检测框、速度估计和跟踪协方差引入 VO 约束，做 risk-aware / chance-constrained shield</td><td>中等偏高</td><td>很适合，且非常贴合真实部署</td></tr><tr><td>遮挡与短时失踪鲁棒的历史记忆策略</td><td>当前策略对遮挡后重现的目标未必稳</td><td>加入 track history、短时记忆或显式意图预测</td><td>中等</td><td>适合，尤其适合复杂人流场景</td></tr><tr><td>防卡死的 RL + 短时规划混合框架</td><td>reactive policy + shield 在密集动态环境里可能保守甚至卡住</td><td>RL 给局部意图，再由 MPC / sampling / trajectory optimizer 生成短时可执行轨迹</td><td>高</td><td>非常适合，论文潜力高</td></tr><tr><td>面向真实部署的 sim-to-real 鲁棒训练</td><td>论文证明了能迁移，但迁移边界分析不足</td><td>把真实日志噪声、时延、漏检、模糊回灌训练，做多种 randomization</td><td>中等</td><td>很适合，尤其适合真机导向工作</td></tr><tr><td>人类动态障碍意图建模</td><td>目前只处理“障碍在动”，没有建模“障碍想怎么动”</td><td>引入 pedestrian intent prediction、social navigation 约束</td><td>高</td><td>适合，但需要更系统的人机交互实验</td></tr></tbody></table><h3 id="更推荐的组合">更推荐的组合</h3><p><strong>感知不确定性建模 + 安全屏蔽层升级 + 真实部署鲁棒训练</strong></p><p>这条线最能击中 NavRL 的真实薄弱处，而且最容易形成“动态未知环境 + 真机部署”的清晰论文故事。</p><h2 id="7-最后给出一份简短笔记">7. 最后给出一份简短笔记</h2><table><thead><tr><th>项目</th><th>内容</th></tr></thead><tbody><tr><td>论文解决的问题</td><td>让无人机在动态未知环境中进行安全局部导航，同时尽量实现从仿真到真实平台的零样本迁移</td></tr><tr><td>核心方法</td><td>静态障碍射线图 + 动态障碍目标状态作为输入，PPO 学局部速度策略，再用 VO-inspired safety shield 修正不安全动作</td></tr><tr><td>输入</td><td>目标相对状态、当前速度、静态障碍射线距离矩阵、动态障碍的位置 / 速度 / 尺寸状态</td></tr><tr><td>输出</td><td>目标坐标系下的三维速度指令</td></tr><tr><td>主要创新</td><td>可迁移状态表示、Beta 有界速度动作设计、VO 风格安全屏蔽层、大规模并行训练到真机部署链路</td></tr><tr><td>关键实验结论</td><td>NavRL 在动态和混合环境中的碰撞次数明显低于对比方法；安全屏蔽层对动态场景非常关键；Jetson Orin NX 上可实时运行</td></tr><tr><td>最大局限性</td><td>对动态障碍感知与跟踪质量依赖很强，且 safety shield 在密集动态场景中可能偏保守、容易卡住</td></tr><tr><td>最值得尝试的创新方向</td><td>感知不确定性感知的安全屏蔽层，以及考虑时延、漏检和遮挡的真实部署鲁棒训练</td></tr><tr><td>是否值得复现</td><td>值得，尤其适合作为“动态环境 RL 局部导航 + 真机部署”的强基线</td></tr><tr><td>复现时优先看哪些模块</td><td>状态表示构造、ray casting 静态编码、动态目标跟踪接口、奖励函数实现、VO safety shield、Isaac Sim 并行训练配置、PX4 / Jetson 部署链路</td></tr></tbody></table><h2 id="总体评价">总体评价</h2><p>如果只从算法新颖性看，NavRL 不是那种会让人惊呼“方法太新了”的论文；但如果从<strong>真实系统能不能落地</strong>这个角度看，它是很有价值的。它真正回答的问题不是“RL 能不能在论文里跑通”，而是“RL 能不能和感知、安全机制、机载计算一起组成一个能飞的动态导航系统”。</p><p>对这一研究方向来说，这篇论文最大的价值有两个：</p><ol><li>它给出一个<strong>能对标、能复现、能上机</strong>的动态局部导航基线。</li><li>它把后续最值得创新的痛点暴露得很清楚：<strong>感知不确定性、动态交互建模、shield 的保守性、以及复杂真实场景下的鲁棒性。</strong></li></ol><p>因此，本文的结论是：<strong>这篇论文很值得读，也值得复现，但更值得在它暴露出来的问题上继续往前推。</strong></p>]]></content>
    
    
    <summary type="html">梳理 NavRL 的状态表示、PPO 局部速度策略、安全屏蔽层、动态避障实验与 Jetson 真机部署价值。</summary>
    
    
    
    <category term="论文学习" scheme="https://dreamer198.top/categories/%E8%AE%BA%E6%96%87%E5%AD%A6%E4%B9%A0/"/>
    
    
    <category term="强化学习" scheme="https://dreamer198.top/tags/%E5%BC%BA%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
    
    <category term="NavRL" scheme="https://dreamer198.top/tags/NavRL/"/>
    
    <category term="路径规划" scheme="https://dreamer198.top/tags/%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92/"/>
    
    <category term="局部导航" scheme="https://dreamer198.top/tags/%E5%B1%80%E9%83%A8%E5%AF%BC%E8%88%AA/"/>
    
  </entry>
  
  <entry>
    <title>Vim编辑器使用教程</title>
    <link href="https://dreamer198.top/2026/06/03/vim%E7%BC%96%E8%BE%91%E5%99%A8%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/06/03/vim%E7%BC%96%E8%BE%91%E5%99%A8%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</id>
    <published>2026-06-03T06:00:00.000Z</published>
    <updated>2026-06-03T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>Vim 是 Linux 终端里非常常用的文本编辑器。平时修改配置文件、写脚本、在服务器或 Docker 容器里临时改代码，经常都会用到它。</p><p>刚开始接触 Vim 时，最容易卡住的地方不是“怎么高效编辑”，而是“怎么进入编辑、怎么保存、怎么退出”。所以这篇文章先从最基本的生存命令开始，再逐步整理移动、复制粘贴、查找替换、多文件编辑和常用配置。</p><span id="more"></span><h2 id="一、安装-Vim">一、安装 Vim</h2><p>在 Ubuntu / Debian 系统中安装：</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> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y vim</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">vim --version</span><br></pre></td></tr></table></figure><p>如果只是临时使用，系统里也可能已经有 <code>vi</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">vi 文件名</span><br></pre></td></tr></table></figure><p>不过日常更推荐安装并使用 <code>vim</code>。</p><h2 id="二、打开和创建文件">二、打开和创建文件</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">vim test.txt</span><br></pre></td></tr></table></figure><p>如果 <code>test.txt</code> 不存在，Vim 会在保存时创建这个文件。</p><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> vim /etc/hosts</span><br></pre></td></tr></table></figure><p>打开后，默认处于普通模式，此时直接敲键盘不会像普通编辑器一样输入文字。</p><h2 id="三、理解-Vim-的几种模式">三、理解 Vim 的几种模式</h2><p>Vim 最重要的特点是“模式编辑”。常用模式有下面几种：</p><table><thead><tr><th>模式</th><th>作用</th><th>进入方式</th><th>退出方式</th></tr></thead><tbody><tr><td>普通模式</td><td>移动光标、复制、删除、保存退出</td><td>打开文件后默认进入</td><td>按 <code>i</code>、<code>a</code>、<code>o</code> 等进入插入模式</td></tr><tr><td>插入模式</td><td>正常输入文字</td><td>在普通模式按 <code>i</code>、<code>a</code>、<code>o</code></td><td>按 <code>Esc</code> 回到普通模式</td></tr><tr><td>命令模式</td><td>保存、退出、查找替换等</td><td>普通模式按 <code>:</code></td><td>执行命令后自动返回普通模式</td></tr><tr><td>可视模式</td><td>选择一段文本</td><td>普通模式按 <code>v</code>、<code>V</code>、<code>Ctrl + v</code></td><td>按 <code>Esc</code> 回到普通模式</td></tr></tbody></table><p>最常用的流程可以记成：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">普通模式 -&gt; 按 i -&gt; 插入模式 -&gt; 输入内容 -&gt; 按 Esc -&gt; 普通模式 -&gt; 输入 :wq 保存退出</span><br></pre></td></tr></table></figure><h2 id="四、最重要的保存和退出">四、最重要的保存和退出</h2><p>这些命令都需要先按 <code>Esc</code> 回到普通模式，再输入 <code>:</code> 进入命令模式。</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>保存文件</td><td><code>:w</code></td></tr><tr><td>退出 Vim</td><td><code>:q</code></td></tr><tr><td>保存并退出</td><td><code>:wq</code></td></tr><tr><td>保存并退出</td><td><code>:x</code></td></tr><tr><td>不保存强制退出</td><td><code>:q!</code></td></tr><tr><td>强制保存</td><td><code>:w!</code></td></tr></tbody></table><p>刚开始最常用的是：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">wq</span></span><br></pre></td></tr></table></figure><p>如果改乱了，不想保存：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:q!</span><br></pre></td></tr></table></figure><h2 id="五、进入插入模式">五、进入插入模式</h2><p>在普通模式下，可以通过下面这些按键进入插入模式：</p><table><thead><tr><th>按键</th><th>作用</th></tr></thead><tbody><tr><td><code>i</code></td><td>在当前光标前插入</td></tr><tr><td><code>a</code></td><td>在当前光标后插入</td></tr><tr><td><code>I</code></td><td>在当前行开头插入</td></tr><tr><td><code>A</code></td><td>在当前行末尾插入</td></tr><tr><td><code>o</code></td><td>在当前行下方新建一行并插入</td></tr><tr><td><code>O</code></td><td>在当前行上方新建一行并插入</td></tr></tbody></table><p>最开始只记两个就够：</p><ul><li><code>i</code>：开始编辑当前内容。</li><li><code>o</code>：新开一行继续写。</li></ul><h2 id="六、移动光标">六、移动光标</h2><p>Vim 中可以用方向键移动，也可以用 <code>h</code>、<code>j</code>、<code>k</code>、<code>l</code> 移动：</p><table><thead><tr><th>按键</th><th>方向</th></tr></thead><tbody><tr><td><code>h</code></td><td>左</td></tr><tr><td><code>j</code></td><td>下</td></tr><tr><td><code>k</code></td><td>上</td></tr><tr><td><code>l</code></td><td>右</td></tr></tbody></table><p>常用移动命令：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>移动到行首</td><td><code>0</code></td></tr><tr><td>移动到行尾</td><td><code>$</code></td></tr><tr><td>移动到文件第一行</td><td><code>gg</code></td></tr><tr><td>移动到文件最后一行</td><td><code>G</code></td></tr><tr><td>移动到第 20 行</td><td><code>20G</code> 或 <code>:20</code></td></tr><tr><td>向后移动一个单词</td><td><code>w</code></td></tr><tr><td>向前移动一个单词</td><td><code>b</code></td></tr><tr><td>移动到当前单词结尾</td><td><code>e</code></td></tr></tbody></table><p>翻页操作：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>向下翻半屏</td><td><code>Ctrl + d</code></td></tr><tr><td>向上翻半屏</td><td><code>Ctrl + u</code></td></tr><tr><td>向下翻一屏</td><td><code>Ctrl + f</code></td></tr><tr><td>向上翻一屏</td><td><code>Ctrl + b</code></td></tr></tbody></table><h2 id="七、删除、修改和撤销">七、删除、修改和撤销</h2><p>常用删除命令：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>删除当前字符</td><td><code>x</code></td></tr><tr><td>删除当前行</td><td><code>dd</code></td></tr><tr><td>删除 3 行</td><td><code>3dd</code></td></tr><tr><td>删除当前光标到行尾</td><td><code>D</code></td></tr><tr><td>删除当前单词</td><td><code>dw</code></td></tr><tr><td>删除当前光标到单词结尾</td><td><code>de</code></td></tr></tbody></table><p>常用修改命令：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>修改当前字符</td><td><code>r</code> 后接新字符</td></tr><tr><td>删除当前字符并进入插入模式</td><td><code>s</code></td></tr><tr><td>删除当前行并进入插入模式</td><td><code>cc</code></td></tr><tr><td>删除当前光标到行尾并进入插入模式</td><td><code>C</code></td></tr></tbody></table><p>撤销和重做：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>撤销上一步</td><td><code>u</code></td></tr><tr><td>重做</td><td><code>Ctrl + r</code></td></tr></tbody></table><p>如果误删了一行，可以按：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">u</span></span><br></pre></td></tr></table></figure><h2 id="八、复制、剪切和粘贴">八、复制、剪切和粘贴</h2><p>在 Vim 里，删除操作很多时候也可以理解成“剪切”。</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>复制当前行</td><td><code>yy</code></td></tr><tr><td>复制 3 行</td><td><code>3yy</code></td></tr><tr><td>剪切当前行</td><td><code>dd</code></td></tr><tr><td>粘贴到当前行下方</td><td><code>p</code></td></tr><tr><td>粘贴到当前行上方</td><td><code>P</code></td></tr></tbody></table><p>例如复制当前行并粘贴到下一行：</p><figure class="highlight vim"><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">yy</span><br><span class="line"><span class="keyword">p</span></span><br></pre></td></tr></table></figure><p>剪切当前行并粘贴到其他位置：</p><figure class="highlight vim"><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">dd</span><br><span class="line">移动光标</span><br><span class="line"><span class="keyword">p</span></span><br></pre></td></tr></table></figure><h2 id="九、可视模式选择文本">九、可视模式选择文本</h2><p>普通模式下按 <code>v</code> 进入字符选择，按 <code>V</code> 进入整行选择。</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>字符选择</td><td><code>v</code></td></tr><tr><td>整行选择</td><td><code>V</code></td></tr><tr><td>块选择</td><td><code>Ctrl + v</code></td></tr><tr><td>复制选中内容</td><td><code>y</code></td></tr><tr><td>删除选中内容</td><td><code>d</code></td></tr><tr><td>粘贴</td><td><code>p</code></td></tr></tbody></table><p>如果想复制一整段内容：</p><ol><li>按 <code>V</code> 进入整行选择。</li><li>使用 <code>j</code> / <code>k</code> 选择多行。</li><li>按 <code>y</code> 复制。</li><li>移动到目标位置后按 <code>p</code> 粘贴。</li></ol><h2 id="十、查找和替换">十、查找和替换</h2><h3 id="1-查找文本">1. 查找文本</h3><p>向下查找：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/关键词</span><br></pre></td></tr></table></figure><p>向上查找：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?关键词</span><br></pre></td></tr></table></figure><p>查找后继续跳转：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>下一个匹配</td><td><code>n</code></td></tr><tr><td>上一个匹配</td><td><code>N</code></td></tr></tbody></table><h3 id="2-替换文本">2. 替换文本</h3><p>替换当前行第一个匹配：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:s/旧内容/新内容/</span><br></pre></td></tr></table></figure><p>替换当前行所有匹配：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:s/旧内容/新内容/g</span><br></pre></td></tr></table></figure><p>替换全文所有匹配：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:%s/旧内容/新内容/g</span><br></pre></td></tr></table></figure><p>替换全文并逐个确认：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:%s/旧内容/新内容/gc</span><br></pre></td></tr></table></figure><p>其中：</p><ul><li><code>%</code> 表示全文。</li><li><code>s</code> 表示 substitute，也就是替换。</li><li><code>g</code> 表示当前行内所有匹配项。</li><li><code>c</code> 表示 confirm，每次替换前确认。</li></ul><h2 id="十一、显示行号和常用临时设置">十一、显示行号和常用临时设置</h2><p>在 Vim 中可以临时开启行号：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">set</span> <span class="keyword">number</span></span><br></pre></td></tr></table></figure><p>关闭行号：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">set</span> nonumber</span><br></pre></td></tr></table></figure><p>开启搜索高亮：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">set</span> hlsearch</span><br></pre></td></tr></table></figure><p>关闭搜索高亮：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">nohlsearch</span></span><br></pre></td></tr></table></figure><p>开启自动缩进：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">set</span> autoindent</span><br></pre></td></tr></table></figure><p>显示当前文件名和状态：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">f</span></span><br></pre></td></tr></table></figure><h2 id="十二、编辑多个文件">十二、编辑多个文件</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">vim file1.txt file2.txt</span><br></pre></td></tr></table></figure><p>查看缓冲区列表：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">ls</span></span><br></pre></td></tr></table></figure><p>切换到下一个文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">bn</span></span><br></pre></td></tr></table></figure><p>切换到上一个文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">bp</span></span><br></pre></td></tr></table></figure><p>打开另一个文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">e</span> another.txt</span><br></pre></td></tr></table></figure><p>保存全部文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">wa</span></span><br></pre></td></tr></table></figure><p>退出全部文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">qa</span></span><br></pre></td></tr></table></figure><p>保存并退出全部文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">wqa</span></span><br></pre></td></tr></table></figure><h2 id="十三、分屏编辑">十三、分屏编辑</h2><p>横向分屏打开文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">split</span> <span class="keyword">file</span>.txt</span><br></pre></td></tr></table></figure><p>也可以简写：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">sp</span> <span class="keyword">file</span>.txt</span><br></pre></td></tr></table></figure><p>纵向分屏打开文件：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">vsplit</span> <span class="keyword">file</span>.txt</span><br></pre></td></tr></table></figure><p>也可以简写：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:vsp <span class="keyword">file</span>.txt</span><br></pre></td></tr></table></figure><p>分屏后常用快捷键：</p><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>切换到左侧窗口</td><td><code>Ctrl + w</code> 后按 <code>h</code></td></tr><tr><td>切换到下方窗口</td><td><code>Ctrl + w</code> 后按 <code>j</code></td></tr><tr><td>切换到上方窗口</td><td><code>Ctrl + w</code> 后按 <code>k</code></td></tr><tr><td>切换到右侧窗口</td><td><code>Ctrl + w</code> 后按 <code>l</code></td></tr><tr><td>关闭当前窗口</td><td><code>:q</code></td></tr></tbody></table><h2 id="十四、没有权限保存怎么办">十四、没有权限保存怎么办</h2><p>有时打开系统文件时忘了加 <code>sudo</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">vim /etc/hosts</span><br></pre></td></tr></table></figure><p>编辑完保存时可能提示没有权限。可以在 Vim 中执行：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:<span class="keyword">w</span> !sudo tee %</span><br></pre></td></tr></table></figure><p>执行后输入当前用户密码，文件就会通过 <code>sudo tee</code> 写回去。</p><p>这个命令的含义是：</p><ul><li><code>:w</code>：把当前缓冲区内容写出。</li><li><code>!sudo tee %</code>：把内容交给外部命令 <code>sudo tee</code>。</li><li><code>%</code>：表示当前文件名。</li></ul><p>保存成功后，如果 Vim 提示文件已改变，可以输入：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">:e!</span><br></pre></td></tr></table></figure><p>重新加载当前文件。</p><h2 id="十五、配置-vimrc">十五、配置 vimrc</h2><p>如果希望每次打开 Vim 都自动显示行号、开启语法高亮，可以编辑用户配置文件：</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">vim ~/.vimrc</span><br></pre></td></tr></table></figure><p>一个简单的配置示例：</p><figure class="highlight vim"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">&quot; 显示行号</span></span><br><span class="line"><span class="keyword">set</span> <span class="keyword">number</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 开启语法高亮</span></span><br><span class="line"><span class="keyword">syntax</span> <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 开启文件类型检测</span></span><br><span class="line"><span class="keyword">filetype</span> plugin <span class="built_in">indent</span> <span class="keyword">on</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 搜索时忽略大小写，输入大写字母时自动区分大小写</span></span><br><span class="line"><span class="keyword">set</span> ignorecase</span><br><span class="line"><span class="keyword">set</span> smartcase</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 搜索时边输入边高亮</span></span><br><span class="line"><span class="keyword">set</span> incsearch</span><br><span class="line"><span class="keyword">set</span> hlsearch</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; Tab 显示为 4 个空格宽度</span></span><br><span class="line"><span class="keyword">set</span> tabstop=<span class="number">4</span></span><br><span class="line"><span class="keyword">set</span> <span class="built_in">shiftwidth</span>=<span class="number">4</span></span><br><span class="line"><span class="keyword">set</span> softtabstop=<span class="number">4</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 按 Tab 输入空格</span></span><br><span class="line"><span class="keyword">set</span> expandtab</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 自动缩进</span></span><br><span class="line"><span class="keyword">set</span> autoindent</span><br><span class="line"><span class="keyword">set</span> smartindent</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 显示当前光标所在行</span></span><br><span class="line"><span class="keyword">set</span> cursorline</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 底部显示命令</span></span><br><span class="line"><span class="keyword">set</span> showcmd</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 显示模式</span></span><br><span class="line"><span class="keyword">set</span> showmode</span><br><span class="line"></span><br><span class="line"><span class="comment">&quot; 支持鼠标</span></span><br><span class="line"><span class="keyword">set</span> mouse=<span class="keyword">a</span></span><br></pre></td></tr></table></figure><p>保存后重新打开 Vim 即可生效。</p><p>如果不想使用鼠标，可以删除：</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> mouse=<span class="keyword">a</span></span><br></pre></td></tr></table></figure><h2 id="十六、常用命令速查">十六、常用命令速查</h2><table><thead><tr><th>场景</th><th>命令</th></tr></thead><tbody><tr><td>打开文件</td><td><code>vim 文件名</code></td></tr><tr><td>进入插入模式</td><td><code>i</code></td></tr><tr><td>退出插入模式</td><td><code>Esc</code></td></tr><tr><td>保存</td><td><code>:w</code></td></tr><tr><td>退出</td><td><code>:q</code></td></tr><tr><td>保存并退出</td><td><code>:wq</code></td></tr><tr><td>不保存退出</td><td><code>:q!</code></td></tr><tr><td>撤销</td><td><code>u</code></td></tr><tr><td>重做</td><td><code>Ctrl + r</code></td></tr><tr><td>删除当前行</td><td><code>dd</code></td></tr><tr><td>复制当前行</td><td><code>yy</code></td></tr><tr><td>粘贴</td><td><code>p</code></td></tr><tr><td>查找</td><td><code>/关键词</code></td></tr><tr><td>全文替换</td><td><code>:%s/旧内容/新内容/g</code></td></tr><tr><td>显示行号</td><td><code>:set number</code></td></tr><tr><td>跳到文件开头</td><td><code>gg</code></td></tr><tr><td>跳到文件末尾</td><td><code>G</code></td></tr><tr><td>跳到第 20 行</td><td><code>20G</code></td></tr></tbody></table><h2 id="十七、推荐练习顺序">十七、推荐练习顺序</h2><p>刚开始不要一口气背太多命令，可以按下面顺序练：</p><ol><li>用 <code>vim test.txt</code> 打开文件。</li><li>按 <code>i</code> 输入几行文字。</li><li>按 <code>Esc</code> 回到普通模式。</li><li>用 <code>:w</code> 保存。</li><li>用 <code>dd</code> 删除一行，再用 <code>u</code> 撤销。</li><li>用 <code>yy</code> 复制一行，再用 <code>p</code> 粘贴。</li><li>用 <code>/关键词</code> 查找内容。</li><li>用 <code>:%s/旧内容/新内容/gc</code> 练习确认替换。</li><li>最后用 <code>:wq</code> 保存退出。</li></ol><p>熟悉这些以后，日常修改配置文件、编辑脚本、在服务器里处理文本基本就够用了。</p><h2 id="总结">总结</h2><p>Vim 的学习重点不是一次记住所有快捷键，而是先建立“模式”的概念：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">普通模式负责操作，插入模式负责输入，命令模式负责保存退出和高级命令。</span><br></pre></td></tr></table></figure><p>新手最先需要掌握的是：</p><ul><li><code>i</code>：进入编辑。</li><li><code>Esc</code>：回到普通模式。</li><li><code>:wq</code>：保存退出。</li><li><code>:q!</code>：放弃修改退出。</li><li><code>dd</code>、<code>yy</code>、<code>p</code>、<code>u</code>：完成基础编辑。</li></ul><p>等这些命令用熟之后，再逐渐学习查找替换、分屏、多文件和 <code>.vimrc</code> 配置，Vim 就会从“容易退出不了的编辑器”变成一个非常顺手的终端工具。</p>]]></content>
    
    
    <summary type="html">前言 Vim 是 Linux 终端里非常常用的文本编辑器。平时修改配置文件、写脚本、在服务器或 Docker 容器里临时改代码，经常都会用到它。 刚开始接触 Vim 时，最容易卡住的地方不是“怎么高效编辑”，而是“怎么进入编辑、怎么保存、怎么退出”。所以这篇文章先从最基本的生存命令开始，再逐步整理移动、复制…</summary>
    
    
    
    <category term="开发工具" scheme="https://dreamer198.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Linux" scheme="https://dreamer198.top/tags/Linux/"/>
    
    <category term="Vim" scheme="https://dreamer198.top/tags/Vim/"/>
    
    <category term="终端" scheme="https://dreamer198.top/tags/%E7%BB%88%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>Docker ROS1 Diff-Planner PX4 真机部署教程</title>
    <link href="https://dreamer198.top/2026/06/02/Docker-ROS1-DiffPlanner-PX4%E7%9C%9F%E6%9C%BA%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/06/02/Docker-ROS1-DiffPlanner-PX4%E7%9C%9F%E6%9C%BA%E9%83%A8%E7%BD%B2%E6%95%99%E7%A8%8B/</id>
    <published>2026-06-02T09:00:00.000Z</published>
    <updated>2026-06-08T10:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文记录在 Jetson 上通过 Docker 跑通 ROS1 真机链路：</p><figure class="highlight text"><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></pre></td><td class="code"><pre><span class="line">Mid-360S</span><br><span class="line">  -&gt; livox_ros_driver2</span><br><span class="line">  -&gt; FAST-LIO</span><br><span class="line">  -&gt; /Odometry + /cloud_registered</span><br><span class="line">  -&gt; odom_to_base.py</span><br><span class="line">  -&gt; /Odometry_base</span><br><span class="line">  -&gt; odom_to_pose.py</span><br><span class="line">  -&gt; /mavros/vision_pose/pose</span><br><span class="line">  -&gt; PX4 EKF2</span><br><span class="line"></span><br><span class="line">/Odometry_base + /cloud_registered</span><br><span class="line">  -&gt; Diff-Planner</span><br><span class="line">  -&gt; /drone_0_planning/pos_cmd</span><br><span class="line">  -&gt; trajectory_msg_converter.py</span><br><span class="line">  -&gt; /command/trajectory</span><br><span class="line">  -&gt; se3_controller</span><br><span class="line">  -&gt; /mavros/setpoint_raw/attitude</span><br><span class="line">  -&gt; PX4 Offboard</span><br></pre></td></tr></table></figure><p>环境：</p><figure class="highlight text"><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">机载电脑：Jetson Orin NX</span><br><span class="line">宿主系统：Ubuntu 22.04</span><br><span class="line">容器镜像：ros:noetic-ros-base-focal</span><br><span class="line">飞控：PX4</span><br><span class="line">雷达：Livox Mid-360S</span><br><span class="line">算法：FAST-LIO + Diff-Planner</span><br></pre></td></tr></table></figure><ul><li>宿主机只负责 Docker、雷达网口、PX4 串口。</li><li>ROS1 全部放进容器。</li><li>真机默认不自动切 Offboard，不自动解锁，始终保留遥控器接管能力。</li><li>第一次建议先不装桨；装桨后先在 <code>0.3 ~ 0.5 m</code> 高度测试。</li><li>最新 Dockerfile 部署仓库：<a href="https://github.com/dreamer198/diff-planner-px4-deployment">https://github.com/dreamer198/diff-planner-px4-deployment</a></li></ul><span id="more"></span><h2 id="代码链路总览">代码链路总览</h2><p>这套真机方案分成三部分：Jetson 宿主机负责启动和硬件访问，ROS1 节点跑在容器内，本机负责 RViz 可视化和发目标点。</p><p>目前推荐直接使用整理好的 Dockerfile 部署仓库：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://github.com/dreamer198/diff-planner-px4-deployment</span><br></pre></td></tr></table></figure><ul><li>Jetson 推荐路径：<code>/home/jetson2/diff-planner-px4-deployment</code></li><li>Jetson 宿主机启动入口：<code>scripts/start_real_px4_mid360_fastlio.sh</code></li><li>本机 RViz 启动入口：<code>scripts/start_jetson_ros1_rviz.sh</code></li><li>ROS1 容器名：<code>ros_noetic_realflight</code></li><li>容器内工作区：<code>~/livox_ws</code>（Livox 驱动）、<code>~/catkin_ws</code>（FAST-LIO、Diff-Planner、控制相关代码）</li></ul><p>启动脚本运行在 Jetson 宿主机，不在容器内执行。它通过 <code>tmux + docker exec</code> 依次拉起下面这条链路：</p><figure class="highlight text"><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">Jetson 宿主机</span><br><span class="line">  -&gt; scripts/start_real_px4_mid360_fastlio.sh</span><br><span class="line">  -&gt; docker exec 进入 ros_noetic_realflight 容器</span><br><span class="line"></span><br><span class="line">容器内感知链路：</span><br><span class="line">  livox_ros_driver2</span><br><span class="line">    -&gt; /livox/lidar + /livox/imu</span><br><span class="line">  FAST-LIO</span><br><span class="line">    -&gt; /Odometry + /cloud_registered</span><br><span class="line">  odom_to_base.py</span><br><span class="line">    -&gt; /Odometry_base</span><br><span class="line">  odom_to_pose.py</span><br><span class="line">    -&gt; /mavros/vision_pose/pose</span><br><span class="line">  MAVROS</span><br><span class="line">    -&gt; PX4 EKF2</span><br><span class="line"></span><br><span class="line">容器内规划控制链路：</span><br><span class="line">  /Odometry_base + 点云/地图</span><br><span class="line">    -&gt; Diff-Planner</span><br><span class="line">    -&gt; /drone_0_planning/pos_cmd</span><br><span class="line">    -&gt; trajectory_msg_converter.py</span><br><span class="line">    -&gt; /command/trajectory</span><br><span class="line">    -&gt; se3_controller</span><br><span class="line">    -&gt; /mavros/setpoint_raw/attitude</span><br><span class="line">    -&gt; PX4 Offboard</span><br><span class="line"></span><br><span class="line">本机可视化链路：</span><br><span class="line">  scripts/start_jetson_ros1_rviz.sh</span><br><span class="line">    -&gt; 本机容器内 RViz + /root/jetson_real_stack.rviz + rviz_goal_to_diff_planner.py</span><br><span class="line">    -&gt; /move_base_simple/goal 或 /clicked_point</span><br><span class="line">    -&gt; /goal</span><br><span class="line">    -&gt; Diff-Planner</span><br></pre></td></tr></table></figure><p>各层对应代码如下：</p><ul><li>Dockerfile 部署层：<code>docker/Dockerfile</code>、<code>docker/docker_run_real.sh</code>，负责构建镜像、创建 <code>ros_noetic_realflight</code> 容器并挂载真机配置。</li><li>启动调度层：<code>scripts/start_real_px4_mid360_fastlio.sh</code>，负责 <code>start / stop / restart / status / attach</code>，并在宿主机顺序拉起整条链路。</li><li>雷达驱动层：<code>~/livox_ws/src/livox_ros_driver2</code>，入口 <code>msg_MID360s.launch</code>，配置 <code>config/MID360s_config.json</code>，发布 <code>/livox/lidar</code>、<code>/livox/imu</code>。</li><li>里程计层：<code>~/catkin_ws/src/FAST_LIO</code>，入口 <code>launch/mapping_mid360.launch</code>，发布 <code>/Odometry</code>、`/cloud_registered``；Mid-360S 倾斜安装时常改 IMU 初始化逻辑。</li><li>机体外参补偿层：<code>/root/catkin_ws/src/px4_realflight_tools/scripts/odom_to_base.py</code>，把 <code>/Odometry</code> 转成 <code>/Odometry_base</code>。</li><li>PX4 视觉桥接层：<code>/root/catkin_ws/src/px4_realflight_tools/scripts/odom_to_pose.py</code>，把 <code>/Odometry_base</code> 转成 <code>/mavros/vision_pose/pose</code>。</li><li>规划层：<code>~/catkin_ws/src/Diff-Planner-PX4/src/diff_planner</code>，真机入口通常是 <code>run_real_mid360_lio.launch</code>，输出 <code>/drone_0_planning/pos_cmd</code>。</li><li>轨迹格式转换层：<code>~/catkin_ws/src/Diff-Planner-PX4/src/diff_planner/plan_manage/scripts/trajectory_msg_converter.py</code>，把 <code>/drone_0_planning/pos_cmd</code> 转成 <code>/command/trajectory</code>。</li><li>控制层：<code>~/catkin_ws/src/Diff-Planner-PX4/src/se3_controller</code>，核心节点 <code>src/se3_controller_node.cpp</code>，把 <code>/command/trajectory</code> 转成 <code>/mavros/setpoint_raw/attitude</code>。</li><li>本机 RViz 层：<code>scripts/start_jetson_ros1_rviz.sh</code>、<code>config/rviz/jetson_real_stack.rviz</code>、<code>scripts/rviz_goal_to_diff_planner.py</code>，负责显示点云、里程计、占据地图、规划轨迹，并把 <code>2D Nav Goal</code> 或 <code>Publish Point</code> 转成 <code>/goal</code>。</li></ul><p>如果后面要排错，可以按这个顺序看：<code>/livox/lidar</code> -&gt; <code>/Odometry</code> -&gt; <code>/Odometry_base</code> -&gt; <code>/mavros/vision_pose/pose</code> -&gt; <code>/drone_0_planning/pos_cmd</code> -&gt; <code>/command/trajectory</code> -&gt; <code>/mavros/setpoint_raw/attitude</code>。</p><h2 id="推荐：使用-Dockerfile-部署仓库">推荐：使用 Dockerfile 部署仓库</h2><p>新 Jetson 迁移时，优先使用这个仓库，而不是手动一步步复制代码：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /home/jetson2</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/dreamer198/diff-planner-px4-deployment.git</span><br><span class="line"><span class="built_in">cd</span> diff-planner-px4-deployment</span><br><span class="line">./docker/docker_run_real.sh build</span><br><span class="line">FCU_DEVICE=/dev/ttyACM0 ./docker/docker_run_real.sh restart</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> diff-planner-px4-deployment \</span><br><span class="line">FCU_URL=<span class="string">&#x27;serial:///dev/ttyACM0:57600&#x27;</span> \</span><br><span class="line">GCS_URL=<span class="string">&#x27;udp://:14550@10.0.30.196:14550&#x27;</span> \</span><br><span class="line">SE3_HOVER_PERCENT=0.60 \</span><br><span class="line">SE3_MAX_HOVER_PERCENT=0.95 \</span><br><span class="line">SE3_MAX_OUTPUT_THRUST=1.00 \</span><br><span class="line">SE3_ENABLE_THRUST_ESTIMATION=<span class="literal">false</span> \</span><br><span class="line">SE3_MAX_FEEDFORWARD_ACC=1.2 \</span><br><span class="line">DIFF_PLANNER_INFLATION_SIZE=0.2 \</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh start</span><br></pre></td></tr></table></figure><p>后面的手动安装步骤主要用于理解链路和排错；实际迁移建议直接用这个 Dockerfile 仓库。</p><h2 id="一、宿主机准备">一、宿主机准备</h2><p>下面命令都在 Jetson 宿主机执行。</p><h3 id="1-Docker">1. Docker</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 status docker</span><br><span class="line">docker info</span><br></pre></td></tr></table></figure><p>如当前用户没有 Docker 权限：</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> usermod -aG docker <span class="variable">$USER</span></span><br></pre></td></tr></table></figure><h3 id="2-Mid-360S-网口">2. Mid-360S 网口</h3><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 addr</span><br></pre></td></tr></table></figure><p>假设雷达网卡为 <code>eth0</code>：</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> ip addr add 192.168.1.101/24 dev eth0</span><br><span class="line"><span class="built_in">sudo</span> ip <span class="built_in">link</span> <span class="built_in">set</span> eth0 up</span><br><span class="line">ping 192.168.1.199</span><br></pre></td></tr></table></figure><p>把上面 IP 改成你的实际值。</p><h3 id="3-PX4-串口">3. PX4 串口</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">ls</span> -l /dev/ttyACM*</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> 666 /dev/ttyACM0</span><br></pre></td></tr></table></figure><p>本文默认使用 <code>/dev/ttyACM0</code>。</p><h2 id="二、创建-ROS1-容器">二、创建 ROS1 容器</h2><p>如果你已经有 <code>ros_noetic</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">mkdir</span> -p ~/docker/ros_root</span><br><span class="line">xhost +SI:localuser:root</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">docker run -it \</span><br><span class="line">  --name ros_noetic \</span><br><span class="line">  --network host \</span><br><span class="line">  --ipc host \</span><br><span class="line">  --privileged \</span><br><span class="line">  --device=/dev/ttyACM0 \</span><br><span class="line">  -e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">  -e XAUTHORITY=/root/.Xauthority \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  -v /tmp/.X11-unix:/tmp/.X11-unix \</span><br><span class="line">  -v <span class="variable">$HOME</span>/.Xauthority:/root/.Xauthority:ro \</span><br><span class="line">  -v /dev/bus/usb:/dev/bus/usb \</span><br><span class="line">  -v ~/docker/ros_root:/root \</span><br><span class="line">  ros:noetic-ros-base-focal \</span><br><span class="line">  bash</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">docker start -ai ros_noetic</span><br><span class="line">docker <span class="built_in">exec</span> -it ros_noetic bash</span><br></pre></td></tr></table></figure><h2 id="三、容器内基础环境">三、容器内基础环境</h2><p>下面命令都在容器内执行。</p><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></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  git wget curl vim nano \</span><br><span class="line">  net-tools iproute2 iputils-ping \</span><br><span class="line">  build-essential cmake python3-pip \</span><br><span class="line">  python3-catkin-tools python3-rosdep python3-osrf-pycommon \</span><br><span class="line">  libeigen3-dev libpcl-dev pcl-tools libopencv-dev \</span><br><span class="line">  libgoogle-glog-dev libgflags-dev libarmadillo-dev \</span><br><span class="line">  ros-noetic-cv-bridge ros-noetic-eigen-conversions \</span><br><span class="line">  ros-noetic-mavros ros-noetic-mavros-extras \</span><br><span class="line">  ros-noetic-pcl-ros ros-noetic-roslint \</span><br><span class="line">  ros-noetic-tf ros-noetic-tf2-ros \</span><br><span class="line">  ros-noetic-tf2-sensor-msgs ros-noetic-tf2-geometry-msgs \</span><br><span class="line">  ros-noetic-ddynamic-reconfigure ros-noetic-rviz</span><br></pre></td></tr></table></figure><p>初始化 rosdep：</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">rosdep init || <span class="literal">true</span></span><br><span class="line">rosdep update</span><br></pre></td></tr></table></figure><p>安装 GeographicLib 数据：</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 class="built_in">cd</span> ~</span><br><span class="line">wget https://gitee.com/tyx6/mytools/raw/main/mavros/install_geographiclib_datasets.sh</span><br><span class="line"><span class="built_in">chmod</span> +x install_geographiclib_datasets.sh</span><br><span class="line">./install_geographiclib_datasets.sh</span><br></pre></td></tr></table></figure><p>在 <code>~/.bashrc</code> 末尾添加：</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></pre></td><td class="code"><pre><span class="line">[ -f /opt/ros/noetic/setup.bash ] &amp;&amp; <span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">[ -f ~/livox_ws/devel/setup.bash ] &amp;&amp; <span class="built_in">source</span> ~/livox_ws/devel/setup.bash</span><br><span class="line">[ -f ~/catkin_ws/devel/setup.bash ] &amp;&amp; <span class="built_in">source</span> ~/catkin_ws/devel/setup.bash</span><br><span class="line"></span><br><span class="line"><span class="built_in">export</span> FCU_URL=<span class="string">&quot;serial:///dev/ttyACM0:57600&quot;</span></span><br><span class="line"><span class="built_in">export</span> GCS_URL=<span class="string">&quot;udp://:14550@10.0.30.196:14550&quot;</span></span><br><span class="line"><span class="built_in">export</span> DRONE_ID=0</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">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="四、安装-Livox-驱动">四、安装 Livox 驱动</h2><p>安装 Livox-SDK2：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~</span><br><span class="line"><span class="built_in">mkdir</span> -p ~/code</span><br><span class="line"><span class="built_in">cd</span> ~/code</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/Livox-SDK/Livox-SDK2.git</span><br><span class="line"><span class="built_in">cd</span> Livox-SDK2</span><br><span class="line"><span class="built_in">mkdir</span> -p build &amp;&amp; <span class="built_in">cd</span> build</span><br><span class="line">cmake ..</span><br><span class="line">make -j4</span><br><span class="line">make install</span><br><span class="line">ldconfig</span><br></pre></td></tr></table></figure><p>编译 <code>livox_ros_driver2</code>：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p ~/livox_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/livox_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/Livox-SDK/livox_ros_driver2.git</span><br><span class="line"><span class="built_in">cd</span> ~/livox_ws/src/livox_ros_driver2</span><br><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">./build.sh ROS1</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/devel/setup.bash</span><br></pre></td></tr></table></figure><p>配置 Mid-360S：</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> ~/livox_ws/src/livox_ros_driver2/config</span><br><span class="line">nano MID360s_config.json</span><br></pre></td></tr></table></figure><p>重点确认：</p><ul><li><code>host_ip</code>：Jetson 雷达网卡 IP</li><li><code>lidar_configs[].ip</code>：Mid-360S IP</li></ul><h2 id="五、编译-FAST-LIO、Diff-Planner-和工具节点">五、编译 FAST-LIO、Diff-Planner 和工具节点</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p ~/catkin_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/catkin_ws/src</span><br><span class="line">git <span class="built_in">clone</span> --recursive https://github.com/hku-mars/FAST_LIO.git</span><br><span class="line">git <span class="built_in">clone</span> -b px4_sitl https://github.com/dreamer198/Diff-Planner-PX4.git</span><br></pre></td></tr></table></figure><p>更新 FAST-LIO 子模块：</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> ~/catkin_ws/src/FAST_LIO</span><br><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure><p>适配 <code>livox_ros_driver2</code>：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/catkin_ws/src/FAST_LIO</span><br><span class="line">sed -i -E <span class="string">&#x27;s/livox_ros_driver([^2]|$)/livox_ros_driver2\1/g&#x27;</span> \</span><br><span class="line">  CMakeLists.txt \</span><br><span class="line">  package.xml \</span><br><span class="line">  src/laserMapping.cpp \</span><br><span class="line">  src/preprocess.cpp \</span><br><span class="line">  src/preprocess.h</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">grep -q <span class="string">&#x27;add_dependencies(fastlio_mapping&#x27;</span> CMakeLists.txt || \</span><br><span class="line">  sed -i <span class="string">&#x27;/add_executable(fastlio_mapping/a add_dependencies(fastlio_mapping $&#123;$&#123;PROJECT_NAME&#125;_EXPORTED_TARGETS&#125; $&#123;catkin_EXPORTED_TARGETS&#125;)&#x27;</span> CMakeLists.txt</span><br></pre></td></tr></table></figure><p>如果 Mid-360S 相对机体不是水平安装，建议把 <code>FAST_LIO/src/IMU_Processing.hpp</code> 初始化逻辑改成重力对齐版本：</p><figure class="highlight cpp"><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">state_ikfom init_state = kf_state.<span class="built_in">get_x</span>();</span><br><span class="line">init_state.grav = <span class="built_in">S2</span>(<span class="built_in">V3D</span>(<span class="number">0</span>, <span class="number">0</span>, -G_m_s2));</span><br><span class="line">Eigen::Quaterniond q_init = Eigen::Quaterniond::<span class="built_in">FromTwoVectors</span>(mean_acc, <span class="built_in">V3D</span>(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>));</span><br><span class="line">init_state.rot = <span class="built_in">SO3</span>(q_init.<span class="built_in">w</span>(), q_init.<span class="built_in">x</span>(), q_init.<span class="built_in">y</span>(), q_init.<span class="built_in">z</span>());</span><br><span class="line">init_state.bg  = mean_gyr;</span><br><span class="line">init_state.offset_T_L_I = Lidar_T_wrt_IMU;</span><br><span class="line">init_state.offset_R_L_I = Lidar_R_wrt_IMU;</span><br></pre></td></tr></table></figure><p>当前环境使用两个已调通脚本：</p><figure class="highlight text"><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">/root/catkin_ws/src/px4_realflight_tools/scripts/odom_to_base.py</span><br><span class="line">/root/catkin_ws/src/px4_realflight_tools/scripts/odom_to_pose.py</span><br></pre></td></tr></table></figure><p>默认安装外参：</p><figure class="highlight text"><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">MOUNT_X=0.0</span><br><span class="line">MOUNT_Y=0.0</span><br><span class="line">MOUNT_Z=0.10</span><br><span class="line">MOUNT_ROLL_DEG=0.0</span><br><span class="line">MOUNT_PITCH_DEG=30.0</span><br><span class="line">MOUNT_YAW_DEG=0.0</span><br></pre></td></tr></table></figure><p>含义：Mid-360S 相对机体前倾约 30 度，高于飞控约 10 cm。</p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/devel/setup.bash</span><br><span class="line">catkin init</span><br><span class="line">catkin config --extend ~/livox_ws/devel</span><br><span class="line">catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release</span><br><span class="line">catkin build</span><br><span class="line"><span class="built_in">source</span> ~/catkin_ws/devel/setup.bash</span><br></pre></td></tr></table></figure><h2 id="六、PX4-参数">六、PX4 参数</h2><p>在 QGroundControl 中重点确认：</p><figure class="highlight text"><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">MAV_1_CONFIG = TELEM 2</span><br><span class="line">MAV_1_MODE = Onboard</span><br><span class="line">SER_TEL2_BAUD = 921600</span><br><span class="line">EKF2_EV_CTRL = 15</span><br><span class="line">EKF2_HGT_REF = Vision</span><br><span class="line">EKF2_EV_DELAY = 0.0</span><br><span class="line">EKF2_EV_POS_X/Y/Z = 0.0</span><br></pre></td></tr></table></figure><h2 id="七、一键启动脚本">七、一键启动脚本</h2><p>Jetson 宿主机脚本路径：</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">/home/jetson2/diff-planner-px4-deployment/scripts/start_real_px4_mid360_fastlio.sh</span><br></pre></td></tr></table></figure><p>它会自动启动：</p><ul><li><code>livox_ros_driver2</code></li><li><code>fast_lio</code></li><li><code>odom_to_base.py</code></li><li><code>mavros</code></li><li><code>odom_to_pose.py</code></li><li><code>diff_planner</code></li><li><code>trajectory_msg_converter.py</code></li><li><code>se3_controller</code></li></ul><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="built_in">cd</span> /home/jetson2/diff-planner-px4-deployment</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh start</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh status</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh attach</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh stop</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh restart</span><br></pre></td></tr></table></figure><p>现在的 <code>restart</code> 会先执行完整 <code>stop</code>，尽量清理 tmux 会话和容器残留进程，再重新启动整条链路。</p><h3 id="当前真机实测可用参数">当前真机实测可用参数</h3><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">FCU_URL=<span class="string">&#x27;serial:///dev/ttyACM0:57600&#x27;</span> \</span><br><span class="line">GCS_URL=<span class="string">&#x27;udp://:14550@10.0.30.196:14550&#x27;</span> \</span><br><span class="line">SE3_HOVER_PERCENT=0.90 \</span><br><span class="line">SE3_MAX_HOVER_PERCENT=0.95 \</span><br><span class="line">SE3_MAX_OUTPUT_THRUST=1.00 \</span><br><span class="line">SE3_ENABLE_THRUST_ESTIMATION=<span class="literal">false</span> \</span><br><span class="line">SE3_MAX_FEEDFORWARD_ACC=1.2 \</span><br><span class="line">DIFF_PLANNER_INFLATION_SIZE=0.2 \</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh restart</span><br></pre></td></tr></table></figure><p>如果切 Offboard 仍掉高，优先微调：</p><ul><li><code>SE3_HOVER_PERCENT</code>：每次调 <code>0.02 ~ 0.03</code></li><li><code>DIFF_PLANNER_INFLATION_SIZE</code></li></ul><p>如果只想测试定位和 PX4 外部视觉融合：</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">START_DIFF_PLANNER=<span class="literal">false</span> \</span><br><span class="line">./scripts/start_real_px4_mid360_fastlio.sh restart</span><br></pre></td></tr></table></figure><h2 id="八、快速检查">八、快速检查</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br><span class="line">rostopic <span class="built_in">type</span> /livox/lidar</span><br><span class="line">rostopic hz /Odometry</span><br><span class="line">rostopic hz /Odometry_base</span><br><span class="line">rostopic <span class="built_in">echo</span> -n 1 /mavros/state</span><br><span class="line">rostopic hz /mavros/vision_pose/pose</span><br><span class="line">rostopic hz /drone_0_planning/pos_cmd</span><br><span class="line">rostopic hz /mavros/setpoint_raw/attitude</span><br></pre></td></tr></table></figure><p>只要满足下面几点，链路通常就已经通了：</p><ul><li><code>/livox/lidar</code> 类型为 <code>livox_ros_driver2/CustomMsg</code></li><li><code>/Odometry</code>、<code>/Odometry_base</code>、<code>/mavros/vision_pose/pose</code> 持续输出</li><li><code>/mavros/state</code> 中 <code>connected: True</code></li><li><code>/drone_0_planning/pos_cmd</code> 和 <code>/mavros/setpoint_raw/attitude</code> 在发目标后有输出</li></ul><p>飞行原则也保持最简单：</p><ul><li>脚本不会自动解锁</li><li>脚本不会自动切 Offboard</li><li>先用遥控器起飞并悬停，再发目标点，再手动切 Offboard</li><li>飞行中可随时切回 Position 接管</li></ul><h2 id="九、常见问题">九、常见问题</h2><ul><li>容器里没有 <code>/dev/ttyACM0</code>：重建容器时确认加入 <code>--device=/dev/ttyACM0</code></li><li>MAVROS 未连接：检查 <code>/dev/ttyACM0</code>、<code>FCU_URL</code>、<code>/mavros/state</code></li><li>视觉位姿有数据但 PX4 不融合：检查 <code>EKF2_EV_CTRL</code>、<code>EKF2_HGT_REF</code> 和 QGC EKF 告警</li><li>切 Offboard 立刻退出：检查 <code>/mavros/setpoint_raw/attitude</code> 是否持续发布，并确认 <code>COM_OF_LOSS_T</code>、<code>COM_OBL_RC_ACT</code></li><li>Offboard 掉高：优先微调 <code>SE3_HOVER_PERCENT</code>、<code>SE3_MAX_HOVER_PERCENT</code>、<code>SE3_MAX_OUTPUT_THRUST</code></li></ul><h2 id="参考资料">参考资料</h2><ul><li>PX4 外部视觉定位文档：<a href="https://docs.px4.io/main/en/ros/external_position_estimation.html">https://docs.px4.io/main/en/ros/external_position_estimation.html</a></li><li>PX4 Offboard 模式文档：<a href="https://docs.px4.io/main/en/flight_modes/offboard.html">https://docs.px4.io/main/en/flight_modes/offboard.html</a></li><li>MAVROS 文档：<a href="https://mavros.readthedocs.io/en/latest/">https://mavros.readthedocs.io/en/latest/</a></li><li>本文 Dockerfile 部署仓库：<a href="https://github.com/dreamer198/diff-planner-px4-deployment">https://github.com/dreamer198/diff-planner-px4-deployment</a></li><li>Livox ROS Driver 2：<a href="https://github.com/Livox-SDK/livox_ros_driver2">https://github.com/Livox-SDK/livox_ros_driver2</a></li><li>FAST-LIO：<a href="https://github.com/hku-mars/FAST_LIO">https://github.com/hku-mars/FAST_LIO</a></li><li>Diff-Planner-PX4：<a href="https://github.com/dreamer198/Diff-Planner-PX4">https://github.com/dreamer198/Diff-Planner-PX4</a></li></ul>]]></content>
    
    
    <summary type="html">前言 本文记录在 Jetson 上通过 Docker 跑通 ROS1 真机链路： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Mid-360S -&amp;gt; livoxrosdriver2 -&amp;gt; FAST-LIO -&amp;gt; /Odometry + /clo…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Jetson" scheme="https://dreamer198.top/tags/Jetson/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="MAVROS" scheme="https://dreamer198.top/tags/MAVROS/"/>
    
    <category term="Docker" scheme="https://dreamer198.top/tags/Docker/"/>
    
    <category term="ROS1" scheme="https://dreamer198.top/tags/ROS1/"/>
    
    <category term="Diff-Planner" scheme="https://dreamer198.top/tags/Diff-Planner/"/>
    
    <category term="FAST-LIO" scheme="https://dreamer198.top/tags/FAST-LIO/"/>
    
  </entry>
  
  <entry>
    <title>Docker-ROS1-DiffPlanner-PX4仿真环境搭建</title>
    <link href="https://dreamer198.top/2026/05/28/Docker-ROS1-DiffPlanner-PX4%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
    <id>https://dreamer198.top/2026/05/28/Docker-ROS1-DiffPlanner-PX4%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</id>
    <published>2026-05-28T02:00:00.000Z</published>
    <updated>2026-05-29T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文记录在 Docker 容器中搭建 PX4 SITL + ROS1 + Diff-Planner 的流程，并分别跑通深度相机和 Mid360 两条仿真链路。</p><p>如果还没有创建 ROS Noetic 容器，可以先参考《Docker使用教程》。</p><p>本文默认在容器内 root 用户下操作，<code>~</code> 即 <code>/root</code>。</p><span id="more"></span><h2 id="一、环境说明">一、环境说明</h2><ul><li>Docker 容器：<code>ros_noetic</code></li><li>ROS：Noetic</li><li>PX4：<code>v1.14.3</code></li><li>仿真：Gazebo classic</li><li>PX4 目录：<code>~/PX4-Autopilot</code></li><li>Catkin 工作空间：<code>~/catkin_ws</code></li></ul><p>整体顺序：</p><ol><li>安装并编译 PX4 SITL</li><li>配置 ROS、Gazebo 和 MAVROS</li><li>编译 Diff-Planner-PX4</li><li>分别启动深度相机和 Mid360 仿真</li></ol><h2 id="二、PX4-SITL-基础环境">二、PX4 SITL 基础环境</h2><h3 id="1-下载-PX4">1. 下载 PX4</h3><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">apt update</span><br><span class="line">apt install -y git</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> ~</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/PX4/PX4-Autopilot.git</span><br><span class="line"><span class="built_in">cd</span> PX4-Autopilot</span><br><span class="line">git checkout -b dev v1.14.3</span><br><span class="line">git submodule update --init --recursive</span><br></pre></td></tr></table></figure><h3 id="2-安装-PX4-依赖">2. 安装 PX4 依赖</h3><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">cd</span> ~/PX4-Autopilot/Tools/setup</span><br><span class="line"><span class="built_in">chmod</span> +x ubuntu.sh</span><br><span class="line">./ubuntu.sh --no-nuttx --no-sim-tools</span><br></pre></td></tr></table></figure><p>这个脚本会补齐 PX4 基础依赖，首次执行会比较久。</p><p>如果出现 <code>pandas</code> 和 <code>numpy</code> 版本冲突，例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ERROR: pandas 2.0.3 has requirement numpy&gt;=1.20.3; python_version &lt; &quot;3.10&quot;, but you&#x27;ll have numpy 1.17.4 which is incompatible.</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><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="built_in">cd</span> ~/PX4-Autopilot/Tools/setup</span><br><span class="line">python3 -m pip uninstall -y pandas numpy</span><br><span class="line"></span><br><span class="line">apt update</span><br><span class="line">apt install -y python3-numpy python3-pandas</span><br><span class="line"></span><br><span class="line">./ubuntu.sh --no-nuttx --no-sim-tools</span><br></pre></td></tr></table></figure><h3 id="3-安装-Gazebo-相关依赖">3. 安装 Gazebo 相关依赖</h3><p>下载 Gazebo 模型：</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 class="built_in">mkdir</span> -p ~/.gazebo/models</span><br><span class="line"><span class="built_in">cd</span> ~</span><br><span class="line">git <span class="built_in">clone</span> https://gitee.com/tyx6/gazebo_models.git</span><br><span class="line"><span class="built_in">mv</span> ./gazebo_models/* ~/.gazebo/models/</span><br></pre></td></tr></table></figure><p>因为前面使用了 <code>--no-sim-tools</code>，后续编译 Gazebo classic 前需要补充 GStreamer 依赖：</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></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  pkg-config \</span><br><span class="line">  libgstreamer1.0-dev \</span><br><span class="line">  libgstreamer-plugins-base1.0-dev \</span><br><span class="line">  gstreamer1.0-plugins-base \</span><br><span class="line">  gstreamer1.0-plugins-good</span><br></pre></td></tr></table></figure><h3 id="4-编译-PX4">4. 编译 PX4</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">cd</span> ~/PX4-Autopilot</span><br><span class="line">make px4_sitl_default gazebo</span><br></pre></td></tr></table></figure><p>如果编译时报 <code>GSTREAMER_APP_LIBRARIES</code> 或 <code>GSTREAMER_LIBRARIES</code> 为 <code>NOTFOUND</code>，安装上面的 GStreamer 依赖后清理 Gazebo classic 缓存，再重新编译：</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="built_in">cd</span> ~/PX4-Autopilot</span><br><span class="line"></span><br><span class="line"><span class="built_in">rm</span> -rf build/px4_sitl_default/build_gazebo-classic</span><br><span class="line"><span class="built_in">rm</span> -rf build/px4_sitl_default/external/Stamp/sitl_gazebo-classic</span><br><span class="line"></span><br><span class="line">make px4_sitl_default gazebo</span><br></pre></td></tr></table></figure><h2 id="三、ROS-环境和-MAVROS">三、ROS 环境和 MAVROS</h2><h3 id="1-写入环境变量">1. 写入环境变量</h3><p>建议把环境变量写进 <code>~/.bashrc</code>。如果容器的 <code>/root</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">nano ~/docker/ros_root/.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><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="comment"># ROS Noetic</span></span><br><span class="line">[ -f /opt/ros/noetic/setup.bash ] &amp;&amp; <span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># Catkin workspace</span></span><br><span class="line">[ -f ~/catkin_ws/devel/setup.bash ] &amp;&amp; <span class="built_in">source</span> ~/catkin_ws/devel/setup.bash</span><br><span class="line"></span><br><span class="line"><span class="comment"># PX4 Gazebo classic</span></span><br><span class="line"><span class="keyword">if</span> [ -f ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">source</span> ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ~/PX4-Autopilot/ ~/PX4-Autopilot/build/px4_sitl_default &gt; /dev/null</span><br><span class="line"><span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># PX4 ROS package path</span></span><br><span class="line"><span class="built_in">export</span> ROS_PACKAGE_PATH=<span class="variable">$ROS_PACKAGE_PATH</span>:~/PX4-Autopilot/</span><br><span class="line"><span class="built_in">export</span> ROS_PACKAGE_PATH=<span class="variable">$ROS_PACKAGE_PATH</span>:~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic</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">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="2-安装-MAVROS">2. 安装 MAVROS</h3><p>PX4 通过 MAVROS 和 ROS 通信。容器内默认是 root 用户，不需要加 <code>sudo</code>：</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></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  ros-noetic-mavros \</span><br><span class="line">  ros-noetic-mavros-extras \</span><br><span class="line">  wget</span><br></pre></td></tr></table></figure><p>安装 GeographicLib datasets：</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 class="built_in">cd</span> ~</span><br><span class="line">wget https://gitee.com/tyx6/mytools/raw/main/mavros/install_geographiclib_datasets.sh</span><br><span class="line"><span class="built_in">chmod</span> a+x ./install_geographiclib_datasets.sh</span><br><span class="line">./install_geographiclib_datasets.sh</span><br></pre></td></tr></table></figure><p>这个脚本会下载地理坐标数据集，可能需要等待一段时间。</p><h3 id="3-验证-PX4-和-MAVROS">3. 验证 PX4 和 MAVROS</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">roslaunch px4 mavros_posix_sitl.launch</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">rostopic <span class="built_in">echo</span> /mavros/state | grep connected</span><br></pre></td></tr></table></figure><p>看到 <code>connected: True</code> 后，再继续后面的步骤。</p><h2 id="四、Diff-Planner-PX4-集成">四、Diff-Planner-PX4 集成</h2><h3 id="1-安装依赖">1. 安装依赖</h3><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></pre></td><td class="code"><pre><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  python3-catkin-tools \</span><br><span class="line">  python3-rosinstall-generator \</span><br><span class="line">  python3-osrf-pycommon \</span><br><span class="line">  libgoogle-glog-dev \</span><br><span class="line">  libgflags-dev \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  libarmadillo-dev \</span><br><span class="line">  ros-noetic-pcl-ros \</span><br><span class="line">  ros-noetic-tf2-geometry-msgs \</span><br><span class="line">  ros-noetic-laser-geometry \</span><br><span class="line">  ros-noetic-tf2-sensor-msgs</span><br></pre></td></tr></table></figure><h3 id="2-下载并编译">2. 下载并编译</h3><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">mkdir</span> -p ~/catkin_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/catkin_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/dreamer198/Diff-Planner-PX4.git</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">catkin init</span><br><span class="line">catkin build</span><br></pre></td></tr></table></figure><p>如果工作空间已经初始化过，直接执行 <code>catkin build</code> 即可。</p><h3 id="3-复制仿真配置">3. 复制仿真配置</h3><p>把 Diff-Planner-PX4 中的模型、世界文件和 launch 文件复制到 PX4 工程：</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="comment"># model and world</span></span><br><span class="line"><span class="built_in">cp</span> -r ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/models/* ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/</span><br><span class="line"><span class="built_in">cp</span> ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/worlds/* ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/</span><br><span class="line"></span><br><span class="line"><span class="comment"># launch</span></span><br><span class="line"><span class="built_in">cp</span> ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/outdoor_depth_camera.launch ~/PX4-Autopilot/launch/</span><br><span class="line"><span class="built_in">cp</span> ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/outdoor_mid360.launch ~/PX4-Autopilot/launch/</span><br><span class="line"><span class="built_in">cp</span> ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/px4_config.yaml ~/PX4-Autopilot/launch/</span><br></pre></td></tr></table></figure><p>如果 PX4 目录中已有同名文件，复制前可以先备份。</p><h2 id="五、深度相机仿真">五、深度相机仿真</h2><p>这一条链路需要三个终端：Gazebo、<code>se3_controller</code>、<code>diff_planner</code>。</p><h3 id="终端一：启动-Gazebo">终端一：启动 Gazebo</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">roslaunch px4 outdoor_depth_camera.launch</span><br></pre></td></tr></table></figure><h3 id="终端二：启动控制器">终端二：启动控制器</h3><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">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">roslaunch se3_controller sitl_se3_controller.launch</span><br></pre></td></tr></table></figure><p>控制器启动后，通常会自动进入 offboard 模式、解锁，并起飞到 2 m。</p><h3 id="终端三：启动-Diff-Planner">终端三：启动 Diff-Planner</h3><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">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">roslaunch diff_planner run_px4_sitl_gazebo.launch</span><br></pre></td></tr></table></figure><h2 id="六、Mid360-仿真">六、Mid360 仿真</h2><p>Mid360 需要额外编译 Livox Gazebo 插件，并把插件和模型复制到 PX4。</p><h3 id="1-下载并编译插件">1. 下载并编译插件</h3><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="built_in">cd</span> ~/catkin_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/Tfly6/Mid360_px4_sim_plugin.git</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">catkin build</span><br></pre></td></tr></table></figure><h3 id="2-测试插件">2. 测试插件</h3><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">source</span> ~/catkin_ws/devel/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash</span><br><span class="line">roslaunch livox_laser_simulation test_pattern.launch</span><br></pre></td></tr></table></figure><p>如果能正常看到点云，说明插件加载成功。</p><h3 id="3-复制-Mid360-配置">3. 复制 Mid360 配置</h3><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">cp</span> ./src/Mid360_px4_sim_plugin/livox_laser_simulation/launch/mavros_posix_sitl_mid360.launch ~/PX4-Autopilot/launch/</span><br><span class="line"><span class="built_in">cp</span> ./devel/lib/liblivox_laser_simulation.so ~/PX4-Autopilot/build/px4_sitl_default/build_gazebo-classic/</span><br><span class="line"><span class="built_in">cp</span> -r ./src/Mid360_px4_sim_plugin/livox_laser_simulation/models/Mid360 ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/</span><br><span class="line"><span class="built_in">cp</span> -r ./src/Mid360_px4_sim_plugin/livox_laser_simulation/models/iris_mid360 ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/</span><br></pre></td></tr></table></figure><h3 id="终端一：启动-Gazebo-2">终端一：启动 Gazebo</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">roslaunch px4 outdoor_mid360.launch</span><br></pre></td></tr></table></figure><h3 id="终端二：启动控制器-2">终端二：启动控制器</h3><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">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">roslaunch se3_controller sitl_se3_controller.launch</span><br></pre></td></tr></table></figure><h3 id="终端三：启动-Diff-Planner-2">终端三：启动 Diff-Planner</h3><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">cd</span> ~/catkin_ws</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">roslaunch diff_planner run_px4_sitl_gazebo_mid360.launch</span><br></pre></td></tr></table></figure><h2 id="七、宿主机一键启动仿真">七、宿主机一键启动仿真</h2><p>上面的三条命令都在容器内执行。如果想从宿主机一键拉起仿真，可以用 <code>tmux + docker exec</code> 管理三个终端。</p><p>先在宿主机安装 <code>tmux</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> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y tmux</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"><span class="built_in">mkdir</span> -p ~/code</span><br><span class="line">nano ~/code/start_sim_diff_planner_px4.sh</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><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><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/usr/bin/env bash</span></span><br><span class="line"><span class="built_in">set</span> -euo pipefail</span><br><span class="line"></span><br><span class="line">CONTAINER=<span class="string">&quot;<span class="variable">$&#123;CONTAINER:-ros_noetic&#125;</span>&quot;</span></span><br><span class="line">SESSION=<span class="string">&quot;<span class="variable">$&#123;SESSION:-diff_px4_sitl&#125;</span>&quot;</span></span><br><span class="line">WAIT_GAZEBO=<span class="string">&quot;<span class="variable">$&#123;WAIT_GAZEBO:-12&#125;</span>&quot;</span></span><br><span class="line">WAIT_CONTROLLER=<span class="string">&quot;<span class="variable">$&#123;WAIT_CONTROLLER:-5&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line">ACTION=<span class="string">&quot;<span class="variable">$&#123;1:-start&#125;</span>&quot;</span></span><br><span class="line">MODE=<span class="string">&quot;<span class="variable">$&#123;2:-<span class="variable">$&#123;MODE:-mid360&#125;</span>&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">usage</span></span>() &#123;</span><br><span class="line">  <span class="built_in">cat</span> &lt;&lt;<span class="string">EOF</span></span><br><span class="line"><span class="string">Usage:</span></span><br><span class="line"><span class="string">  $0 start [mid360|depth]</span></span><br><span class="line"><span class="string">  $0 restart [mid360|depth]</span></span><br><span class="line"><span class="string">  $0 stop</span></span><br><span class="line"><span class="string">  $0 status</span></span><br><span class="line"><span class="string">  $0 attach</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Environment:</span></span><br><span class="line"><span class="string">  CONTAINER=ros_noetic</span></span><br><span class="line"><span class="string">  SESSION=diff_px4_sitl</span></span><br><span class="line"><span class="string">  WAIT_GAZEBO=12</span></span><br><span class="line"><span class="string">  WAIT_CONTROLLER=5</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">need_tmux</span></span>() &#123;</span><br><span class="line">  <span class="keyword">if</span> ! <span class="built_in">command</span> -v tmux &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;tmux not found. Install it first: sudo apt install -y tmux&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">require_container</span></span>() &#123;</span><br><span class="line">  <span class="keyword">if</span> ! docker inspect <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;Docker container not found: <span class="variable">$CONTAINER</span>&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">ensure_container_running</span></span>() &#123;</span><br><span class="line">  <span class="built_in">local</span> running</span><br><span class="line">  running=<span class="string">&quot;<span class="subst">$(docker inspect -f &#x27;&#123;&#123;.State.Running&#125;&#125;&#x27; <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">  <span class="keyword">if</span> [ <span class="string">&quot;<span class="variable">$running</span>&quot;</span> != <span class="string">&quot;true&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    docker start <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> &gt;/dev/null</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">allow_x11</span></span>() &#123;</span><br><span class="line">  <span class="keyword">if</span> [ -n <span class="string">&quot;<span class="variable">$&#123;DISPLAY:-&#125;</span>&quot;</span> ] &amp;&amp; <span class="built_in">command</span> -v xhost &gt;/dev/null 2&gt;&amp;1; <span class="keyword">then</span></span><br><span class="line">    xhost +SI:localuser:root &gt;/dev/null 2&gt;&amp;1 || <span class="literal">true</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">quote</span></span>() &#123;</span><br><span class="line">  <span class="built_in">printf</span> <span class="string">&#x27;%q&#x27;</span> <span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">docker_exec_cmd</span></span>() &#123;</span><br><span class="line">  <span class="built_in">local</span> cmd=<span class="string">&quot;<span class="variable">$1</span>&quot;</span></span><br><span class="line">  <span class="built_in">printf</span> <span class="string">&#x27;docker exec -it %q bash -lc %s&#x27;</span> <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> <span class="string">&quot;<span class="subst">$(quote <span class="string">&quot;<span class="variable">$cmd</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">build_commands</span></span>() &#123;</span><br><span class="line">  <span class="built_in">local</span> gazebo_launch planner_launch extra_setup</span><br><span class="line"></span><br><span class="line">  <span class="keyword">case</span> <span class="string">&quot;<span class="variable">$MODE</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">    mid360)</span><br><span class="line">      gazebo_launch=<span class="string">&quot;outdoor_mid360.launch&quot;</span></span><br><span class="line">      planner_launch=<span class="string">&quot;run_px4_sitl_gazebo_mid360.launch&quot;</span></span><br><span class="line">      extra_setup=<span class="string">&#x27;[ -f ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash ] &amp;&amp; source ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash&#x27;</span></span><br><span class="line">      ;;</span><br><span class="line">    depth|depth_camera)</span><br><span class="line">      MODE=<span class="string">&quot;depth&quot;</span></span><br><span class="line">      gazebo_launch=<span class="string">&quot;outdoor_depth_camera.launch&quot;</span></span><br><span class="line">      planner_launch=<span class="string">&quot;run_px4_sitl_gazebo.launch&quot;</span></span><br><span class="line">      extra_setup=<span class="string">&#x27;true&#x27;</span></span><br><span class="line">      ;;</span><br><span class="line">    *)</span><br><span class="line">      usage</span><br><span class="line">      <span class="built_in">exit</span> 1</span><br><span class="line">      ;;</span><br><span class="line">  <span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line">  <span class="comment"># shellcheck disable=SC2016</span></span><br><span class="line">  BASE_SETUP=<span class="string">&#x27;source /opt/ros/noetic/setup.bash; [ -f ~/catkin_ws/devel/setup.bash ] &amp;&amp; source ~/catkin_ws/devel/setup.bash; if [ -f ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ]; then source ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ~/PX4-Autopilot ~/PX4-Autopilot/build/px4_sitl_default &gt;/dev/null; fi; export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:~/PX4-Autopilot:~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic&#x27;</span></span><br><span class="line"></span><br><span class="line">  GAZEBO_CMD=<span class="string">&quot;<span class="variable">$BASE_SETUP</span>; <span class="variable">$extra_setup</span>; roslaunch px4 <span class="variable">$gazebo_launch</span>; exec bash&quot;</span></span><br><span class="line">  CONTROLLER_CMD=<span class="string">&quot;<span class="variable">$BASE_SETUP</span>; cd ~/catkin_ws; roslaunch se3_controller sitl_se3_controller.launch; exec bash&quot;</span></span><br><span class="line">  PLANNER_CMD=<span class="string">&quot;<span class="variable">$BASE_SETUP</span>; cd ~/catkin_ws; roslaunch diff_planner <span class="variable">$planner_launch</span>; exec bash&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">start_stack</span></span>() &#123;</span><br><span class="line">  need_tmux</span><br><span class="line">  require_container</span><br><span class="line">  ensure_container_running</span><br><span class="line">  allow_x11</span><br><span class="line">  build_commands</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> tmux has-session -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> 2&gt;/dev/null; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;tmux session already exists: <span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;Use &#x27;<span class="variable">$0</span> attach&#x27; to view it, or &#x27;<span class="variable">$0</span> restart <span class="variable">$MODE</span>&#x27; to restart.&quot;</span></span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  tmux new-session -d -s <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> -n gazebo <span class="string">&quot;<span class="subst">$(docker_exec_cmd <span class="string">&quot;<span class="variable">$GAZEBO_CMD</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">  <span class="built_in">sleep</span> <span class="string">&quot;<span class="variable">$WAIT_GAZEBO</span>&quot;</span></span><br><span class="line"></span><br><span class="line">  tmux new-window -t <span class="string">&quot;<span class="variable">$SESSION</span>:&quot;</span> -n controller <span class="string">&quot;<span class="subst">$(docker_exec_cmd <span class="string">&quot;<span class="variable">$CONTROLLER_CMD</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">  <span class="built_in">sleep</span> <span class="string">&quot;<span class="variable">$WAIT_CONTROLLER</span>&quot;</span></span><br><span class="line"></span><br><span class="line">  tmux new-window -t <span class="string">&quot;<span class="variable">$SESSION</span>:&quot;</span> -n diff_planner <span class="string">&quot;<span class="subst">$(docker_exec_cmd <span class="string">&quot;<span class="variable">$PLANNER_CMD</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">  tmux select-window -t <span class="string">&quot;<span class="variable">$SESSION</span>:gazebo&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;Started <span class="variable">$MODE</span> simulation in tmux session: <span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;Attach: <span class="variable">$0</span> attach&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">stop_stack</span></span>() &#123;</span><br><span class="line">  <span class="keyword">if</span> <span class="built_in">command</span> -v tmux &gt;/dev/null 2&gt;&amp;1 &amp;&amp; tmux has-session -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> 2&gt;/dev/null; <span class="keyword">then</span></span><br><span class="line">    tmux kill-session -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> docker inspect <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> &gt;/dev/null 2&gt;&amp;1 \</span><br><span class="line">    &amp;&amp; [ <span class="string">&quot;<span class="subst">$(docker inspect -f &#x27;&#123;&#123;.State.Running&#125;&#125;&#x27; <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> 2&gt;/dev/null)</span>&quot;</span> = <span class="string">&quot;true&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">    docker <span class="built_in">exec</span> <span class="string">&quot;<span class="variable">$CONTAINER</span>&quot;</span> bash -lc <span class="string">&#x27;</span></span><br><span class="line"><span class="string">      pkill -INT -f &quot;[r]oslaunch px4 outdoor_.*launch&quot; || true</span></span><br><span class="line"><span class="string">      pkill -INT -f &quot;[r]oslaunch se3_controller sitl_se3_controller.launch&quot; || true</span></span><br><span class="line"><span class="string">      pkill -INT -f &quot;[r]oslaunch diff_planner run_px4_sitl_gazebo&quot; || true</span></span><br><span class="line"><span class="string">      sleep 2</span></span><br><span class="line"><span class="string">      pkill -TERM -f &quot;[g]zserver|[g]zclient|[p]x4|[r]osmaster|[r]osout&quot; || true</span></span><br><span class="line"><span class="string">    &#x27;</span> &gt;/dev/null 2&gt;&amp;1 || <span class="literal">true</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;Stopped tmux session and simulation processes.&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">status_stack</span></span>() &#123;</span><br><span class="line">  <span class="keyword">if</span> <span class="built_in">command</span> -v tmux &gt;/dev/null 2&gt;&amp;1 &amp;&amp; tmux has-session -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span> 2&gt;/dev/null; <span class="keyword">then</span></span><br><span class="line">    tmux list-windows -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;tmux session not running: <span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  docker ps --filter <span class="string">&quot;name=<span class="variable">$CONTAINER</span>&quot;</span> --format <span class="string">&quot;container=&#123;&#123;.Names&#125;&#125; status=&#123;&#123;.Status&#125;&#125;&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">attach_stack</span></span>() &#123;</span><br><span class="line">  need_tmux</span><br><span class="line">  tmux attach -t <span class="string">&quot;<span class="variable">$SESSION</span>&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">&quot;<span class="variable">$ACTION</span>&quot;</span> <span class="keyword">in</span></span><br><span class="line">  start)</span><br><span class="line">    start_stack</span><br><span class="line">    ;;</span><br><span class="line">  restart)</span><br><span class="line">    stop_stack</span><br><span class="line">    start_stack</span><br><span class="line">    ;;</span><br><span class="line">  stop)</span><br><span class="line">    stop_stack</span><br><span class="line">    ;;</span><br><span class="line">  status)</span><br><span class="line">    status_stack</span><br><span class="line">    ;;</span><br><span class="line">  attach)</span><br><span class="line">    attach_stack</span><br><span class="line">    ;;</span><br><span class="line">  -h|--<span class="built_in">help</span>|<span class="built_in">help</span>)</span><br><span class="line">    usage</span><br><span class="line">    ;;</span><br><span class="line">  *)</span><br><span class="line">    usage</span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line">    ;;</span><br><span class="line"><span class="keyword">esac</span></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">chmod</span> +x ~/code/start_sim_diff_planner_px4.sh</span><br></pre></td></tr></table></figure><p>启动 Mid360 仿真：</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">~/code/start_sim_diff_planner_px4.sh start mid360</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">~/code/start_sim_diff_planner_px4.sh start depth</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">~/code/start_sim_diff_planner_px4.sh status</span><br><span class="line">~/code/start_sim_diff_planner_px4.sh attach</span><br><span class="line">~/code/start_sim_diff_planner_px4.sh stop</span><br><span class="line">~/code/start_sim_diff_planner_px4.sh restart mid360</span><br></pre></td></tr></table></figure><p><code>attach</code> 后会进入 tmux 会话，三个窗口分别是：</p><figure class="highlight text"><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">gazebo</span><br><span class="line">controller</span><br><span class="line">diff_planner</span><br></pre></td></tr></table></figure><p>tmux 常用快捷键：</p><figure class="highlight text"><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">Ctrl-b n    切到下一个窗口</span><br><span class="line">Ctrl-b p    切到上一个窗口</span><br><span class="line">Ctrl-b d    退出 tmux 会话但不停止仿真</span><br></pre></td></tr></table></figure><p>如果 Gazebo 窗口没有显示，先确认创建容器时已经挂载 X11，并且宿主机执行过：</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><h2 id="八、常见问题">八、常见问题</h2><h3 id="1-roslaunch-找不到-package">1. <code>roslaunch</code> 找不到 package</h3><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"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/catkin_ws/devel/setup.bash</span><br></pre></td></tr></table></figure><p>如果已经写入 <code>~/.bashrc</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">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="2-Gazebo-找不到模型">2. Gazebo 找不到模型</h3><p>检查模型是否已经复制到：</p><figure class="highlight text"><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">~/.gazebo/models/</span><br><span class="line">~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/</span><br></pre></td></tr></table></figure><h3 id="3-Mid360-插件没加载出来">3. Mid360 插件没加载出来</h3><p>重点检查：</p><ol><li>是否执行过 <code>source ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash</code></li><li><code>liblivox_laser_simulation.so</code> 是否已经复制到 <code>~/PX4-Autopilot/build/px4_sitl_default/build_gazebo-classic/</code></li><li><code>Mid360</code> 和 <code>iris_mid360</code> 模型是否已经复制到 PX4 的 Gazebo models 目录</li></ol><h3 id="4-出现-obs-Land-enabled">4. 出现 <code>obs Land enabled</code></h3><p>这通常是地理围栏触发，不一定是程序错误。先确认仿真流程是否还能继续，再决定是否调整参数。</p><h2 id="总结">总结</h2><p>这套流程的关键是先跑通 PX4 SITL + MAVROS，再接入 Diff-Planner。深度相机和 Mid360 的差异主要在 Gazebo 模型、插件和对应的 launch 文件。</p>]]></content>
    
    
    <summary type="html">前言 本文记录在 Docker 容器中搭建 PX4 SITL + ROS1 + Diff-Planner 的流程，并分别跑通深度相机和 Mid360 两条仿真链路。 如果还没有创建 ROS Noetic 容器，可以先参考《Docker使用教程》。 本文默认在容器内 root 用户下操作， 即 /root。…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="Docker" scheme="https://dreamer198.top/tags/Docker/"/>
    
    <category term="ROS1" scheme="https://dreamer198.top/tags/ROS1/"/>
    
    <category term="Gazebo" scheme="https://dreamer198.top/tags/Gazebo/"/>
    
    <category term="Diff-Planner" scheme="https://dreamer198.top/tags/Diff-Planner/"/>
    
  </entry>
  
  <entry>
    <title>无人机路径规划开源算法总结</title>
    <link href="https://dreamer198.top/2026/05/27/%E6%97%A0%E4%BA%BA%E6%9C%BA%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92%E5%BC%80%E6%BA%90%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93/"/>
    <id>https://dreamer198.top/2026/05/27/%E6%97%A0%E4%BA%BA%E6%9C%BA%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92%E5%BC%80%E6%BA%90%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93/</id>
    <published>2026-05-27T07:00:00.000Z</published>
    <updated>2026-08-06T14:21:44.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文依据我的 <a href="https://github.com/dreamer198/awesome-uav-planning">Awesome UAV Planning</a> 论文与阅读笔记仓库重新整理。当前基线为 2026-08-06 的 <a href="https://github.com/dreamer198/awesome-uav-planning/tree/e8ea156727b669d9baf48a5d3104a58dc78a9037">e8ea156</a>，共收录 <strong>63 个方法、数据集或平台</strong>，按照它们主要解决的问题分为 9 类。</p><p>表格中的方法名链接到对应中文阅读笔记，最后一列链接到官方代码、项目页、论文或数据。需要特别注意：<strong>有论文、项目页或 GitHub 仓库，不等于论文完整方法已经开源，也不等于能够一键复现实验。</strong></p><h2 id="开源状态怎么看">开源状态怎么看</h2><table><thead><tr><th>标记</th><th>含义</th></tr></thead><tbody><tr><td>代码 / 核心代码</td><td>已公开论文相关实现，但仍需核对权重、数据、配置和部署资产</td></tr><tr><td>部分开源</td><td>仅公开部分模块或相关子系统</td></tr><tr><td>上游代码 / 基础仿真器</td><td>只公开依赖或研究底座，不代表论文新增方法已经发布</td></tr><tr><td>仓库 / 占位仓库 / 项目页</td><td>可能只有说明、演示或发布预告，不能据此认定代码已经开源</td></tr><tr><td>未开源 / 未确认</td><td>官方明确尚未发布，或截至核验日期尚未确认官方方法实现</td></tr></tbody></table><blockquote><p>截至 2026-08-06，YOPOv2-Tracker 的 Tracker 代码与权重尚未发布；MAD 和 AeroAct 的论文专用实现尚未确认开源。FlightBench 与 Drive-OccWorld 已公开核心代码，但都不是包含全部资产、权重和配置的一键复现包。</p></blockquote><h2 id="几个容易混淆的概念">几个容易混淆的概念</h2><table><thead><tr><th>概念</th><th>主要回答的问题</th><th>常见输出</th></tr></thead><tbody><tr><td>路径规划</td><td>从哪里走</td><td>离散路径、几何路径</td></tr><tr><td>运动规划</td><td>在动力学约束下能否这样飞</td><td>状态序列、动作序列</td></tr><tr><td>轨迹规划</td><td>如何连续、平滑地飞</td><td>多项式、B-spline、MINCO 轨迹</td></tr><tr><td>在线重规划</td><td>环境变化后如何快速修正</td><td>滚动更新的局部轨迹</td></tr><tr><td>探索 / 覆盖</td><td>目标尚未确定时，下一步去哪里看</td><td>Frontier、视点、扫描航线</td></tr><tr><td>多机规划</td><td>多架无人机如何分工并避免冲突</td><td>多机轨迹、任务分配</td></tr><tr><td>学习型规划</td><td>网络替代或增强规划链路的哪一部分</td><td>动作、航点、轨迹、代价或初值</td></tr><tr><td>世界 / 动作模型</td><td>动作会让环境和自身状态怎样变化</td><td>未来占据、潜在状态、未来视觉或动作块</td></tr></tbody></table><h2 id="分类总览">分类总览</h2><table><thead><tr><th>类别</th><th style="text-align:right">数量</th><th>核心问题</th><th>代表方法</th></tr></thead><tbody><tr><td>单机局部轨迹规划与重规划</td><td style="text-align:right">9</td><td>实时生成安全、平滑、动力学可行的局部轨迹</td><td>Fast-Planner、EGO-Planner、GCOPTER、SUPER</td></tr><tr><td>动态环境与感知约束安全规划</td><td style="text-align:right">6</td><td>处理动态障碍、有限视野、定位退化和跟踪扰动</td><td>PANTHER、Intent-MPC、APACE、SIMP</td></tr><tr><td>多机安全集群与编队轨迹规划</td><td style="text-align:right">6</td><td>多机避碰、通信延迟、规模扩展和编队变形</td><td>EGO-Swarm、MADER、Primitive-Swarm</td></tr><tr><td>探索覆盖搜索与重建任务规划</td><td style="text-align:right">13</td><td>主动选择 Frontier、视点、覆盖顺序和任务分配</td><td>FUEL、RACER、FC-Planner、ActiveGS</td></tr><tr><td>学习型规划与端到端避障</td><td style="text-align:right">10</td><td>用学习方法生成动作、航点、轨迹或规划器近似</td><td>YOPO、YOPOv2-Tracker、NavRL、OMC-RL</td></tr><tr><td>视觉语言导航</td><td style="text-align:right">7</td><td>根据语言、图像或语义目标产生导航行为</td><td>AerialVLN、AerialVLA、HALO、WorldVLN</td></tr><tr><td>3DGS 神经地图安全导航与主动建图</td><td style="text-align:right">3</td><td>在 Gaussian Splatting 地图中规划、控制和选择视点</td><td>Splat-Nav、SAFER-Splat、RT-GuIDE</td></tr><tr><td>平台、数据集与评测工具</td><td style="text-align:right">6</td><td>提供任务定义、数据生成、仿真和统一评测</td><td>FlightBench、OpenFly、CARIC、CityNav</td></tr><tr><td>世界动作模型</td><td style="text-align:right">3</td><td>预测动作影响下的未来占据、潜在状态或视觉后果，并用于规划或控制</td><td>Drive-OccWorld、MAD、AeroAct</td></tr><tr><td><strong>合计</strong></td><td style="text-align:right"><strong>63</strong></td><td></td><td></td></tr></tbody></table><h2 id="1-单机局部轨迹规划与重规划">1. 单机局部轨迹规划与重规划</h2><p>这一类关注单架无人机如何在未知或复杂环境中，持续生成可执行的安全局部轨迹。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/Fast-Planner/Fast-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Fast-Planner</a></td><td>Kinodynamic search、B-spline 优化与滚动重规划</td><td><a href="https://github.com/HKUST-Aerial-Robotics/Fast-Planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/Topo-PGO/Topo-PGO%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Topo-PGO</a></td><td>用多条拓扑路径引导梯度优化，跳出局部极小值</td><td><a href="https://github.com/HKUST-Aerial-Robotics/Fast-Planner">Fast-Planner</a> · <a href="https://github.com/HKUST-Aerial-Robotics/TopoTraj">TopoTraj</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/RAPTOR/RAPTOR%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">RAPTOR</a></td><td>拓扑路径、风险感知轨迹修正与主动 yaw 规划</td><td><a href="https://github.com/HKUST-Aerial-Robotics/Fast-Planner">部分开源：规划主干</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/EGO-Planner/EGO-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EGO-Planner</a></td><td>不显式构建 ESDF 的梯度轨迹优化</td><td><a href="https://github.com/ZJU-FAST-Lab/ego-planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/EVA-Planner/EVA-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EVA-Planner</a></td><td>根据环境复杂度自适应调节飞行激进度</td><td><a href="https://github.com/ZJU-FAST-Lab/EVA-planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/TGK-Planner/TGK-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">TGK-Planner</a></td><td>拓扑引导采样与锚定迭代 QP</td><td><a href="https://github.com/ZJU-FAST-Lab/TGK-Planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/GCOPTER/GCOPTER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">GCOPTER</a></td><td>安全走廊约束下的 MINCO 轨迹优化</td><td><a href="https://github.com/ZJU-FAST-Lab/GCOPTER">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/FASTER/FASTER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FASTER</a></td><td>Whole / Safe 双轨迹与未知空间安全回退</td><td><a href="https://github.com/mit-acl/faster">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/01-%E5%8D%95%E6%9C%BA%E5%B1%80%E9%83%A8%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92%E4%B8%8E%E9%87%8D%E8%A7%84%E5%88%92/SUPER/SUPER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">SUPER</a></td><td>远距 LiDAR、双轨迹和高速安全重规划</td><td><a href="https://github.com/hku-mars/SUPER">仓库</a></td></tr></tbody></table><h2 id="2-动态环境与感知约束安全规划">2. 动态环境与感知约束安全规划</h2><p>这类方法不仅要避开会移动的障碍，还会把相机视场、特征可见性、定位质量或模型扰动纳入规划。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/PANTHER/PANTHER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">PANTHER</a></td><td>动态避障、视场约束和 yaw 轨迹联合优化</td><td><a href="https://github.com/mit-acl/panther">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/Intent-MPC/Intent-MPC%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Intent-MPC</a></td><td>将动态障碍的意图预测引入 MPC</td><td><a href="https://github.com/Zhefan-Xu/Intent-MPC">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/LA-Planner/LA-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">LA-Planner</a></td><td>弱纹理和低光照环境中的定位感知规划</td><td><a href="https://github.com/Robotics-STAR-Lab/LA-Planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/APACE/APACE%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">APACE</a></td><td>以共视、视差和可匹配性提升高速视觉定位</td><td><a href="https://github.com/HKUST-Aerial-Robotics/APACE">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/PE-Planner/PE-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">PE-Planner</a></td><td>MPCC、高阶离散 CBF 与在线扰动补偿</td><td><a href="https://github.com/USTC-AIR-Lab/PE-Planner">仿真代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/02-%E5%8A%A8%E6%80%81%E7%8E%AF%E5%A2%83%E4%B8%8E%E6%84%9F%E7%9F%A5%E7%BA%A6%E6%9D%9F%E5%AE%89%E5%85%A8%E8%A7%84%E5%88%92/SIMP/SIMP%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">SIMP</a></td><td>安全时间区间、时空拓扑和四维走廊轨迹</td><td><a href="https://github.com/KumarRobotics/SIMP">核心代码</a></td></tr></tbody></table><h2 id="3-多机安全集群与编队轨迹规划">3. 多机安全集群与编队轨迹规划</h2><p>这一类主要处理多机间避碰、分布式协同、通信延迟、规模扩展，以及编队保持和变形。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/EGO-Swarm/EGO-Swarm%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EGO-Swarm</a></td><td>EGO-Planner 的去中心化多机扩展</td><td><a href="https://github.com/ZJU-FAST-Lab/ego-planner-swarm">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/EGO-v2/EGO-v2%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EGO-v2</a></td><td>时空联合优化、UWB 漂移修正和野外微型集群</td><td><a href="https://github.com/ZJU-FAST-Lab/EGO-Planner-v2">仿真</a> · <a href="https://doi.org/10.5281/zenodo.5804079">数据与硬件</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/MADER/MADER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">MADER</a></td><td>去中心化异步规划与 check-recheck 协议</td><td><a href="https://github.com/mit-acl/mader">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/Robust-MADER/Robust-MADER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Robust MADER</a></td><td>将有界通信延迟纳入多机安全提交协议</td><td><a href="https://github.com/mit-acl/rmader">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/Primitive-Swarm/Primitive-Swarm%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Primitive-Swarm</a></td><td>用运动基元库和占用查表扩展大规模集群</td><td><a href="https://github.com/ZJU-FAST-Lab/Primitive-Planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/03-%E5%A4%9A%E6%9C%BA%E5%AE%89%E5%85%A8%E9%9B%86%E7%BE%A4%E4%B8%8E%E7%BC%96%E9%98%9F%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/Swarm-Formation/Swarm-Formation%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Swarm-Formation</a></td><td>复杂环境中的分布式编队保持与变形</td><td><a href="https://github.com/ZJU-FAST-Lab/Swarm-Formation">代码</a></td></tr></tbody></table><h2 id="4-探索、覆盖、搜索与重建任务规划">4. 探索、覆盖、搜索与重建任务规划</h2><p>这类工作的目标通常不只是“到达一个已知终点”，而是主动决定下一步去哪里观察、扫描、搜索或重建。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/FUEL/FUEL%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FUEL</a></td><td>增量 Frontier 与分层未知空间探索</td><td><a href="https://github.com/HKUST-Aerial-Robotics/FUEL">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/FALCON/FALCON%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FALCON</a></td><td>用覆盖路径引导全局探索，减少局部短视</td><td><a href="https://github.com/HKUST-Aerial-Robotics/FALCON">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/EDEN/EDEN%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EDEN</a></td><td>双层探索顺序、曲率感知视点与连续高速轨迹</td><td><a href="https://github.com/NKU-MobFly-Robotics/EDEN">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/EPIC/EPIC%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">EPIC</a></td><td>基于点云拓扑的轻量 LiDAR 探索</td><td><a href="https://github.com/Robotics-STAR-Lab/EPIC">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/Star-Searcher/Star-Searcher%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Star-Searcher</a></td><td>在未知环境中联合目标搜索与表面检查</td><td><a href="https://github.com/Robotics-STAR-Lab/STAR-Searcher">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/RACER/RACER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">RACER</a></td><td>受限通信下的去中心化多机快速探索</td><td><a href="https://github.com/Robotics-STAR-Lab/RACER">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/C2-Explorer/C2-Explorer%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">C2-Explorer</a></td><td>连通任务表示与连续性驱动的多机任务分配</td><td><a href="https://github.com/Robotics-STAR-Lab/C2-Explorer">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/FC-Planner/FC-Planner%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FC-Planner</a></td><td>骨架引导的复杂三维场景覆盖规划</td><td><a href="https://github.com/HKUST-Aerial-Robotics/FC-Planner">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/FC-Vision/FC-Vision%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FC-Vision</a></td><td>未知障碍下的可见性维护和扫描路径修复</td><td><a href="https://github.com/FC-Family/FC-Vision">仓库</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/PredRecon/PredRecon%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">PredRecon</a></td><td>用未知表面预测改善主动重建视点规划</td><td><a href="https://github.com/HKUST-Aerial-Robotics/PredRecon">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/ActiveGS/ActiveGS%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">ActiveGS</a></td><td>以 Gaussian 置信度联合探索与重建质量提升</td><td><a href="https://github.com/dmar-bonn/active-gs">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/MAGICIAN/MAGICIAN%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">MAGICIAN</a></td><td>用预测占据和 imagined Gaussians 做长期主动建图</td><td><a href="https://github.com/shiyao-li/MAGICIAN">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/04-%E6%8E%A2%E7%B4%A2%E8%A6%86%E7%9B%96%E6%90%9C%E7%B4%A2%E4%B8%8E%E9%87%8D%E5%BB%BA%E4%BB%BB%E5%8A%A1%E8%A7%84%E5%88%92/SOAR/SOAR%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">SOAR</a></td><td>异构 LiDAR / 相机多 UAV 主动重建</td><td><a href="https://github.com/Robotics-STAR-Lab/SOAR">代码</a></td></tr></tbody></table><h2 id="5-学习型规划与端到端避障">5. 学习型规划与端到端避障</h2><p>这一类用模仿学习、强化学习、神经网络或可微物理，直接生成动作、航点、轨迹，或近似传统规划链路的一部分。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/Deep-PANTHER/Deep-PANTHER%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Deep-PANTHER</a></td><td>用模仿学习生成 PANTHER 多模态候选轨迹</td><td><a href="https://github.com/mit-acl/deep_panther">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/YOPO/YOPO%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">YOPO</a></td><td>用一次网络前向压缩感知、搜索和轨迹优化</td><td><a href="https://github.com/TJU-Aerial-Robotics/YOPO">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/YOPOv2-Tracker/YOPOv2-Tracker%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">YOPOv2-Tracker</a></td><td>统一 RGB-D 目标检测、候选轨迹与姿态 / 推力控制</td><td><strong>Tracker 未开源</strong> · <a href="https://arxiv.org/abs/2505.06923">论文</a> · <a href="https://github.com/TJU-Aerial-Robotics/YOPO-Tracker">占位仓库</a> · <a href="https://github.com/TJU-Aerial-Robotics/YOPO">相关导航 / 控制代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/NavRL/NavRL%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">NavRL</a></td><td>PPO 动态避障策略与 velocity-obstacle safety shield</td><td><a href="https://github.com/Zhefan-Xu/NavRL">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/Agile-Autonomy/Agile-Autonomy%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Agile-Autonomy</a></td><td>从特权专家学习真实环境高速视觉飞行</td><td><a href="https://github.com/uzh-rpg/agile_autonomy">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/ViTFly/ViTFly%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">ViTFly</a></td><td>比较 ViT、CNN 和时序网络的深度图避障策略</td><td><a href="https://github.com/anish-bhattacharya/vitfly">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/evfly/evfly%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">evfly</a></td><td>单目事件相机、ConvLSTM 和少样本真实迁移</td><td><a href="https://github.com/anish-bhattacharya/evfly">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/RAPID/RAPID%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">RAPID</a></td><td>用逆强化学习从深度图生成高速避障航点</td><td><strong>未确认官方代码</strong></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/DiffPhysDrone/DiffPhysDrone%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">DiffPhysDrone</a></td><td>通过可微物理直接训练单机和无通信集群策略</td><td><a href="https://github.com/HenryHuYu/DiffPhysDrone">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/05-%E5%AD%A6%E4%B9%A0%E5%9E%8B%E8%A7%84%E5%88%92%E4%B8%8E%E7%AB%AF%E5%88%B0%E7%AB%AF%E9%81%BF%E9%9A%9C/OMC-RL/OMC-RL%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">OMC-RL</a></td><td>掩码时序对比表征与 Oracle 引导的视觉强化学习</td><td><a href="https://github.com/zzzzzyh111/OMC-RL">部分开源：上游代码</a></td></tr></tbody></table><h2 id="6-视觉语言导航">6. 视觉语言导航</h2><p>这类任务把自然语言、目标图像或开放词汇语义加入导航，让无人机理解“去哪里”以及“要找什么”。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/AerialVLN/AerialVLN%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">AerialVLN</a></td><td>城市级无人机视觉语言导航任务与 AirSim 基准</td><td><a href="https://github.com/AirVLN/AirVLN">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/AerialVLA/AerialVLA%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">AerialVLA / AeroVLA</a></td><td>将视觉和语言直接映射为 UAV 动作 token</td><td><a href="https://github.com/XuPeng23/AeroVLA">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/GaussNav/GaussNav%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">GaussNav</a></td><td>用 Semantic Gaussian 视觉记忆做 ImageGoal 导航</td><td><a href="https://github.com/XiaohanLei/GaussNav">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/HALO/HALO%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">HALO</a></td><td>高空单目开放词汇地图与语言条件探索</td><td><a href="https://github.com/KumarRobotics/HALO">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/UAV-Flow/UAV-Flow%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">UAV-Flow</a></td><td>一句短指令对应的真实 UAV 轨迹模仿基准</td><td><a href="https://github.com/buaa-colalab/UAV-Flow">代码</a> · <a href="https://huggingface.co/datasets/wangxiangyu0814/UAV-Flow">真实数据</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/WorldVLN/WorldVLN%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">WorldVLN</a></td><td>在潜空间预测未来，再解码连续相对航点</td><td><a href="https://github.com/EmbodiedCity/WorldVLN.code">代码</a> · <a href="https://huggingface.co/EmbodiedCity/WorldVLN">权重</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/06-%E8%A7%86%E8%A7%89%E8%AF%AD%E8%A8%80%E5%AF%BC%E8%88%AA/see-point-fly/See-Point-Fly%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">See, Point, Fly</a></td><td>通用 VLM 二维指点与相机几何三维动作生成</td><td><a href="https://github.com/Hu-chih-yao/see-point-fly">代码</a></td></tr></tbody></table><h2 id="7-3DGS-神经地图安全导航与主动建图">7. 3DGS 神经地图安全导航与主动建图</h2><p>这类方法直接利用 Gaussian Splatting 地图进行碰撞检查、安全控制或信息驱动的视点规划。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/07-3DGS%E7%A5%9E%E7%BB%8F%E5%9C%B0%E5%9B%BE%E5%AE%89%E5%85%A8%E5%AF%BC%E8%88%AA%E4%B8%8E%E4%B8%BB%E5%8A%A8%E5%BB%BA%E5%9B%BE/Splat-Nav/Splat-Nav%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Splat-Nav</a></td><td>Gaussian 地图中的几何碰撞约束和安全轨迹</td><td><a href="https://github.com/chengine/splatnav">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/07-3DGS%E7%A5%9E%E7%BB%8F%E5%9C%B0%E5%9B%BE%E5%AE%89%E5%85%A8%E5%AF%BC%E8%88%AA%E4%B8%8E%E4%B8%BB%E5%8A%A8%E5%BB%BA%E5%9B%BE/SAFER-Splat/SAFER-Splat%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">SAFER-Splat</a></td><td>在线 Gaussian 地图上的 CBF safety filter</td><td><a href="https://github.com/chengine/safer-splat">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/07-3DGS%E7%A5%9E%E7%BB%8F%E5%9C%B0%E5%9B%BE%E5%AE%89%E5%85%A8%E5%AF%BC%E8%88%AA%E4%B8%8E%E4%B8%BB%E5%8A%A8%E5%BB%BA%E5%9B%BE/RT-GuIDE/RT-GuIDE%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">RT-GuIDE</a></td><td>实时 Gaussian 建图与信息驱动视点选择</td><td><a href="https://github.com/KumarRobotics/RT-GuIDE">代码</a></td></tr></tbody></table><h2 id="8-平台、数据集与评测工具">8. 平台、数据集与评测工具</h2><p>这一类不是单一规划算法，而是为算法提供任务定义、数据、仿真场景或统一评测标准。</p><table><thead><tr><th>方法 / 平台</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/FlightBench/FlightBench%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">FlightBench</a></td><td>学习型与优化型第一视角四旋翼导航统一评测</td><td><strong>核心平台已开源，非完整一键复现</strong> · <a href="https://thu-uav.github.io/FlightBench/">项目页</a> · <a href="https://github.com/thu-uav/FlightBench">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/OpenFly/OpenFly%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">OpenFly</a></td><td>Aerial VLN 数据生成、训练和统一评测平台</td><td><a href="https://github.com/SHAILAB-IPEC/OpenFly-Platform">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/CARIC/CARIC%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">CARIC</a></td><td>异构多 UAV 协同巡检、分配和规划基准</td><td><a href="https://ntu-aris.github.io/caric/">项目页</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/UAV-ON/UAV-ON%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">UAV-ON</a></td><td>开放世界无人机 ObjectGoal Navigation 基准</td><td><a href="https://github.com/iLearn-Lab/ACMMM25-UAV_ON">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/CityNav/CityNav%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">CityNav</a></td><td>真实城市三维扫描数字孪生中的大范围语言目标导航</td><td><a href="https://github.com/water-cookie/citynav">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/08-%E5%B9%B3%E5%8F%B0%E6%95%B0%E6%8D%AE%E9%9B%86%E4%B8%8E%E8%AF%84%E6%B5%8B%E5%B7%A5%E5%85%B7/TravelUAV/TravelUAV%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">TravelUAV</a></td><td>连续六自由度长程 VLN 与分级求助基准</td><td><a href="https://github.com/prince687028/TravelUAV">仓库</a></td></tr></tbody></table><h2 id="9-世界动作模型">9. 世界动作模型</h2><p>世界动作模型尝试学习“执行某个动作后，环境和无人机会怎样变化”。这一方向与传统局部规划并不等价：有的方法显式预测未来占据，有的方法只在潜空间保存地图记忆，也有的方法在训练时预测未来视觉、部署时直接生成动作。</p><table><thead><tr><th>方法</th><th>核心内容</th><th>资源与开源状态</th></tr></thead><tbody><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/09-%E4%B8%96%E7%95%8C%E5%8A%A8%E4%BD%9C%E6%A8%A1%E5%9E%8B/Drive-OccWorld/Drive-OccWorld%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">Drive-OccWorld</a></td><td>用动作条件预测未来 4D 语义占据与场景流，再以显式代价选择候选轨迹</td><td><strong>核心代码已开源，仓库未提供论文任务权重</strong> · <a href="https://drive-occworld.github.io/">项目页</a> · <a href="https://github.com/yuyang-cloud/Drive-OccWorld">代码</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/09-%E4%B8%96%E7%95%8C%E5%8A%A8%E4%BD%9C%E6%A8%A1%E5%9E%8B/MAD/MAD%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">MAD</a></td><td>用占据、可见性和本体状态监督世界模型潜在记忆，直接输出四旋翼加速度</td><td><strong>MAD 专用实现未确认开源</strong> · <a href="https://arxiv.org/abs/2606.04534">论文</a> · <a href="https://github.com/flyingbitac/diffaero">基础仿真器（非 MAD 实现）</a></td></tr><tr><td><a href="https://github.com/dreamer198/awesome-uav-planning/blob/e8ea156727b669d9baf48a5d3104a58dc78a9037/09-%E4%B8%96%E7%95%8C%E5%8A%A8%E4%BD%9C%E6%A8%A1%E5%9E%8B/AeroAct/AeroAct%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md">AeroAct</a></td><td>训练时联合预测未来第一视角与动作块，部署时直接生成局部飞行轨迹</td><td><strong>AeroAct 专用代码未确认开源，数据与权重未确认发布</strong> · <a href="https://arxiv.org/abs/2607.14997">论文</a> · <a href="https://github.com/flyingbitac/diffaero">基础仿真器（非 AeroAct 实现）</a></td></tr></tbody></table><h2 id="推荐阅读路线">推荐阅读路线</h2><p>下面的顺序表示知识依赖，不是对算法进行主观排名。</p><ul><li><strong>局部规划基础</strong>：Fast-Planner → EGO-Planner → GCOPTER → SUPER</li><li><strong>拓扑鲁棒与主动感知</strong>：Fast-Planner → Topo-PGO → RAPTOR</li><li><strong>动态障碍与感知约束</strong>：PANTHER → Intent-MPC / SIMP → APACE / LA-Planner</li><li><strong>多机安全</strong>：MADER → Robust MADER；工程集群可读 EGO-Swarm → EGO-v2</li><li><strong>探索与重建</strong>：FUEL → FALCON / EDEN；主动重建可读 PredRecon → ActiveGS / MAGICIAN</li><li><strong>学习型避障</strong>：Agile-Autonomy / YOPO → YOPOv2-Tracker / NavRL / DiffPhysDrone / OMC-RL → FlightBench</li><li><strong>视觉语言导航</strong>：AerialVLN → OpenFly / TravelUAV → AerialVLA / WorldVLN</li><li><strong>3DGS 导航</strong>：Splat-Nav → SAFER-Splat；主动建图可对照 RT-GuIDE</li><li><strong>世界动作模型</strong>：Drive-OccWorld（显式占据预测）→ MAD（潜在空间地图记忆）/ AeroAct（未来视觉辅助动作生成）</li></ul><h2 id="横向比较时先统一口径">横向比较时先统一口径</h2><ul><li>输出究竟是几何路径、连续轨迹、航点还是即时速度；</li><li>环境是静态、动态、未知，还是只有仿真器能够直接访问；</li><li>安全来自经验成功率、后备轨迹、safety filter 还是形式化证明；</li><li>“开源”指完整系统、核心模块、上游代码、项目页，还是仅有预训练模型；</li><li>世界模型在部署时是否真的滚动预测未来，还是未来预测只提供训练监督；</li><li>实验证据属于离线开放环、闭环仿真、sim-to-real、数字孪生还是真机闭环。</li></ul><p>因此，选算法时不能只看论文中的最高速度或成功率，也不能只看仓库是否存在。更可靠的做法是先确定自己的传感器、地图形式、控制接口和场景，再检查代码、权重、数据、配置以及部署资产是否真正齐全。</p>]]></content>
    
    
    <summary type="html">前言 本文依据我的 Awesome UAV Planning 论文与阅读笔记仓库重新整理。当前基线为 2026-08-06 的 e8ea156，共收录 63 个方法、数据集或平台，按照它们主要解决的问题分为 9 类。 表格中的方法名链接到对应中文阅读笔记，最后一列链接到官方代码、项目页、论文或数据。需要特别…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="路径规划" scheme="https://dreamer198.top/tags/%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92/"/>
    
    <category term="多机" scheme="https://dreamer198.top/tags/%E5%A4%9A%E6%9C%BA/"/>
    
    <category term="轨迹规划" scheme="https://dreamer198.top/tags/%E8%BD%A8%E8%BF%B9%E8%A7%84%E5%88%92/"/>
    
    <category term="开源算法" scheme="https://dreamer198.top/tags/%E5%BC%80%E6%BA%90%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>OpenVINS搭建与测试</title>
    <link href="https://dreamer198.top/2026/05/13/OpenVINS%E6%90%AD%E5%BB%BA/"/>
    <id>https://dreamer198.top/2026/05/13/OpenVINS%E6%90%AD%E5%BB%BA/</id>
    <published>2026-05-12T16:00:00.000Z</published>
    <updated>2026-07-08T12:44:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="环境">环境</h2><ul><li>系统：Ubuntu 22.04</li><li>ROS2：Humble</li><li>源码仓库：<a href="https://github.com/dreamer198/open_vins.git">https://github.com/dreamer198/open_vins.git</a></li></ul><h2 id="源码下载">源码下载</h2><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">mkdir</span> -p ~/ros2_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/ros2_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/dreamer198/open_vins.git</span><br></pre></td></tr></table></figure><h2 id="依赖安装">依赖安装</h2><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install -y \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  libboost-all-dev \</span><br><span class="line">  libceres-dev \</span><br><span class="line">  python3-colcon-common-extensions \</span><br><span class="line">  ros-humble-ros2bag \</span><br><span class="line">  ros-humble-rosbag2*</span><br></pre></td></tr></table></figure><h2 id="编译">编译</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"><span class="built_in">cd</span> ~/ros2_ws</span><br></pre></td></tr></table></figure><p>直接使用 <code>colcon build</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">MAKEFLAGS=<span class="string">&quot;-j8&quot;</span> CMAKE_BUILD_PARALLEL_LEVEL=1 \</span><br><span class="line">colcon build --executor sequential --packages-select ov_core ov_init ov_msckf</span><br></pre></td></tr></table></figure><p>如果在 Jetson Orin NX 上遇到 OpenCV 缺少 <code>aruco</code> 模块的问题，可以关闭 ArUco Tag 支持后重新编译：</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">MAKEFLAGS=<span class="string">&quot;-j8&quot;</span> CMAKE_BUILD_PARALLEL_LEVEL=1 \</span><br><span class="line">colcon build --executor sequential --packages-select ov_core ov_init ov_msckf \</span><br><span class="line">  --cmake-args -DENABLE_ARUCO_TAGS=OFF</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">source</span> ~/ros2_ws/install/setup.bash</span><br></pre></td></tr></table></figure><h2 id="公开数据集测试">公开数据集测试</h2><h3 id="下载数据集">下载数据集</h3><p>数据集下载地址：<a href="https://docs.ros.org/en/noetic/api/ov_core/html/gs-datasets.html#gs-data-euroc">OpenVINS EuRoC 数据集说明</a></p><p>推荐先下载 <code>Vicon Room 1 01</code>，也就是 <code>V1_01_easy</code>。</p><h3 id="运行测试">运行测试</h3><p>终端 1：启动 OpenVINS。</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">cd</span> ~/ros2_ws</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br><span class="line">ros2 launch ov_msckf subscribe.launch.py config:=euroc_mav</span><br></pre></td></tr></table></figure><p>终端 2：播放数据集。</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">ros2 bag play V1_01_easy</span><br></pre></td></tr></table></figure><p>终端 3：打开 RViz2 可视化。</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">rviz2 -d ~/ros2_ws/src/open_vins/ov_msckf/launch/display_ros2.rviz</span><br></pre></td></tr></table></figure><h2 id="RealSense-D455测试">RealSense D455测试</h2><h3 id="启动相机">启动相机</h3><p>终端 1：发布双目红外图像和 IMU 数据。</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></pre></td><td class="code"><pre><span class="line">ros2 launch realsense2_camera rs_launch.py \</span><br><span class="line">  enable_color:=<span class="literal">false</span> \</span><br><span class="line">  enable_depth:=<span class="literal">false</span> \</span><br><span class="line">  enable_infra1:=<span class="literal">true</span> \</span><br><span class="line">  enable_infra2:=<span class="literal">true</span> \</span><br><span class="line">  enable_gyro:=<span class="literal">true</span> \</span><br><span class="line">  enable_accel:=<span class="literal">true</span> \</span><br><span class="line">  unite_imu_method:=1 \</span><br><span class="line">  depth_module.infra_profile:=640,480,30 \</span><br><span class="line">  gyro_fps:=200 \</span><br><span class="line">  accel_fps:=200</span><br></pre></td></tr></table></figure><p>可以通过 <code>rqt</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">ros2 param <span class="built_in">set</span> /camera/camera depth_module.emitter_enabled 0</span><br></pre></td></tr></table></figure><h3 id="启动-OpenVINS">启动 OpenVINS</h3><p>终端 2：运行 D455 配置。</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">cd</span> ~/ros2_ws</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br><span class="line">ros2 launch ov_msckf d455.launch.py enable_rviz:=<span class="literal">true</span> enable_mavros:=<span class="literal">true</span></span><br></pre></td></tr></table></figure><h2 id="常见问题">常见问题</h2><ul><li>编译卡死：降低 <code>MAKEFLAGS</code> 或 <code>CMAKE_BUILD_PARALLEL_LEVEL</code>。</li><li>Jetson 缺少 <code>aruco</code>：编译时添加 <code>--cmake-args -DENABLE_ARUCO_TAGS=OFF</code>。</li><li>红外图像有斑点：关闭 <code>depth_module.emitter_enabled</code>。</li></ul>]]></content>
    
    
    <summary type="html">环境 系统：Ubuntu 22.04 ROS2：Humble 源码仓库： 源码下载 1 2 3 mkdir -p /ros2ws/src cd /ros2ws/src git clone 依赖安装 1 2 3 4 5 6 7 8 9 10 source /opt/ros/humble/setup.bash…</summary>
    
    
    
    <category term="SLAM与定位" scheme="https://dreamer198.top/categories/SLAM%E4%B8%8E%E5%AE%9A%E4%BD%8D/"/>
    
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="SLAM" scheme="https://dreamer198.top/tags/SLAM/"/>
    
    <category term="OpenVINS" scheme="https://dreamer198.top/tags/OpenVINS/"/>
    
    <category term="VIO" scheme="https://dreamer198.top/tags/VIO/"/>
    
  </entry>
  
  <entry>
    <title>Gazebo 仿真环境搭建</title>
    <link href="https://dreamer198.top/2026/05/13/Gazebo%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
    <id>https://dreamer198.top/2026/05/13/Gazebo%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</id>
    <published>2026-05-12T16:00:00.000Z</published>
    <updated>2026-07-08T12:44:19.491Z</updated>
    
    <content type="html"><![CDATA[<p>本文记录 Gazebo + PX4 SITL 仿真环境的基础启动流程，包括加载仓库世界、启动普通无人机、启动搭载 MID360 的无人机，以及将 MID360 点云桥接到 ROS 2。</p><span id="more"></span><h2 id="一、启动-Gazebo-世界">一、启动 Gazebo 世界</h2><p>打开第一个终端，进入 PX4 工程并加载 Gazebo 环境：</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">cd</span> ~/PX4-Autopilot</span><br><span class="line"><span class="built_in">source</span> build/px4_sitl_default/rootfs/gz_env.sh</span><br><span class="line">gz sim -r Tools/simulation/gz/worlds/warehouse_world.sdf</span><br></pre></td></tr></table></figure><p>该命令会启动 <code>warehouse_world</code> 仓库仿真场景。</p><h2 id="二、启动普通无人机">二、启动普通无人机</h2><p>打开第二个终端，启动带下视相机的 X500 无人机：</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">cd</span> ~/PX4-Autopilot</span><br><span class="line"></span><br><span class="line">PX4_GZ_STANDALONE=1 \</span><br><span class="line">PX4_GZ_WORLD=warehouse_world \</span><br><span class="line">PX4_SYS_AUTOSTART=4014 \</span><br><span class="line">PX4_SIM_MODEL=gz_x500_mono_cam_down \</span><br><span class="line">PX4_GZ_MODEL_POSE=<span class="string">&quot;0,0,0.2,0,0,0&quot;</span> \</span><br><span class="line">./build/px4_sitl_default/bin/px4 -i 0</span><br></pre></td></tr></table></figure><p>其中 <code>PX4_GZ_STANDALONE=1</code> 表示 Gazebo 已经单独启动，PX4 只负责加载模型并运行 SITL。</p><h2 id="三、启动搭载-MID360-的无人机">三、启动搭载 MID360 的无人机</h2><p>如果需要激光雷达点云数据，改用搭载 MID360 的无人机模型：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/PX4-Autopilot</span><br><span class="line"></span><br><span class="line">PX4_GZ_STANDALONE=1 \</span><br><span class="line">PX4_GZ_WORLD=warehouse_world \</span><br><span class="line">PX4_GZ_MODELS=<span class="variable">$PWD</span>/Tools/simulation/gz/worlds \</span><br><span class="line">PX4_GZ_WORLDS=<span class="variable">$PWD</span>/Tools/simulation/gz/worlds \</span><br><span class="line">GZ_SIM_RESOURCE_PATH=<span class="variable">$PWD</span>/Tools/simulation/gz/worlds:<span class="variable">$PWD</span>/Tools/simulation/gz/models:<span class="variable">$GZ_SIM_RESOURCE_PATH</span> \</span><br><span class="line">PX4_SYS_AUTOSTART=4014 \</span><br><span class="line">PX4_SIM_MODEL=gz_x500_mono_cam_down_mid360 \</span><br><span class="line">PX4_GZ_MODEL_POSE=<span class="string">&quot;0,0,0.2,0,0,0&quot;</span> \</span><br><span class="line">PX4_PARAM_NAV_DLL_ACT=0 \</span><br><span class="line">./build/px4_sitl_default/bin/px4 -i 0</span><br></pre></td></tr></table></figure><p>这里需要额外配置 <code>PX4_GZ_MODELS</code>、<code>PX4_GZ_WORLDS</code> 和 <code>GZ_SIM_RESOURCE_PATH</code>，确保 Gazebo 能找到自定义世界和模型资源。</p><h2 id="四、桥接-MID360-点云">四、桥接 MID360 点云</h2><p>打开第三个终端，将 Gazebo 中的 MID360 点云桥接到 ROS 2：</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></pre></td><td class="code"><pre><span class="line">GZ_LIDAR_TOPIC=/world/warehouse_world/model/x500_mono_cam_down_mid360_0/link/lidar_sensor_link/sensor/lidar/scan/points</span><br><span class="line"></span><br><span class="line">ros2 run ros_gz_bridge parameter_bridge \</span><br><span class="line">  <span class="string">&quot;<span class="variable">$&#123;GZ_LIDAR_TOPIC&#125;</span>@sensor_msgs/msg/PointCloud2[gz.msgs.PointCloudPacked&quot;</span> \</span><br><span class="line">  --ros-args -r <span class="string">&quot;<span class="variable">$&#123;GZ_LIDAR_TOPIC&#125;</span>:=/mid360/points&quot;</span></span><br></pre></td></tr></table></figure><p>桥接成功后，ROS 2 中会得到点云话题：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/mid360/points</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">ros2 topic list | grep mid360</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /mid360/points --once</span><br></pre></td></tr></table></figure><h2 id="五、启动顺序">五、启动顺序</h2><p>推荐按下面的顺序启动：</p><figure class="highlight text"><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">1. 启动 Gazebo 世界</span><br><span class="line">2. 启动 PX4 SITL 无人机</span><br><span class="line">3. 桥接 MID360 点云</span><br><span class="line">4. 启动后续算法节点，例如 FAST-LIO2、EGO-Planner</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">本文记录 Gazebo + PX4 SITL 仿真环境的基础启动流程，包括加载仓库世界、启动普通无人机、启动搭载 MID360 的无人机，以及将 MID360 点云桥接到 ROS 2。 一、启动 Gazebo 世界 打开第一个终端，进入 PX4 工程并加载 Gazebo 环境： 1 2 3 cd /PX4-…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="Gazebo" scheme="https://dreamer198.top/tags/Gazebo/"/>
    
  </entry>
  
  <entry>
    <title>Gazebo 仿真中跑通 PX4 + MID360 + FAST-LIO2 + EGO-Planner 完整链路</title>
    <link href="https://dreamer198.top/2026/05/07/Gazebo-PX4-MID360-FAST-LIO2-EGO-Planner%E4%BB%BF%E7%9C%9F%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/"/>
    <id>https://dreamer198.top/2026/05/07/Gazebo-PX4-MID360-FAST-LIO2-EGO-Planner%E4%BB%BF%E7%9C%9F%E5%AE%8C%E6%95%B4%E9%93%BE%E8%B7%AF/</id>
    <published>2026-05-07T14:30:00.000Z</published>
    <updated>2026-07-08T12:44:19.491Z</updated>
    
    <content type="html"><![CDATA[<p>本文记录一条已经跑通的仿真链路，并重点说明一键启动脚本的用法：</p><figure class="highlight text"><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">Gazebo + PX4 SITL</span><br><span class="line">  -&gt; MID360-like LiDAR + IMU</span><br><span class="line">  -&gt; FAST-LIO2</span><br><span class="line">  -&gt; EGO-Planner</span><br><span class="line">  -&gt; MAVROS</span><br><span class="line">  -&gt; PX4 Offboard</span><br></pre></td></tr></table></figure><p>最终效果：执行 <code>start_sim_px4_mid360_fastlio_ego.sh start</code> 后，发送目标点，再切 <code>OFFBOARD</code> 并解锁，无人机即可按 EGO-Planner 规划轨迹飞行。</p><span id="more"></span><h2 id="一、环境与文件">一、环境与文件</h2><p>默认环境：</p><figure class="highlight text"><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">Ubuntu 22.04</span><br><span class="line">ROS 2 Humble</span><br><span class="line">Gazebo Harmonic</span><br><span class="line">PX4-Autopilot SITL</span><br><span class="line">FAST_LIO_ROS2</span><br><span class="line">ego-planner-swarm ROS2</span><br><span class="line">MAVROS ROS2</span><br></pre></td></tr></table></figure><p>默认路径：</p><table><thead><tr><th>组件</th><th>默认路径</th></tr></thead><tbody><tr><td>PX4</td><td><code>~/PX4-Autopilot</code></td></tr><tr><td>FAST-LIO2</td><td><code>~/fastlio_ws/src/FAST_LIO_ROS2</code></td></tr><tr><td>EGO-Planner</td><td><code>~/ros2_ws/src/ego-planner-swarm</code></td></tr><tr><td>MAVROS 里程计桥接</td><td><code>~/ros2_ws/src/external_odom_to_mavros</code></td></tr><tr><td>EGO 到 MAVROS 桥接</td><td><code>~/ros2_ws/src/ego_to_mavros</code></td></tr><tr><td>一键启动脚本</td><td><code>~/code/start_sim_px4_mid360_fastlio_ego.sh</code></td></tr></tbody></table><p>本文用到的脚本和配置：</p><table><thead><tr><th>文件</th><th>作用</th></tr></thead><tbody><tr><td><code>~/code/start_sim_px4_mid360_fastlio_ego.sh</code></td><td>一键启动、停止、发送目标点</td></tr><tr><td><code>ego_planner/launch/ego_fastlio_mid360_tuned.launch.py</code></td><td>EGO-Planner 参数和话题映射</td></tr><tr><td><code>ego_planner/launch/ego_fastlio_mid360_rviz.launch.py</code></td><td>RViz、静态 TF、目标点桥接</td></tr><tr><td><code>ego_planner/launch/ego_fastlio_mid360.rviz</code></td><td>EGO 专用 RViz 配置</td></tr><tr><td><code>fast_lio/scripts/pointcloud_self_filter.py</code></td><td>过滤输入 FAST-LIO 前的机体附近点云</td></tr><tr><td><code>~/fastlio_ws/src/FAST_LIO_ROS2/config/gz_mid360.yaml</code></td><td>Gazebo MID360 对应 FAST-LIO2 配置</td></tr></tbody></table><h2 id="二、一键启动脚本">二、一键启动脚本</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">~/code/start_sim_px4_mid360_fastlio_ego.sh start</span><br></pre></td></tr></table></figure><p>常用命令：</p><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>start</code></td><td>启动完整仿真链路</td></tr><tr><td><code>restart</code></td><td>停止后重新启动</td></tr><tr><td><code>stop</code></td><td>停止 tmux 会话</td></tr><tr><td><code>status</code></td><td>查看 tmux 窗口状态</td></tr><tr><td><code>attach</code></td><td>进入 tmux 会话查看各节点终端</td></tr><tr><td><code>goal</code></td><td>发布一个测试目标点</td></tr></tbody></table><p>脚本默认优先使用 <code>tmux</code>，没有 <code>tmux</code> 时会尝试使用 <code>gnome-terminal</code>。如果想稳定使用 <code>attach/status/stop</code>，推荐安装并使用 <code>tmux</code>。</p><p>脚本启动顺序如下：</p><figure class="highlight text"><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></pre></td><td class="code"><pre><span class="line">gazebo</span><br><span class="line">px4</span><br><span class="line">gz_bridge</span><br><span class="line">self_filter      # ENABLE_SELF_FILTER=true 时启用</span><br><span class="line">fast_lio</span><br><span class="line">ego_planner</span><br><span class="line">ego_rviz</span><br><span class="line">mavros</span><br><span class="line">odom_to_mavros</span><br><span class="line">ego_to_mavros</span><br></pre></td></tr></table></figure><p>其中点云链路是：</p><figure class="highlight text"><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">Gazebo LiDAR</span><br><span class="line">  -&gt; /mid360/points_raw</span><br><span class="line">  -&gt; pointcloud_self_filter</span><br><span class="line">  -&gt; /mid360/points</span><br><span class="line">  -&gt; FAST-LIO2</span><br></pre></td></tr></table></figure><p>如果关闭自滤波，Gazebo 点云会直接桥接到 <code>/mid360/points</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">ENABLE_SELF_FILTER=<span class="literal">false</span> \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><h2 id="三、快速飞行流程">三、快速飞行流程</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"></span><br><span class="line">ros2 topic hz /mid360/points</span><br><span class="line">ros2 topic hz /livox/imu</span><br><span class="line">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic hz /cloud_registered</span><br><span class="line">ros2 topic hz /drone_0_planning/pos_cmd</span><br><span class="line">ros2 topic hz /mavros/setpoint_raw/local</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">~/code/start_sim_px4_mid360_fastlio_ego.sh goal</span><br></pre></td></tr></table></figure><p><code>goal</code> 子命令默认发送到 <code>/rviz_goal_pose</code>，再由 <code>rviz_goal_bridge</code> 补上高度并转发到 <code>/move_base_simple/goal</code>。默认目标是：</p><figure class="highlight text"><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">GOAL_X=1.0</span><br><span class="line">GOAL_Y=0.0</span><br><span class="line">GOAL_Z=1.0</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">GOAL_X=1.0 GOAL_Y=0.5 GOAL_Z=1.0 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh goal</span><br></pre></td></tr></table></figure><p>也可以直接向 EGO-Planner 发目标：</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">ros2 topic pub --once /move_base_simple/goal geometry_msgs/msg/PoseStamped \</span><br><span class="line">  <span class="string">&quot;&#123;header: &#123;frame_id: &#x27;world&#x27;&#125;, pose: &#123;position: &#123;x: 1.0, y: 0.5, z: 1.0&#125;, orientation: &#123;w: 1.0&#125;&#125;&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>EGO-Planner 成功规划时，终端通常会看到：</p><figure class="highlight text"><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">Triggered!</span><br><span class="line">[FSM]: from WAIT_TARGET to GEN_NEW_TRAJ</span><br><span class="line">refine_success=1</span><br><span class="line">[FSM]: from GEN_NEW_TRAJ to EXEC_TRAJ</span><br></pre></td></tr></table></figure><p>确认 <code>/mavros/setpoint_raw/local</code> 稳定发布后，切到 Offboard：</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">ros2 service call /mavros/set_mode mavros_msgs/srv/SetMode \</span><br><span class="line">  <span class="string">&quot;&#123;base_mode: 0, custom_mode: &#x27;OFFBOARD&#x27;&#125;&quot;</span></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">ros2 service call /mavros/cmd/arming mavros_msgs/srv/CommandBool \</span><br><span class="line">  <span class="string">&quot;&#123;value: true&#125;&quot;</span></span><br></pre></td></tr></table></figure><h2 id="四、核心话题">四、核心话题</h2><table><thead><tr><th>作用</th><th>话题</th></tr></thead><tbody><tr><td>Gazebo LiDAR 转 ROS 后点云</td><td><code>/mid360/points</code></td></tr><tr><td>Gazebo IMU 转 ROS 后 IMU</td><td><code>/livox/imu</code></td></tr><tr><td>FAST-LIO2 里程计</td><td><code>/Odometry</code></td></tr><tr><td>FAST-LIO2 当前帧局部点云</td><td><code>/cloud_registered</code></td></tr><tr><td>FAST-LIO2 累计地图</td><td><code>/Laser_map</code></td></tr><tr><td>RViz 原始目标点</td><td><code>/rviz_goal_pose</code></td></tr><tr><td>EGO-Planner 输入目标</td><td><code>/move_base_simple/goal</code></td></tr><tr><td>EGO-Planner 位置指令</td><td><code>/drone_0_planning/pos_cmd</code></td></tr><tr><td>MAVROS 外部视觉位姿</td><td><code>/mavros/vision_pose/pose</code></td></tr><tr><td>MAVROS raw setpoint</td><td><code>/mavros/setpoint_raw/local</code></td></tr></tbody></table><p>EGO-Planner 的避障输入使用 <code>/cloud_registered</code>。<code>/Laser_map</code> 是累计地图，适合观察建图效果，不建议作为规划输入。</p><h2 id="五、RViz-使用">五、RViz 使用</h2><p>脚本会启动：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ros2 launch ego_planner ego_fastlio_mid360_rviz.launch.py</span><br></pre></td></tr></table></figure><p>该 launch 文件做三件事：</p><figure class="highlight text"><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">1. 发布 world -&gt; camera_init 静态 TF</span><br><span class="line">2. 发布 world -&gt; map 静态 TF</span><br><span class="line">3. 启动 rviz_goal_bridge，把 /rviz_goal_pose 转成 /move_base_simple/goal</span><br></pre></td></tr></table></figure><p>主要显示项：</p><table><thead><tr><th>显示项</th><th>话题</th><th>说明</th></tr></thead><tbody><tr><td>Odometry</td><td><code>/Odometry</code></td><td>FAST-LIO2 位姿</td></tr><tr><td>FAST-LIO Map</td><td><code>/Laser_map</code></td><td>累计地图，仅用于观察</td></tr><tr><td>FAST-LIO Cloud</td><td><code>/cloud_registered</code></td><td>当前帧局部点云</td></tr><tr><td>Map Inflate</td><td><code>/drone_0_grid/grid_map/occupancy_inflate</code></td><td>EGO 膨胀占据栅格</td></tr><tr><td>Goal Point</td><td><code>/drone_0_plan_vis/goal_point</code></td><td>目标点</td></tr><tr><td>Optimal Traj</td><td><code>/drone_0_plan_vis/optimal_list</code></td><td>优化轨迹</td></tr><tr><td>Drone Path</td><td><code>/drone_0_vis/path</code></td><td>飞行路径</td></tr></tbody></table><p>在 RViz 中可以用 <code>2D Goal Pose</code> 点目标。RViz 先发到 <code>/rviz_goal_pose</code>，<code>rviz_goal_bridge</code> 再使用 <code>GOAL_Z</code> 作为默认高度转发给 EGO-Planner。</p><p>修改 RViz 点目标默认高度：</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">GOAL_Z=1.5 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><h2 id="六、常用参数">六、常用参数</h2><p>启动脚本支持环境变量覆盖。常用参数如下：</p><table><thead><tr><th>参数</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>BACKEND</code></td><td><code>auto</code></td><td>启动后端，支持 <code>auto/tmux/gnome</code></td></tr><tr><td><code>FASTLIO_RVIZ</code></td><td><code>true</code></td><td>是否打开 FAST-LIO2 自带 RViz</td></tr><tr><td><code>ENABLE_SELF_FILTER</code></td><td><code>true</code></td><td>是否过滤机体附近点云</td></tr><tr><td><code>GOAL_X/Y/Z</code></td><td><code>1.0/0.0/1.0</code></td><td><code>goal</code> 子命令的默认目标</td></tr><tr><td><code>EGO_MAX_VEL</code></td><td><code>0.45</code></td><td>EGO 最大速度</td></tr><tr><td><code>EGO_MAX_ACC</code></td><td><code>0.7</code></td><td>EGO 最大加速度</td></tr><tr><td><code>EGO_INFLATION</code></td><td><code>0.30</code></td><td>障碍物硬膨胀半径</td></tr><tr><td><code>EGO_COLLISION_DIST</code></td><td><code>0.35</code></td><td>优化器软安全距离</td></tr><tr><td><code>EGO_LAMBDA_COLLISION</code></td><td><code>0.8</code></td><td>避障代价权重</td></tr><tr><td><code>EGO_VIRTUAL_CEIL</code></td><td><code>8.0</code></td><td>EGO 虚拟天花板</td></tr><tr><td><code>EGO_TO_MAVROS_LIMITS</code></td><td><code>passthrough</code></td><td>桥接层是否额外限幅，可设为 <code>safe</code></td></tr><tr><td><code>AUTO_OFFBOARD</code></td><td><code>false</code></td><td>是否自动切 Offboard</td></tr><tr><td><code>AUTO_ARM</code></td><td><code>false</code></td><td>是否自动解锁</td></tr></tbody></table><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></pre></td><td class="code"><pre><span class="line">EGO_MAX_VEL=0.35 \</span><br><span class="line">EGO_INFLATION=0.35 \</span><br><span class="line">EGO_COLLISION_DIST=0.45 \</span><br><span class="line">EGO_LAMBDA_COLLISION=1.0 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</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">EGO_INFLATION=0.20 \</span><br><span class="line">EGO_COLLISION_DIST=0.30 \</span><br><span class="line">EGO_LAMBDA_COLLISION=0.7 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</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></pre></td><td class="code"><pre><span class="line">EGO_MAX_VEL=0.8 \</span><br><span class="line">EGO_MAX_ACC=1.2 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><p>提速后如果开始贴墙，优先降速或提高 <code>EGO_INFLATION</code>，不要同时大幅提速和降低安全距离。</p><h2 id="七、自动-Offboard">七、自动 Offboard</h2><p>默认推荐手动切 <code>OFFBOARD</code> 和手动解锁，方便先观察 setpoint 是否稳定。</p><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></pre></td><td class="code"><pre><span class="line">AUTO_OFFBOARD=<span class="literal">true</span> \</span><br><span class="line">AUTO_ARM=<span class="literal">true</span> \</span><br><span class="line">AUTO_START_ON_GOAL=<span class="literal">true</span> \</span><br><span class="line">AUTO_START_PRELOAD_TIME=1.5 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><p>自动模式逻辑：</p><figure class="highlight text"><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">收到有效 EGO pos_cmd</span><br><span class="line">持续发布 setpoint 到 AUTO_START_PRELOAD_TIME 秒</span><br><span class="line">请求 OFFBOARD</span><br><span class="line">确认进入 OFFBOARD 后请求 arm</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">AUTO_OFFBOARD=<span class="literal">false</span> AUTO_ARM=<span class="literal">false</span> \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><h2 id="八、排查">八、排查</h2><h3 id="1-EGO-没有规划">1. EGO 没有规划</h3><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></pre></td><td class="code"><pre><span class="line">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic hz /cloud_registered</span><br><span class="line">ros2 topic <span class="built_in">echo</span> --once /move_base_simple/goal</span><br></pre></td></tr></table></figure><p>再看 EGO 终端是否出现：</p><figure class="highlight text"><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">wait for goal or trigger</span><br><span class="line">plan_success=0</span><br><span class="line">refine_success=0</span><br></pre></td></tr></table></figure><p>第一次测试目标不要太远，也不要贴墙。建议 <code>x/y</code> 距离控制在 1m 到 2m，<code>z=1.0</code>。</p><h3 id="2-不能进入-Offboard">2. 不能进入 Offboard</h3><p>检查 MAVROS 连接和 setpoint：</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">ros2 topic <span class="built_in">echo</span> --once /mavros/state</span><br><span class="line">ros2 topic hz /mavros/setpoint_raw/local</span><br></pre></td></tr></table></figure><p><code>/mavros/setpoint_raw/local</code> 需要稳定发布，建议高于 20Hz。若没有 setpoint，先确认 EGO 已经收到目标并发布 <code>/drone_0_planning/pos_cmd</code>。</p><h3 id="3-无人机贴墙或撞墙">3. 无人机贴墙或撞墙</h3><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></pre></td><td class="code"><pre><span class="line">EGO_MAX_VEL=0.35 \</span><br><span class="line">EGO_INFLATION=0.35 \</span><br><span class="line">EGO_COLLISION_DIST=0.45 \</span><br><span class="line">EGO_LAMBDA_COLLISION=1.0 \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><p>同时在 RViz 中观察：</p><figure class="highlight text"><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">/cloud_registered</span><br><span class="line">/drone_0_grid/grid_map/occupancy_inflate</span><br><span class="line">/drone_0_plan_vis/optimal_list</span><br></pre></td></tr></table></figure><p>重点确认障碍物是否进入局部地图，以及膨胀层是否把通道堵死。</p><h3 id="4-Laser-map-闪烁">4. <code>/Laser_map</code> 闪烁</h3><p><code>/Laser_map</code> 是 FAST-LIO2 每秒发布一次的累计地图。EGO RViz 中已经把 <code>FAST-LIO Map</code> 的 <code>Decay Time</code> 设为 <code>0</code>。</p><p>如果手动添加显示项，同样设置：</p><figure class="highlight text"><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">Topic: /Laser_map</span><br><span class="line">Decay Time: 0</span><br><span class="line">Style: Flat Squares</span><br><span class="line">Use Fixed Frame: true</span><br></pre></td></tr></table></figure><h3 id="5-stop-status-attach-不生效">5. <code>stop/status/attach</code> 不生效</h3><p>这些命令依赖 tmux 会话。如果脚本使用了 <code>gnome-terminal</code> 后端，窗口不会被集中管理，需要手动关闭窗口。</p><p>推荐显式使用 tmux：</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">BACKEND=tmux \</span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh restart</span><br></pre></td></tr></table></figure><h2 id="九、最小命令清单">九、最小命令清单</h2><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></pre></td><td class="code"><pre><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh start</span><br><span class="line"></span><br><span class="line">~/code/start_sim_px4_mid360_fastlio_ego.sh goal</span><br><span class="line"></span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"></span><br><span class="line">ros2 service call /mavros/set_mode mavros_msgs/srv/SetMode \</span><br><span class="line">  <span class="string">&quot;&#123;base_mode: 0, custom_mode: &#x27;OFFBOARD&#x27;&#125;&quot;</span></span><br><span class="line"></span><br><span class="line">ros2 service call /mavros/cmd/arming mavros_msgs/srv/CommandBool \</span><br><span class="line">  <span class="string">&quot;&#123;value: true&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>这就是当前稳定跑通的主流程。</p>]]></content>
    
    
    <summary type="html">本文记录一条已经跑通的仿真链路，并重点说明一键启动脚本的用法： 1 2 3 4 5 6 Gazebo + PX4 SITL -&amp;gt; MID360-like LiDAR + IMU -&amp;gt; FAST-LIO2 -&amp;gt; EGO-Planner -&amp;gt; MAVROS -&amp;gt; PX4 Offbo…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="MAVROS" scheme="https://dreamer198.top/tags/MAVROS/"/>
    
    <category term="FAST-LIO2" scheme="https://dreamer198.top/tags/FAST-LIO2/"/>
    
    <category term="Gazebo" scheme="https://dreamer198.top/tags/Gazebo/"/>
    
    <category term="EGO-Planner" scheme="https://dreamer198.top/tags/EGO-Planner/"/>
    
  </entry>
  
  <entry>
    <title>EGO-Planner ROS2 + MAVROS + PX4 完整教程：从仿真规划到 Offboard 桥接</title>
    <link href="https://dreamer198.top/2026/05/07/EGO-Planner-ROS2%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/05/07/EGO-Planner-ROS2%E6%95%99%E7%A8%8B/</id>
    <published>2026-05-07T12:30:00.000Z</published>
    <updated>2026-07-08T12:44:19.491Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文把两条链路合在一起：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Mid-360S -&gt; FAST-LIO2 -&gt; EGO-Planner -&gt; MAVROS -&gt; PX4 Offboard</span><br></pre></td></tr></table></figure><p>目标分三步：</p><ol><li>先跑通 EGO-Planner 官方仿真。</li><li>再接入 Mid-360S + FAST-LIO2，只在 RViz 中看规划。</li><li>最后把 <code>/drone_0_planning/pos_cmd</code> 桥接到 MAVROS Offboard setpoint。</li></ol><span id="more"></span><p>第一次真机测试不要装桨。先看 setpoint，再切 Offboard，最后才低高度短距离飞行。</p><h2 id="一、整体链路">一、整体链路</h2><p>完整链路如下：</p><figure class="highlight text"><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></pre></td><td class="code"><pre><span class="line">Mid-360S</span><br><span class="line">  -&gt; livox_ros_driver2</span><br><span class="line">  -&gt; FAST-LIO2</span><br><span class="line">  -&gt; /Odometry + /cloud_registered</span><br><span class="line">  -&gt; EGO-Planner</span><br><span class="line">  -&gt; /drone_0_planning/pos_cmd</span><br><span class="line">  -&gt; ego_to_mavros</span><br><span class="line">  -&gt; /uav1/setpoint_raw/local</span><br><span class="line">  -&gt; MAVROS</span><br><span class="line">  -&gt; PX4 Offboard</span><br></pre></td></tr></table></figure><p>如果 MAVROS 没有使用命名空间，把 <code>/uav1/...</code> 改成 <code>/mavros/...</code>。</p><h2 id="二、安全顺序">二、安全顺序</h2><p>建议按下面顺序推进：</p><figure class="highlight text"><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">1. 官方仿真</span><br><span class="line">2. 真实雷达 + FAST-LIO2</span><br><span class="line">3. EGO-Planner 只看 RViz</span><br><span class="line">4. PX4 Position 模式悬停</span><br><span class="line">5. 不装桨检查 Offboard setpoint</span><br><span class="line">6. 不装桨手动切 Offboard</span><br><span class="line">7. 装桨低高度短距离测试</span><br></pre></td></tr></table></figure><p>第一次真机参数建议：</p><table><thead><tr><th>项目</th><th>建议值</th></tr></thead><tbody><tr><td>最大水平速度</td><td><code>0.3 ~ 0.5 m/s</code></td></tr><tr><td>最大高度</td><td><code>1.0 ~ 1.2 m</code></td></tr><tr><td>目标距离</td><td><code>1 ~ 2 m</code></td></tr><tr><td>障碍物</td><td>先不放，后面再用软质障碍物</td></tr></tbody></table><p>不要一开始自动解锁。遥控器要随时能切回 Position / Altitude / Stabilized。</p><h2 id="三、准备环境">三、准备环境</h2><p>本文使用：</p><figure class="highlight text"><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">Ubuntu 22.04</span><br><span class="line">ROS 2 Humble</span><br><span class="line">Livox Mid-360S</span><br><span class="line">FAST_LIO_ROS2</span><br><span class="line">MAVROS ROS2</span><br><span class="line">EGO-Planner ROS2</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y \</span><br><span class="line">  git \</span><br><span class="line">  cmake \</span><br><span class="line">  build-essential \</span><br><span class="line">  python3-colcon-common-extensions \</span><br><span class="line">  python3-rosdep \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  libpcl-dev \</span><br><span class="line">  libopencv-dev \</span><br><span class="line">  ros-humble-pcl-ros \</span><br><span class="line">  ros-humble-cv-bridge \</span><br><span class="line">  ros-humble-rviz2 \</span><br><span class="line">  ros-humble-rmw-cyclonedds-cpp</span><br></pre></td></tr></table></figure><p>建议统一使用 CycloneDDS：</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">export</span> RMW_IMPLEMENTATION=rmw_cyclonedds_cpp</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">echo</span> <span class="variable">$RMW_IMPLEMENTATION</span></span><br></pre></td></tr></table></figure><p>如果确认稳定，可以写入 <code>~/.bashrc</code>。</p><h2 id="四、编译工作区">四、编译工作区</h2><p>下文统一使用 <code>~/ros2_ws</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">mkdir</span> -p ~/ros2_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/ros2_ws/src</span><br></pre></td></tr></table></figure><p>需要准备这些包：</p><table><thead><tr><th>包</th><th>作用</th></tr></thead><tbody><tr><td><code>ego-planner-swarm</code></td><td>EGO-Planner ROS2</td></tr><tr><td><code>external_odom_to_mavros</code></td><td><code>/Odometry</code> 到 MAVROS 外部视觉</td></tr><tr><td><code>ego_to_mavros</code></td><td>EGO <code>pos_cmd</code> 到 MAVROS setpoint</td></tr></tbody></table><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/ros2_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">rosdep install --from-paths src --ignore-src -y</span><br><span class="line">colcon build --symlink-install</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</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></pre></td><td class="code"><pre><span class="line">ros2 pkg executables ego_planner</span><br><span class="line">ros2 pkg executables ego_to_mavros</span><br><span class="line">ros2 pkg executables external_odom_to_mavros</span><br></pre></td></tr></table></figure><p><code>ego_to_mavros</code> 只需要已经在工作区中可用。本文不展开源码。</p><h2 id="五、先跑官方仿真">五、先跑官方仿真</h2><p>先确认 EGO-Planner 本体能跑。</p><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><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line"><span class="built_in">export</span> RMW_IMPLEMENTATION=rmw_cyclonedds_cpp</span><br><span class="line">ros2 launch ego_planner rviz.launch.py</span><br></pre></td></tr></table></figure><p>终端 2：</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 class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line"><span class="built_in">export</span> RMW_IMPLEMENTATION=rmw_cyclonedds_cpp</span><br><span class="line">ros2 launch ego_planner single_run_in_sim.launch.py use_mockamap:=True use_dynamic:=False</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">ros2 topic list | grep planning</span><br></pre></td></tr></table></figure><p>常见输出：</p><figure class="highlight text"><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">/drone_0_planning/bspline</span><br><span class="line">/drone_0_planning/pos_cmd</span><br><span class="line">/drone_0_planning/data_display</span><br></pre></td></tr></table></figure><p>RViz 中能看到障碍物、无人机模型和轨迹，说明仿真通过。</p><h2 id="六、确认-FAST-LIO2-输出">六、确认 FAST-LIO2 输出</h2><p>启动 Mid-360S 和 FAST-LIO2 后，先检查两个话题：</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">ros2 topic info /Odometry</span><br><span class="line">ros2 topic info /cloud_registered</span><br><span class="line">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic hz /cloud_registered</span><br></pre></td></tr></table></figure><p>推荐结果：</p><table><thead><tr><th>话题</th><th>类型</th><th>频率</th></tr></thead><tbody><tr><td><code>/Odometry</code></td><td><code>nav_msgs/msg/Odometry</code></td><td>约 30Hz 或更高</td></tr><tr><td><code>/cloud_registered</code></td><td><code>sensor_msgs/msg/PointCloud2</code></td><td>5Hz 到 20Hz 均可先测试</td></tr></tbody></table><p>第一次不要用 <code>/Laser_map</code> 做避障输入。<code>/Laser_map</code> 是累计地图，点太多，容易卡。</p><h2 id="七、启动真机定位链路">七、启动真机定位链路</h2><p>终端 1：启动 Mid-360S。</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><p>终端 2：启动 FAST-LIO2。</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 class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/fastlio_ws/install/setup.bash</span><br><span class="line">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></span><br></pre></td></tr></table></figure><p>终端 3：启动 MAVROS。</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">ros2 launch mavros px4.launch \</span><br><span class="line">  fcu_url:=<span class="string">&quot;/dev/ttyACM0:921600&quot;</span> \</span><br><span class="line">  namespace:=<span class="string">&quot;uav1&quot;</span> \</span><br><span class="line">  tgt_system:=1</span><br></pre></td></tr></table></figure><p>终端 4：把 FAST-LIO2 里程计送给 PX4。</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line">ros2 launch external_odom_to_mavros bridge.launch.py \</span><br><span class="line">  input_type:=odometry \</span><br><span class="line">  input_topic:=/Odometry \</span><br><span class="line">  output_topic:=/uav1/vision_pose/pose \</span><br><span class="line">  output_frame_id:=map \</span><br><span class="line">  publish_rate:=30.0</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></pre></td><td class="code"><pre><span class="line">ros2 topic hz /uav1/vision_pose/pose</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/state --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/local_position/odom --once</span><br></pre></td></tr></table></figure><p>重点看：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">connected: true</span><br></pre></td></tr></table></figure><p>如果 PX4 不能进入 Position 模式，先不要继续接 EGO-Planner。</p><h2 id="八、启动真实数据规划">八、启动真实数据规划</h2><p>终端 5：启动 RViz。</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 class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line"><span class="built_in">export</span> RMW_IMPLEMENTATION=rmw_cyclonedds_cpp</span><br><span class="line">ros2 launch ego_planner rviz.launch.py</span><br></pre></td></tr></table></figure><p>终端 6：启动 EGO-Planner。</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line"><span class="built_in">export</span> RMW_IMPLEMENTATION=rmw_cyclonedds_cpp</span><br><span class="line">ros2 launch ego_planner fastlio_mid360.launch.py \</span><br><span class="line">  odom_topic:=/Odometry \</span><br><span class="line">  cloud_topic:=/cloud_registered \</span><br><span class="line">  max_vel:=0.5 \</span><br><span class="line">  max_acc:=0.8 \</span><br><span class="line">  map_resolution:=0.15 \</span><br><span class="line">  inflation:=0.30 \</span><br><span class="line">  virtual_ceil:=2.0</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">ros2 topic list | grep planning</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /drone_0_planning/pos_cmd --once</span><br></pre></td></tr></table></figure><p>没发送目标点前，<code>/drone_0_planning/pos_cmd</code> 可能没有持续输出，这是正常的。</p><h2 id="九、发送目标点">九、发送目标点</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">ros2 topic pub --once /move_base_simple/goal geometry_msgs/msg/PoseStamped \</span><br><span class="line">  <span class="string">&quot;&#123;header: &#123;frame_id: &#x27;world&#x27;&#125;, pose: &#123;position: &#123;x: 1.0, y: 0.0, z: 1.0&#125;, orientation: &#123;w: 1.0&#125;&#125;&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>规划成功时，EGO-Planner 终端通常会出现：</p><figure class="highlight text"><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">Triggered!</span><br><span class="line">[TRIG]: from WAIT_TARGET to GEN_NEW_TRAJ</span><br><span class="line">[FSM]: from GEN_NEW_TRAJ to EXEC_TRAJ</span><br></pre></td></tr></table></figure><p>RViz 中应看到：</p><figure class="highlight text"><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">目标点</span><br><span class="line">初始轨迹</span><br><span class="line">优化后的 B-spline 轨迹</span><br></pre></td></tr></table></figure><h2 id="十、检查坐标方向">十、检查坐标方向</h2><p>先看 <code>/Odometry</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">ros2 topic <span class="built_in">echo</span> /Odometry --once</span><br></pre></td></tr></table></figure><p>手动移动飞机，推荐表现如下：</p><table><thead><tr><th>动作</th><th>ROS ENU 中推荐表现</th></tr></thead><tbody><tr><td>向机头方向移动</td><td><code>x</code> 增大</td></tr><tr><td>向机体左侧移动</td><td><code>y</code> 增大</td></tr><tr><td>抬高无人机</td><td><code>z</code> 增大</td></tr></tbody></table><p>再检查 EGO 输出：</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">ros2 topic <span class="built_in">echo</span> /drone_0_grid/grid_map/occupancy_inflate --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /drone_0_planning/bspline --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /drone_0_planning/pos_cmd --once</span><br></pre></td></tr></table></figure><p>如果方向不对，先修坐标系，不要切 Offboard。</p><h2 id="十一、启动-EGO-到-MAVROS-桥接">十一、启动 EGO 到 MAVROS 桥接</h2><p>确认 RViz 规划正常后，再启动桥接。</p><p>第一次不要自动切 Offboard，不要自动解锁：</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><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/ros2_ws/install/setup.bash</span><br><span class="line">ros2 run ego_to_mavros pos_cmd_bridge --ros-args \</span><br><span class="line">  -p pos_cmd_topic:=/drone_0_planning/pos_cmd \</span><br><span class="line">  -p odom_topic:=/Odometry \</span><br><span class="line">  -p setpoint_topic:=/uav1/setpoint_raw/local \</span><br><span class="line">  -p mavros_state_topic:=/uav1/state \</span><br><span class="line">  -p set_mode_service:=/uav1/set_mode \</span><br><span class="line">  -p arm_service:=/uav1/cmd/arming \</span><br><span class="line">  -p publish_rate:=30.0 \</span><br><span class="line">  -p max_vel_xy:=0.3 \</span><br><span class="line">  -p max_vel_z:=0.2 \</span><br><span class="line">  -p max_acc_xy:=0.6 \</span><br><span class="line">  -p max_acc_z:=0.5 \</span><br><span class="line">  -p min_z:=0.2 \</span><br><span class="line">  -p max_z:=1.2 \</span><br><span class="line">  -p max_position_step:=0.5 \</span><br><span class="line">  -p auto_offboard:=<span class="literal">false</span> \</span><br><span class="line">  -p auto_arm:=<span class="literal">false</span></span><br></pre></td></tr></table></figure><p>检查 setpoint：</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">ros2 topic hz /uav1/setpoint_raw/local</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/setpoint_raw/local --once</span><br></pre></td></tr></table></figure><p>建议频率：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">20Hz 以上</span><br></pre></td></tr></table></figure><p>如果没有 MAVROS 命名空间，把命令中的 <code>/uav1</code> 改成 <code>/mavros</code>。</p><h2 id="十二、不装桨-Offboard-测试">十二、不装桨 Offboard 测试</h2><p>保持桥接节点运行，先不装桨。</p><p>手动切 Offboard：</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">ros2 service call /uav1/set_mode mavros_msgs/srv/SetMode \</span><br><span class="line">  <span class="string">&quot;&#123;base_mode: 0, custom_mode: &#x27;OFFBOARD&#x27;&#125;&quot;</span></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">ros2 topic <span class="built_in">echo</span> /uav1/state --once</span><br></pre></td></tr></table></figure><p>正常应看到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mode: OFFBOARD</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></pre></td><td class="code"><pre><span class="line">ros2 topic hz /uav1/setpoint_raw/local</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/state --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/local_position/odom --once</span><br></pre></td></tr></table></figure><p>PX4 进入 Offboard 前必须已经持续收到 setpoint。</p><h2 id="十三、低高度真机测试">十三、低高度真机测试</h2><p>低高度测试建议流程：</p><ol><li>手动起飞到 <code>0.8 ~ 1.0m</code>。</li><li>进入 Position / Hold，确认悬停稳定。</li><li>启动 EGO-Planner 和桥接节点。</li><li>确认 <code>/uav1/setpoint_raw/local</code> 连续输出。</li><li>手动切 Offboard。</li><li>发送 <code>1m</code> 内目标点。</li><li>遥控器随时准备切回 Position / Altitude / Stabilized。</li></ol><p>第一次不要放障碍物。先确认方向、速度、高度都正确。</p><h2 id="十四、参数建议">十四、参数建议</h2><p>EGO-Planner 常用参数：</p><table><thead><tr><th>参数</th><th>建议值</th><th>说明</th></tr></thead><tbody><tr><td><code>max_vel</code></td><td><code>0.3 ~ 0.5</code></td><td>真机第一次低速</td></tr><tr><td><code>max_acc</code></td><td><code>0.6 ~ 1.0</code></td><td>降低轨迹激进程度</td></tr><tr><td><code>map_resolution</code></td><td><code>0.15 ~ 0.20</code></td><td>算力紧张就调大</td></tr><tr><td><code>inflation</code></td><td><code>0.30 ~ 0.40</code></td><td>轨迹太贴障碍物就调大</td></tr><tr><td><code>virtual_ceil</code></td><td><code>1.5 ~ 2.0</code></td><td>防止规划到过高位置</td></tr></tbody></table><p>桥接节点常用参数：</p><table><thead><tr><th>参数</th><th>建议值</th><th>说明</th></tr></thead><tbody><tr><td><code>max_vel_xy</code></td><td><code>0.3</code></td><td>水平限速</td></tr><tr><td><code>max_vel_z</code></td><td><code>0.2</code></td><td>垂直限速</td></tr><tr><td><code>max_z</code></td><td><code>1.2</code></td><td>真机第一次限高</td></tr><tr><td><code>max_position_step</code></td><td><code>0.5</code></td><td>防止 setpoint 突跳</td></tr><tr><td><code>auto_offboard</code></td><td><code>false</code></td><td>先手动切模式</td></tr><tr><td><code>auto_arm</code></td><td><code>false</code></td><td>不建议早期开启</td></tr></tbody></table><h2 id="十五、常见问题">十五、常见问题</h2><h3 id="1-EGO-Planner-提示没有-odom">1. EGO-Planner 提示没有 odom</h3><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">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic info /Odometry</span><br></pre></td></tr></table></figure><p>确认 EGO 启动参数里 <code>odom_topic:=/Odometry</code>。</p><h3 id="2-有-odom，但没有局部地图">2. 有 odom，但没有局部地图</h3><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">ros2 topic hz /cloud_registered</span><br><span class="line">ros2 topic info /cloud_registered</span><br></pre></td></tr></table></figure><p>确认 EGO 启动参数里 <code>cloud_topic:=/cloud_registered</code>。</p><h3 id="3-发送目标点后没有规划">3. 发送目标点后没有规划</h3><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">ros2 topic <span class="built_in">echo</span> /move_base_simple/goal --once</span><br></pre></td></tr></table></figure><p>再看 EGO 终端是否出现 <code>Triggered!</code>。</p><h3 id="4-没有-drone-0-planning-pos-cmd">4. 没有 <code>/drone_0_planning/pos_cmd</code></h3><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">ros2 topic list | grep pos_cmd</span><br></pre></td></tr></table></figure><p>如果 <code>drone_id</code> 不是 0，话题名会变成 <code>/drone_1_planning/pos_cmd</code> 等。</p><h3 id="5-uav1-setpoint-raw-local-没有输出">5. <code>/uav1/setpoint_raw/local</code> 没有输出</h3><p>桥接节点需要先收到 <code>/Odometry</code>。</p><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">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /Odometry --once</span><br></pre></td></tr></table></figure><h3 id="6-PX4-切不进-Offboard">6. PX4 切不进 Offboard</h3><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></pre></td><td class="code"><pre><span class="line">ros2 topic hz /uav1/setpoint_raw/local</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/state --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/local_position/odom --once</span><br></pre></td></tr></table></figure><p>重点看：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">connected: true</span><br></pre></td></tr></table></figure><p>以及 setpoint 是否已经持续发布。</p><h3 id="7-飞机方向反了">7. 飞机方向反了</h3><p>立刻切回手动稳定模式。</p><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">ros2 topic <span class="built_in">echo</span> /Odometry --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/setpoint_raw/local --once</span><br></pre></td></tr></table></figure><p>确认 FAST-LIO2、RViz、EGO-Planner、MAVROS 输入都在同一套坐标理解下。</p><h3 id="8-轨迹太贴障碍物">8. 轨迹太贴障碍物</h3><p>优先增大：</p><figure class="highlight text"><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">inflation</span><br><span class="line">optimization/dist0</span><br></pre></td></tr></table></figure><p>同时降低：</p><figure class="highlight text"><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">max_vel</span><br><span class="line">max_acc</span><br></pre></td></tr></table></figure><h3 id="9-高度异常">9. 高度异常</h3><p>检查：</p><figure class="highlight text"><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">FAST-LIO2 /Odometry 的 z</span><br><span class="line">EGO virtual_ceil</span><br><span class="line">桥接节点 max_z</span><br><span class="line">目标点 z</span><br></pre></td></tr></table></figure><p>第一次建议 <code>virtual_ceil:=2.0</code>，桥接 <code>max_z:=1.2</code>。</p><h2 id="十六、推荐最终启动顺序">十六、推荐最终启动顺序</h2><figure class="highlight text"><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></pre></td><td class="code"><pre><span class="line">1. Livox 驱动</span><br><span class="line">2. FAST-LIO2</span><br><span class="line">3. MAVROS</span><br><span class="line">4. external_odom_to_mavros</span><br><span class="line">5. EGO-Planner RViz</span><br><span class="line">6. EGO-Planner</span><br><span class="line">7. ego_to_mavros</span><br><span class="line">8. 发送目标点</span><br><span class="line">9. 不装桨 Offboard 测试</span><br><span class="line">10. 低高度真机测试</span><br></pre></td></tr></table></figure><h2 id="参考资料">参考资料</h2><ul><li>EGO-Planner ROS2 分支：<a href="https://github.com/ZJU-FAST-Lab/ego-planner-swarm/tree/ros2_version">https://github.com/ZJU-FAST-Lab/ego-planner-swarm/tree/ros2_version</a></li><li>EGO-Planner 论文项目：<a href="https://github.com/ZJU-FAST-Lab/ego-planner">https://github.com/ZJU-FAST-Lab/ego-planner</a></li><li>FAST-LIO 官方仓库：<a href="https://github.com/hku-mars/FAST_LIO">https://github.com/hku-mars/FAST_LIO</a></li><li>FAST-LIO2 ROS2 移植版本：<a href="https://github.com/Ericsii/FAST_LIO_ROS2">https://github.com/Ericsii/FAST_LIO_ROS2</a></li><li>MAVROS 文档：<a href="https://mavros.readthedocs.io/en/latest/">https://mavros.readthedocs.io/en/latest/</a></li><li>PX4 Offboard 模式文档：<a href="https://docs.px4.io/main/en/flight_modes/offboard.html">https://docs.px4.io/main/en/flight_modes/offboard.html</a></li></ul>]]></content>
    
    
    <summary type="html">前言 本文把两条链路合在一起： 1 Mid-360S -&amp;gt; FAST-LIO2 -&amp;gt; EGO-Planner -&amp;gt; MAVROS -&amp;gt; PX4 Offboard 目标分三步： 1. 先跑通 EGO-Planner 官方仿真。 2. 再接入 Mid-360S + FAST-LIO2，只…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="MAVROS" scheme="https://dreamer198.top/tags/MAVROS/"/>
    
    <category term="FAST-LIO2" scheme="https://dreamer198.top/tags/FAST-LIO2/"/>
    
    <category term="EGO-Planner" scheme="https://dreamer198.top/tags/EGO-Planner/"/>
    
  </entry>
  
  <entry>
    <title>Mid-360S + FAST-LIO2 + MAVROS + PX4 真机部署测试</title>
    <link href="https://dreamer198.top/2026/05/06/Mid360-FAST-LIO-PX4%E7%9C%9F%E6%9C%BA%E9%83%A8%E7%BD%B2%E6%B5%8B%E8%AF%95/"/>
    <id>https://dreamer198.top/2026/05/06/Mid360-FAST-LIO-PX4%E7%9C%9F%E6%9C%BA%E9%83%A8%E7%BD%B2%E6%B5%8B%E8%AF%95/</id>
    <published>2026-05-06T08:45:00.000Z</published>
    <updated>2026-07-08T12:44:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>前面已经完成了三件事：</p><ol><li>机载电脑已经连上并启动 Mid-360S。</li><li>FAST-LIO2 已经成功运行，并输出 <code>/Odometry</code>。</li><li>机载电脑上已经运行 MAVROS，可以和 PX4 飞控通信。</li></ol><p>下一步要做的事情，就是把 FAST-LIO2 的定位结果送进 PX4 的 EKF2，让 PX4 在没有 GPS 的室内也能得到稳定的本地位置估计，然后使用 Position 模式或 Offboard 模式实现悬停。</p><p>本文先跑通最实用的一条链路：</p><figure class="highlight text"><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">Mid-360S</span><br><span class="line">  -&gt; livox_ros_driver2</span><br><span class="line">  -&gt; FAST-LIO2</span><br><span class="line">  -&gt; /Odometry</span><br><span class="line">  -&gt; external_odom_to_mavros</span><br><span class="line">  -&gt; /uav1/vision_pose/pose</span><br><span class="line">  -&gt; MAVROS</span><br><span class="line">  -&gt; PX4 EKF2</span><br><span class="line">  -&gt; Position / Hold / Offboard 悬停</span><br></pre></td></tr></table></figure><span id="more"></span><h2 id="一、整体思路">一、整体思路</h2><p>PX4 本身不直接订阅 ROS 2 的 <code>/Odometry</code> 话题。我们需要通过 MAVROS 把外部定位转换成 MAVLink 消息，再送给 PX4。</p><p>常见有两种接入方式：</p><table><thead><tr><th>接入方式</th><th>ROS 2 话题</th><th>MAVLink 消息</th><th>适合情况</th></tr></thead><tbody><tr><td>位姿输入</td><td><code>/uav1/vision_pose/pose</code></td><td><code>VISION_POSITION_ESTIMATE</code></td><td>最简单，先跑通悬停</td></tr><tr><td>里程计输入</td><td><code>/uav1/odometry/out</code></td><td><code>ODOMETRY</code></td><td>可带速度和协方差，后期优化</td></tr></tbody></table><p>本文先使用 <code>/uav1/vision_pose/pose</code>。它只需要 <code>geometry_msgs/msg/PoseStamped</code>，桥接逻辑简单，更适合第一次实机调通。</p><p>后续如果要进一步提升控制品质，可以改用 <code>/uav1/odometry/out</code>，但要特别注意：<code>nav_msgs/Odometry</code> 中的 <code>twist</code> 必须表达在机体系，而不是世界系。</p><h2 id="二、安全检查">二、安全检查</h2><p>实机调定位和悬停前，先把安全检查放在第一位。</p><p>建议顺序：</p><ol><li>第一次只上电，不装桨。</li><li>第二次装桨但固定机体或使用保护架。</li><li>第三次低高度、空旷室内、手动随时可切回 Stabilized 或 Altitude。</li><li>不要一开始就直接 Offboard 自动起飞。</li></ol><p>室内定位悬停不是只看 <code>/Odometry</code> 有输出，还要确认坐标方向、尺度、延迟、PX4 融合状态都正确。</p><h2 id="三、确认-FAST-LIO2-输出">三、确认 FAST-LIO2 输出</h2><p>启动 Mid-360S：</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">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><p>启动 FAST-LIO2：</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">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml</span><br></pre></td></tr></table></figure><p>检查 <code>/Odometry</code>：</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">ros2 topic info /Odometry</span><br><span class="line">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /Odometry --once</span><br></pre></td></tr></table></figure><p>正常情况下类型应为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nav_msgs/msg/Odometry</span><br></pre></td></tr></table></figure><p>重点看消息头：</p><figure class="highlight text"><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">header.frame_id</span><br><span class="line">child_frame_id</span><br><span class="line">pose.pose.position</span><br><span class="line">pose.pose.orientation</span><br></pre></td></tr></table></figure><p>FAST-LIO2 的初始坐标原点一般是启动时的位置，室内悬停不要求它和真实世界坐标对齐，只要求局部坐标连续、方向正确、漂移足够小。</p><h2 id="四、确认坐标方向">四、确认坐标方向</h2><p>PX4 使用的是 FRD / NED 习惯：</p><figure class="highlight text"><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">机体系 FRD:</span><br><span class="line">X 向前</span><br><span class="line">Y 向右</span><br><span class="line">Z 向下</span><br></pre></td></tr></table></figure><p>ROS 常用的是 FLU / ENU 习惯：</p><figure class="highlight text"><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">机体系 FLU:</span><br><span class="line">X 向前</span><br><span class="line">Y 向左</span><br><span class="line">Z 向上</span><br><span class="line"></span><br><span class="line">世界系 ENU:</span><br><span class="line">X 向东或局部前方</span><br><span class="line">Y 向北或局部左方</span><br><span class="line">Z 向上</span><br></pre></td></tr></table></figure><p>MAVROS 会处理 ROS ENU/FLU 到 PX4 NED/FRD 的转换，所以我们发布给 <code>/uav1/vision_pose/pose</code> 的数据应当按 ROS 习惯来理解。</p><p>第一次调试时，可以把无人机拿在手里缓慢移动，观察 <code>/Odometry</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">ros2 topic <span class="built_in">echo</span> /Odometry</span><br></pre></td></tr></table></figure><p>推荐检查：</p><table><thead><tr><th>动作</th><th>ROS ENU 中推荐表现</th></tr></thead><tbody><tr><td>抬高无人机</td><td><code>z</code> 增大</td></tr><tr><td>向机头方向移动</td><td><code>x</code> 增大</td></tr><tr><td>向机体左侧移动</td><td><code>y</code> 增大</td></tr></tbody></table><p>如果方向不一致，不要急着飞，需要先在桥接节点中做坐标转换，或者检查雷达安装方向、FAST-LIO2 外参和 TF。</p><h2 id="五、安装-MAVROS-相关包">五、安装 MAVROS 相关包</h2><p>如果已经安装并启动 MAVROS，可以跳过这一节。</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y \</span><br><span class="line">  ros-humble-mavros \</span><br><span class="line">  ros-humble-mavros-extras \</span><br><span class="line">  ros-humble-mavros-msgs</span><br></pre></td></tr></table></figure><p>安装 GeographicLib 数据：</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">ros2 run mavros install_geographiclib_datasets.sh</span><br></pre></td></tr></table></figure><p>如果你的 MAVROS 包里找不到这个脚本，也可以手动下载：</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 class="built_in">cd</span> /tmp</span><br><span class="line">wget https://raw.githubusercontent.com/mavlink/mavros/ros2/mavros/scripts/install_geographiclib_datasets.sh</span><br><span class="line"><span class="built_in">chmod</span> +x install_geographiclib_datasets.sh</span><br><span class="line">./install_geographiclib_datasets.sh</span><br></pre></td></tr></table></figure><p>启动 MAVROS：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">ros2 launch mavros px4.launch \</span><br><span class="line">  fcu_url:=<span class="string">&quot;/dev/ttyUSB0:921600&quot;</span> \</span><br><span class="line">  namespace:=<span class="string">&quot;uav1&quot;</span> \</span><br><span class="line">  tgt_system:=1</span><br></pre></td></tr></table></figure><p>如果你使用的是 USB、TELEM、UDP 或自定义 launch，需要把 <code>fcu_url</code> 换成自己的连接方式。</p><p>常见例子：</p><figure class="highlight text"><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">串口：/dev/ttyUSB0:921600</span><br><span class="line">USB ACM：/dev/ttyACM0:57600</span><br><span class="line">SITL UDP：udp://:14540@127.0.0.1:14557</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">ros2 topic <span class="built_in">echo</span> /uav1/state</span><br></pre></td></tr></table></figure><p>正常应看到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">connected: true</span><br></pre></td></tr></table></figure><h2 id="六、使用外部里程计到-MAVROS-的通用桥接包">六、使用外部里程计到 MAVROS 的通用桥接包</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">git <span class="built_in">clone</span> https://github.com/dreamer198/external_odom_to_mavros.git</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">colcon build --packages-select external_odom_to_mavros --symlink-install</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br></pre></td></tr></table></figure><p>FAST-LIO2 的输入话题是 <code>/Odometry</code>，消息类型是 <code>nav_msgs/msg/Odometry</code>，所以这样启动：</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">ros2 launch external_odom_to_mavros bridge.launch.py \</span><br><span class="line">  input_type:=odometry \</span><br><span class="line">  input_topic:=/Odometry \</span><br><span class="line">  output_topic:=/uav1/vision_pose/pose \</span><br><span class="line">  output_frame_id:=map \</span><br><span class="line">  publish_rate:=30.0</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">ros2 topic hz /uav1/vision_pose/pose</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/vision_pose/pose --once</span><br></pre></td></tr></table></figure><p>推荐频率至少 30Hz。PX4 官方文档建议外部视觉消息以 30Hz 到 50Hz 输入，频率太低时 EKF2 可能不会融合。</p><p>如果后续换成 VINS、OpenVINS、LIO-SAM、UWB 或动捕，只需要改 <code>input_type</code> 和 <code>input_topic</code>。</p><p>例如输入是 <code>PoseStamped</code>：</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">ros2 launch external_odom_to_mavros bridge.launch.py \</span><br><span class="line">  input_type:=pose_stamped \</span><br><span class="line">  input_topic:=/external_pose \</span><br><span class="line">  output_topic:=/uav1/vision_pose/pose</span><br></pre></td></tr></table></figure><p>这个节点默认假设输入位姿已经符合 ROS ENU/FLU 习惯。如果方向检查不通过，需要先修正上游定位坐标系、传感器外参，或者在桥接前增加专门的坐标转换节点。</p><h2 id="七、配置-PX4-EKF2-参数">七、配置 PX4 EKF2 参数</h2><p>打开 QGroundControl：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Vehicle Setup -&gt; Parameters</span><br></pre></td></tr></table></figure><p>重点修改下面几个参数。</p><h3 id="1-开启外部视觉融合">1. 开启外部视觉融合</h3><p>参数：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EKF2_EV_CTRL</span><br></pre></td></tr></table></figure><p>建议先开启：</p><figure class="highlight text"><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">Horizontal position fusion</span><br><span class="line">Vertical position fusion</span><br></pre></td></tr></table></figure><p>如果 FAST-LIO2 的 yaw 方向稳定，并且坐标方向已经确认正确，再考虑开启：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Yaw fusion</span><br></pre></td></tr></table></figure><p>如果只是先跑通室内悬停，不建议一开始就融合 velocity，因为本文使用的是 <code>/uav1/vision_pose/pose</code>，没有给 PX4 发送速度。</p><h3 id="2-设置高度参考">2. 设置高度参考</h3><p>参数：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EKF2_HGT_REF</span><br></pre></td></tr></table></figure><p>室内主要依赖 Mid-360S / FAST-LIO2 定位时，可以设为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Vision</span><br></pre></td></tr></table></figure><p>如果你的 FAST-LIO2 垂直方向还有漂移，可以先保留 Baro 做对比测试，但最终室内定点悬停通常希望视觉/激光定位成为主要高度来源。</p><h3 id="3-设置延迟">3. 设置延迟</h3><p>参数：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EKF2_EV_DELAY</span><br></pre></td></tr></table></figure><p>第一次可以先设为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0.0</span><br></pre></td></tr></table></figure><p>如果飞行时出现明显的跟随滞后、刹车发飘、来回修正，再通过日志调这个值。这个延迟和雷达时间戳、FAST-LIO2 计算耗时、MAVROS 转发链路都有关系，每套机子都可能不同。</p><h3 id="4-设置外部定位传感器相对机体的位置">4. 设置外部定位传感器相对机体的位置</h3><p>参数：</p><figure class="highlight text"><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">EKF2_EV_POS_X</span><br><span class="line">EKF2_EV_POS_Y</span><br><span class="line">EKF2_EV_POS_Z</span><br></pre></td></tr></table></figure><p>如果桥接节点发布的是无人机 <code>base_link</code> 或飞控中心的位姿，可以先都设为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0.0</span><br></pre></td></tr></table></figure><p>如果你直接发布的是雷达或 FAST-LIO IMU 坐标系的位姿，就要填雷达定位原点相对机体中心的位置。</p><p>注意这里按 PX4 机体系 FRD 填：</p><figure class="highlight text"><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">X：向前为正</span><br><span class="line">Y：向右为正</span><br><span class="line">Z：向下为正</span><br></pre></td></tr></table></figure><p>例如雷达在飞控前方 0.08m、左侧 0.02m、上方 0.05m：</p><figure class="highlight text"><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">EKF2_EV_POS_X = 0.08</span><br><span class="line">EKF2_EV_POS_Y = -0.02</span><br><span class="line">EKF2_EV_POS_Z = -0.05</span><br></pre></td></tr></table></figure><h3 id="5-室内-GPS-处理">5. 室内 GPS 处理</h3><p>如果室内没有 GPS，或者 GPS 信号很差，建议不要让 GPS 参与位置融合。可以检查：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EKF2_GPS_CTRL</span><br></pre></td></tr></table></figure><p>室内纯激光定位测试时，可将 GPS 融合关闭。不同 PX4 版本参数界面略有差别，建议通过 QGroundControl 的参数说明确认当前固件对应选项。</p><p>修改 EKF2 参数后，重启飞控。</p><h2 id="八、检查-PX4-是否收到外部定位">八、检查 PX4 是否收到外部定位</h2><p>启动顺序推荐如下：</p><h3 id="终端-1：启动雷达">终端 1：启动雷达</h3><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><h3 id="终端-2：启动-FAST-LIO2">终端 2：启动 FAST-LIO2</h3><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/fastlio_ws/install/setup.bash</span><br><span class="line">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></span><br></pre></td></tr></table></figure><h3 id="终端-3：启动-MAVROS">终端 3：启动 MAVROS</h3><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="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">ros2 launch mavros px4.launch \</span><br><span class="line">  fcu_url:=<span class="string">&quot;/dev/ttyACM0:921600&quot;</span> \</span><br><span class="line">  namespace:=<span class="string">&quot;uav1&quot;</span> \</span><br><span class="line">  tgt_system:=1 \</span><br><span class="line">  gcs_url:=<span class="string">&quot;udp://:14550@10.0.30.196:14550&quot;</span></span><br></pre></td></tr></table></figure><h3 id="终端-4：启动桥接节点">终端 4：启动桥接节点</h3><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/px4_lio_ws/install/setup.bash</span><br><span class="line">ros2 launch external_odom_to_mavros bridge.launch.py \</span><br><span class="line">  input_type:=odometry \</span><br><span class="line">  input_topic:=/Odometry \</span><br><span class="line">  output_topic:=/uav1/vision_pose/pose \</span><br><span class="line">  output_frame_id:=map \</span><br><span class="line">  publish_rate:=30.0</span><br></pre></td></tr></table></figure><p>检查 MAVROS 本地位置：</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">ros2 topic <span class="built_in">echo</span> /uav1/local_position/odom</span><br></pre></td></tr></table></figure><p>如果 PX4 已经融合外部定位，这里应当能看到比较稳定的本地位置输出。</p><p>也可以在 QGroundControl 中查看：</p><figure class="highlight text"><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">MAVLink Inspector -&gt; LOCAL_POSITION_NED</span><br><span class="line">MAVLink Inspector -&gt; ODOMETRY</span><br></pre></td></tr></table></figure><p>为了让 PX4 回传收到的外部 odometry，可设置：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MAV_ODOM_LP = 1</span><br></pre></td></tr></table></figure><p>方向和姿态检查完成后，建议再设回：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MAV_ODOM_LP = 0</span><br></pre></td></tr></table></figure><p>然后手持无人机做方向检查：</p><table><thead><tr><th>动作</th><th>PX4 / MAVLink NED 中推荐表现</th></tr></thead><tbody><tr><td>向机头方向移动</td><td><code>x</code> 增大</td></tr><tr><td>向机体右侧移动</td><td><code>y</code> 增大</td></tr><tr><td>抬高无人机</td><td><code>z</code> 减小</td></tr></tbody></table><p>如果这个方向不对，不要起飞。</p><h2 id="九、先用-Position-模式悬停">九、先用 Position 模式悬停</h2><p>最推荐的第一次室内悬停方式不是 Offboard，而是 Position 模式。</p><p>原因是：</p><ol><li>PX4 自己负责位置控制。</li><li>遥控器可以直接接管。</li><li>更容易判断 EKF2 融合是否正常。</li></ol><p>操作步骤：</p><ol><li>确认 <code>/Odometry</code> 稳定。</li><li>确认 <code>/uav1/vision_pose/pose</code> 约 30Hz。</li><li>确认 <code>/uav1/state</code> 中 <code>connected: true</code>。</li><li>确认 QGroundControl 没有位置估计相关的红色告警。</li><li>切到 Position 模式。</li><li>低高度起飞，建议先 0.3m 到 0.5m。</li><li>摇杆回中，观察是否能定点悬停。</li></ol><p>如果 Position 模式无法进入，通常说明 PX4 认为本地位置估计还不可用。优先查：</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">ros2 topic hz /uav1/vision_pose/pose</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/local_position/odom --once</span><br><span class="line">ros2 topic <span class="built_in">echo</span> /uav1/state</span><br></pre></td></tr></table></figure><p>QGroundControl 中也要看 EKF2 是否有 innovation、height、local position 相关告警。</p><h2 id="十、再测试-Offboard-悬停">十、再测试 Offboard 悬停</h2><p>Position 模式能稳定悬停以后，再测试 Offboard。</p><p>PX4 Offboard 有一个硬要求：进入 Offboard 前，外部控制端必须已经持续发送 setpoint，频率要大于 2Hz。实际使用建议 20Hz 或 30Hz。</p><p>可以先用 MAVROS 的位置 setpoint 话题做简单测试：</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">ros2 topic pub -r 20 /uav1/setpoint_position/local geometry_msgs/msg/PoseStamped \</span><br><span class="line">  <span class="string">&quot;&#123;header: &#123;frame_id: &#x27;map&#x27;&#125;, pose: &#123;position: &#123;x: 0.0, y: 0.0, z: 0.8&#125;, orientation: &#123;w: 1.0&#125;&#125;&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>保持这个命令运行，再切 Offboard：</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">ros2 service call /uav1/set_mode mavros_msgs/srv/SetMode \</span><br><span class="line">  <span class="string">&quot;&#123;base_mode: 0, custom_mode: &#x27;OFFBOARD&#x27;&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>如果需要通过 MAVROS 解锁：</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">ros2 service call /uav1/cmd/arming mavros_msgs/srv/CommandBool \</span><br><span class="line">  <span class="string">&quot;&#123;value: true&#125;&quot;</span></span><br></pre></td></tr></table></figure><p>实机第一次不建议直接用命令解锁自动飞，最好先手动起飞到低高度，确认 Position 稳定后，再让 Offboard 接管当前位置。</p><h2 id="十一、常见问题">十一、常见问题</h2><h3 id="1-uav1-vision-pose-pose-有数据，但-PX4-不融合">1. <code>/uav1/vision_pose/pose</code> 有数据，但 PX4 不融合</h3><p>检查：</p><ul><li>频率是否太低，建议 30Hz 到 50Hz。</li><li>EKF2 参数是否已开启 external vision fusion。</li><li>修改参数后是否重启飞控。</li><li>坐标方向是否正确。</li><li>时间戳是否持续更新。</li><li>QGroundControl 是否有 EKF2 相关告警。</li></ul><h3 id="2-起飞后上下抖动">2. 起飞后上下抖动</h3><p>优先检查：</p><ul><li><code>EKF2_HGT_REF</code> 是否合理。</li><li>FAST-LIO2 的 <code>z</code> 是否稳定。</li><li>雷达是否看到了足够的地面、墙面或结构。</li><li>机架震动是否影响 Mid-360S 或机载电脑。</li><li><code>EKF2_EV_DELAY</code> 是否需要调大。</li></ul><h3 id="3-水平位置慢慢漂移">3. 水平位置慢慢漂移</h3><p>常见原因：</p><ul><li>室内环境结构太少，比如空旷大厅、玻璃墙、长直走廊。</li><li>FAST-LIO2 外参不准。</li><li>雷达安装不够刚性。</li><li>飞机振动导致点云畸变或 IMU 质量变差。</li><li>起飞前 FAST-LIO2 没有静止初始化。</li></ul><h3 id="4-一切正常但-Position-模式无法进入">4. 一切正常但 Position 模式无法进入</h3><p>看 QGroundControl 的 Preflight / EKF 提示。</p><p>重点确认：</p><figure class="highlight text"><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">local position estimate valid</span><br><span class="line">local velocity estimate valid</span><br><span class="line">height estimate valid</span><br></pre></td></tr></table></figure><p>如果没有 GPS，且飞控仍然要求 GPS，检查当前固件的 arming check 和 GPS 相关参数，不要盲目关闭所有安全检查。</p><h3 id="5-Offboard-一切换就退出">5. Offboard 一切换就退出</h3><p>PX4 Offboard 要求 setpoint 持续输入。检查：</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">ros2 topic hz /uav1/setpoint_position/local</span><br></pre></td></tr></table></figure><p>应大于 2Hz，实际建议 20Hz。</p><p>还要检查：</p><figure class="highlight text"><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">COM_OF_LOSS_T</span><br><span class="line">COM_OBL_RC_ACT</span><br></pre></td></tr></table></figure><p>它们决定 Offboard 丢失后的超时和保护动作。</p><h2 id="十二、后续优化方向">十二、后续优化方向</h2><p>第一次能稳定悬停以后，可以继续优化：</p><ol><li>把 <code>/uav1/vision_pose/pose</code> 改成 <code>/uav1/odometry/out</code>，向 PX4 发送完整 <code>ODOMETRY</code>。</li><li>加入速度估计，并确认速度表达在机体系。</li><li>精确标定 Mid-360S / FAST-LIO IMU 到机体中心的外参。</li><li>调整 <code>EKF2_EV_DELAY</code>，降低动态飞行时的创新误差。</li><li>录制 rosbag 和 PX4 ulog，对比 FAST-LIO2 输出与 PX4 local position。</li><li>在 Offboard 中加入当前位置锁定、限速、失联降落和遥控器接管逻辑。</li></ol><h2 id="参考资料">参考资料</h2><ul><li>PX4 外部视觉定位文档：<a href="https://docs.px4.io/v1.16/en/ros/external_position_estimation">https://docs.px4.io/v1.16/en/ros/external_position_estimation</a></li><li>PX4 Offboard 模式文档：<a href="https://docs.px4.io/v1.16/en/flight_modes/offboard">https://docs.px4.io/v1.16/en/flight_modes/offboard</a></li><li>MAVROS ROS 2 文档：<a href="https://mavros.readthedocs.io/en/latest/">https://mavros.readthedocs.io/en/latest/</a></li><li>MAVROS odometry 插件：<a href="https://mavros.readthedocs.io/en/latest/plugins/extras/odom/">https://mavros.readthedocs.io/en/latest/plugins/extras/odom/</a></li><li>MAVROS vision_pose 插件：<a href="https://mavros.readthedocs.io/en/latest/plugins/extras/vision_pose_estimate/">https://mavros.readthedocs.io/en/latest/plugins/extras/vision_pose_estimate/</a></li><li>FAST-LIO 官方仓库：<a href="https://github.com/hku-mars/FAST_LIO">https://github.com/hku-mars/FAST_LIO</a></li><li>FAST-LIO2 ROS2 移植版本：<a href="https://github.com/Ericsii/FAST_LIO_ROS2">https://github.com/Ericsii/FAST_LIO_ROS2</a></li></ul>]]></content>
    
    
    <summary type="html">前言 前面已经完成了三件事： 1. 机载电脑已经连上并启动 Mid-360S。 2. FAST-LIO2 已经成功运行，并输出 /Odometry。 3. 机载电脑上已经运行 MAVROS，可以和 PX4 飞控通信。 下一步要做的事情，就是把 FAST-LIO2 的定位结果送进 PX4 的 EKF2，让 P…</summary>
    
    
    
    <category term="SLAM与定位" scheme="https://dreamer198.top/categories/SLAM%E4%B8%8E%E5%AE%9A%E4%BD%8D/"/>
    
    
    <category term="Livox" scheme="https://dreamer198.top/tags/Livox/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="MAVROS" scheme="https://dreamer198.top/tags/MAVROS/"/>
    
    <category term="FAST-LIO2" scheme="https://dreamer198.top/tags/FAST-LIO2/"/>
    
  </entry>
  
  <entry>
    <title>使用 SSH 远控局域网中的其他电脑</title>
    <link href="https://dreamer198.top/2026/05/06/ssh%E8%BF%9C%E6%8E%A7%E7%94%B5%E8%84%91/"/>
    <id>https://dreamer198.top/2026/05/06/ssh%E8%BF%9C%E6%8E%A7%E7%94%B5%E8%84%91/</id>
    <published>2026-05-06T07:30:00.000Z</published>
    <updated>2026-07-08T12:44:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>在机器人调试、ROS 开发、服务器维护或多台 Ubuntu 电脑协同时，经常需要从一台电脑登录另一台电脑执行命令。</p><p>SSH（Secure Shell）可以让我们通过网络安全地登录远程终端。只要两台电脑在同一个局域网，并且被控电脑开启了 SSH 服务，就可以在本机终端中远程操作它。</p><span id="more"></span><h2 id="一、准备">一、准备</h2><p>本文约定：</p><figure class="highlight text"><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">电脑 A：控制端，当前操作的电脑</span><br><span class="line">电脑 B：被控端，需要远程登录的电脑</span><br></pre></td></tr></table></figure><p>使用 SSH 前需要确认：</p><ul><li>两台电脑在同一个局域网内。</li><li>电脑 B 已安装并启动 SSH 服务。</li><li>电脑 A 能访问电脑 B 的 IP 地址。</li><li>已知道电脑 B 的用户名和登录密码。</li></ul><p>在电脑 B 上查看用户名：</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">whoami</span></span><br></pre></td></tr></table></figure><p>查看局域网 IP：</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">hostname -I</span><br></pre></td></tr></table></figure><p>输出中类似 <code>192.168.1.23</code>、<code>192.168.31.88</code>、<code>10.0.0.15</code> 的地址，一般就是局域网 IP。</p><h2 id="二、安装并启动-SSH-服务">二、安装并启动 SSH 服务</h2><p>以下命令在电脑 B 上执行。</p><p>安装 <code>openssh-server</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> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y openssh-server</span><br></pre></td></tr></table></figure><p>启动 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> systemctl <span class="built_in">enable</span> --now ssh</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">systemctl status ssh</span><br></pre></td></tr></table></figure><p>如果看到 <code>active (running)</code>，说明 SSH 服务已经正常运行。</p><p>少数系统的服务名可能是 <code>sshd</code>，如果 <code>ssh</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">systemctl status sshd</span><br></pre></td></tr></table></figure><h2 id="三、远程登录">三、远程登录</h2><p>假设电脑 B 的信息如下：</p><figure class="highlight text"><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">用户名：dreamer</span><br><span class="line">IP 地址：192.168.1.23</span><br></pre></td></tr></table></figure><p>在电脑 A 上登录电脑 B：</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">ssh dreamer@192.168.1.23</span><br></pre></td></tr></table></figure><p>第一次连接时，终端会询问是否继续连接，输入 <code>yes</code> 后回车，再输入电脑 B 的用户密码即可。</p><p>登录成功后，当前终端执行的命令都运行在电脑 B 上。可以用下面的命令确认当前主机名：</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">hostname</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">exit</span></span><br></pre></td></tr></table></figure><h2 id="四、测试网络连通性">四、测试网络连通性</h2><p>如果 SSH 连接不上，先在电脑 A 上测试能否访问电脑 B：</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">ping 192.168.1.23</span><br></pre></td></tr></table></figure><p>如果能持续收到回复，说明两台电脑网络基本连通。</p><p>如果没有回复，优先检查：</p><ul><li>两台电脑是否在同一个局域网。</li><li>IP 地址是否写错。</li><li>电脑 B 是否连接到了正确的 WiFi 或网口。</li><li>网线、交换机、路由器是否正常。</li><li>是否有多个网卡，导致看错了 IP。</li></ul><h2 id="五、常用优化">五、常用优化</h2><h3 id="设置固定-IP">设置固定 IP</h3><p>如果电脑 B 使用 DHCP 自动获取 IP，重启或换网络后 IP 可能变化。建议给电脑 B 设置固定 IP，后续连接会更方便。</p><p>推荐在路由器后台设置：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DHCP 静态分配 / 地址保留 / IP-MAC 绑定</span><br></pre></td></tr></table></figure><p>把电脑 B 的 MAC 地址绑定到固定 IP。这样不用修改系统网络配置，也不容易把网络改乱。</p><p>如果需要在 Ubuntu 桌面版中手动设置，可以进入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">设置 -&gt; 网络 -&gt; 有线/无线 -&gt; IPv4 -&gt; 手动</span><br></pre></td></tr></table></figure><p>示例：</p><figure class="highlight text"><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">地址：192.168.1.23</span><br><span class="line">子网掩码：255.255.255.0</span><br><span class="line">网关：192.168.1.1</span><br><span class="line">DNS：8.8.8.8 或 114.114.114.114</span><br></pre></td></tr></table></figure><p>注意：固定 IP 不能和局域网内其他设备重复，否则会产生 IP 冲突。</p><h3 id="配置免密登录">配置免密登录</h3><p>每次登录都输入密码比较麻烦，可以在电脑 A 上配置 SSH 密钥。</p><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">ssh-keygen -t ed25519</span><br></pre></td></tr></table></figure><p>一路回车即可。默认会生成：</p><figure class="highlight text"><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">~/.ssh/id_ed25519</span><br><span class="line">~/.ssh/id_ed25519.pub</span><br></pre></td></tr></table></figure><p>把公钥复制到电脑 B：</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">ssh-copy-id dreamer@192.168.1.23</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">ssh dreamer@192.168.1.23</span><br></pre></td></tr></table></figure><p>如果配置成功，就不需要再输入密码。</p><h3 id="设置主机别名">设置主机别名</h3><p>IP 地址不好记，可以在电脑 A 上编辑 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">nano ~/.ssh/config</span><br></pre></td></tr></table></figure><p>添加：</p><figure class="highlight text"><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">Host robot</span><br><span class="line">    HostName 192.168.1.23</span><br><span class="line">    User dreamer</span><br><span class="line">    Port 22</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">ssh robot</span><br></pre></td></tr></table></figure><h2 id="六、传输文件">六、传输文件</h2><p>SSH 也可以配合 <code>scp</code> 在两台电脑之间传文件。</p><p>从电脑 A 复制文件到电脑 B：</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">scp local_file.txt dreamer@192.168.1.23:~/</span><br></pre></td></tr></table></figure><p>从电脑 B 复制文件到电脑 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">scp dreamer@192.168.1.23:~/remote_file.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">scp -r my_folder dreamer@192.168.1.23:~/</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">scp -r my_folder robot:~/</span><br></pre></td></tr></table></figure><h2 id="七、常见问题">七、常见问题</h2><table><thead><tr><th>报错</th><th>常见原因</th><th>处理方法</th></tr></thead><tbody><tr><td><code>Connection refused</code></td><td>网络能找到电脑 B，但 SSH 服务未启动或 22 端口未开放</td><td>在电脑 B 上执行 <code>sudo systemctl status ssh</code>，必要时执行 <code>sudo systemctl start ssh</code></td></tr><tr><td><code>No route to host</code></td><td>网络不通，或 IP 地址写错</td><td>在电脑 A 上执行 <code>ping 192.168.1.23</code>，检查网络和 IP</td></tr><tr><td><code>Permission denied</code></td><td>用户名、密码或密钥不正确</td><td>在电脑 B 上用 <code>whoami</code> 确认用户名，再用 <code>ssh 用户名@IP地址</code> 登录</td></tr><tr><td><code>REMOTE HOST IDENTIFICATION HAS CHANGED</code></td><td>电脑 B 重装系统或主机指纹变化</td><td>在电脑 A 上执行 <code>ssh-keygen -R 192.168.1.23</code> 后重新连接</td></tr></tbody></table><h2 id="八、安全建议">八、安全建议</h2><ul><li>不要使用过于简单的系统密码。</li><li>优先使用 SSH 密钥登录。</li><li>不需要远程控制时，可以关闭 SSH 服务。</li><li>不要随便把 SSH 端口暴露到公网。</li><li>如果必须公网访问，建议先用 VPN、Tailscale、ZeroTier 等方式组网，再使用 SSH 登录。</li></ul><p>关闭 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> systemctl stop ssh</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> ssh</span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>局域网中使用 SSH 远控电脑的核心流程：</p><ol><li>在被控电脑上安装并启动 <code>openssh-server</code>。</li><li>查看被控电脑的用户名和局域网 IP。</li><li>在控制端使用 <code>ssh 用户名@IP地址</code> 登录。</li><li>按需配置固定 IP、免密登录和主机别名。</li><li>使用 <code>scp</code> 在两台电脑之间传输文件。</li></ol><p>配置完成后，远程调试机器人、编译代码、查看 ROS 节点或启动程序都会方便很多。</p>]]></content>
    
    
    <summary type="html">前言 在机器人调试、ROS 开发、服务器维护或多台 Ubuntu 电脑协同时，经常需要从一台电脑登录另一台电脑执行命令。 SSH（Secure Shell）可以让我们通过网络安全地登录远程终端。只要两台电脑在同一个局域网，并且被控电脑开启了 SSH 服务，就可以在本机终端中远程操作它。 一、准备 本文约定：…</summary>
    
    
    
    <category term="开发工具" scheme="https://dreamer198.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="SSH" scheme="https://dreamer198.top/tags/SSH/"/>
    
    <category term="Linux" scheme="https://dreamer198.top/tags/Linux/"/>
    
    <category term="局域网" scheme="https://dreamer198.top/tags/%E5%B1%80%E5%9F%9F%E7%BD%91/"/>
    
  </entry>
  
  <entry>
    <title>FAST-LIO2 从 0 跑通教程</title>
    <link href="https://dreamer198.top/2026/05/06/FAST-LIO2%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/05/06/FAST-LIO2%E6%95%99%E7%A8%8B/</id>
    <published>2026-05-06T07:23:00.000Z</published>
    <updated>2026-07-08T12:44:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>前面已经跑通了 Mid-360S，接下来把 <code>/livox/lidar</code> 和 <code>/livox/imu</code> 接入 FAST-LIO2，实现实时激光惯性里程计和建图。</p><p>本文环境：</p><figure class="highlight text"><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">Ubuntu 22.04</span><br><span class="line">ROS 2 Humble</span><br><span class="line">Livox Mid-360S</span><br><span class="line">FAST_LIO_ROS2</span><br></pre></td></tr></table></figure><span id="more"></span><h2 id="一、准备环境">一、准备环境</h2><p>加载 ROS 2 Humble：</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$ROS_DISTRO</span></span><br></pre></td></tr></table></figure><p>正常应输出：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">humble</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y \</span><br><span class="line">  git \</span><br><span class="line">  cmake \</span><br><span class="line">  build-essential \</span><br><span class="line">  python3-colcon-common-extensions \</span><br><span class="line">  python3-rosdep \</span><br><span class="line">  libpcl-dev \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  ros-humble-pcl-ros \</span><br><span class="line">  ros-humble-rviz2</span><br></pre></td></tr></table></figure><p>如果没有初始化过 <code>rosdep</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> rosdep init</span><br><span class="line">rosdep update</span><br></pre></td></tr></table></figure><p>如果提示已经初始化过，忽略 <code>sudo rosdep init</code> 的报错，直接执行 <code>rosdep update</code> 即可。</p><h2 id="二、确认-Mid-360S-数据正常">二、确认 Mid-360S 数据正常</h2><p>先加载 Livox 驱动环境：</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br></pre></td></tr></table></figure><p>启动 Mid-360S：</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">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><p>这里要注意：跑 FAST-LIO2 时要用 <code>msg_MID360s_launch.py</code>，不要用 <code>rviz_MID360s_launch.py</code>。前者发布的是 FAST-LIO2 需要的 Livox 自定义点云消息。</p><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">ros2 topic list</span><br></pre></td></tr></table></figure><p>应该能看到：</p><figure class="highlight text"><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">/livox/lidar</span><br><span class="line">/livox/imu</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">ros2 topic info /livox/lidar</span><br><span class="line">ros2 topic info /livox/imu</span><br></pre></td></tr></table></figure><p>推荐结果：</p><figure class="highlight text"><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">/livox/lidar: livox_ros_driver2/msg/CustomMsg</span><br><span class="line">/livox/imu: sensor_msgs/msg/Imu</span><br></pre></td></tr></table></figure><p>如果 <code>/livox/lidar</code> 是 <code>sensor_msgs/msg/PointCloud2</code>，说明启动方式不对，换回 <code>msg_MID360s_launch.py</code>。</p><h2 id="三、下载并编译-FAST-LIO2">三、下载并编译 FAST-LIO2</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p ~/fastlio_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/fastlio_ws/src</span><br></pre></td></tr></table></figure><p>下载 ROS2 版本：</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">git <span class="built_in">clone</span> https://github.com/Ericsii/FAST_LIO_ROS2.git --recursive</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/fastlio_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">rosdep install --from-paths src --ignore-src -y</span><br><span class="line">colcon build --symlink-install</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">source</span> ~/fastlio_ws/install/setup.bash</span><br></pre></td></tr></table></figure><p>也可以写入 <code>~/.bashrc</code>：</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">echo</span> <span class="string">&#x27;source ~/livox_ws/install/setup.bash&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;source ~/fastlio_ws/install/setup.bash&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="四、检查配置文件">四、检查配置文件</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/fastlio_ws/src/FAST_LIO_ROS2/config</span><br><span class="line"><span class="built_in">ls</span></span><br></pre></td></tr></table></figure><p>实际可以看到：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">avia.yaml  horizon.yaml  mid360.yaml  ouster64.yaml  velodyne.yaml</span><br></pre></td></tr></table></figure><p>Mid-360S 跑 FAST-LIO2 时直接使用仓库自带的 <code>mid360.yaml</code> 即可：</p><figure class="highlight text"><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">Livox 驱动启动文件：msg_MID360s_launch.py</span><br><span class="line">FAST-LIO2 配置文件：mid360.yaml</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">gedit mid360.yaml</span><br></pre></td></tr></table></figure><p>第一次跑通时，建议先保持默认参数，只确认下面几个关键项：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">common:</span></span><br><span class="line">  <span class="attr">lid_topic:</span> <span class="string">&quot;/livox/lidar&quot;</span></span><br><span class="line">  <span class="attr">imu_topic:</span> <span class="string">&quot;/livox/imu&quot;</span></span><br><span class="line">  <span class="attr">time_sync_en:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="attr">preprocess:</span></span><br><span class="line">  <span class="attr">lidar_type:</span> <span class="number">1</span></span><br><span class="line">  <span class="attr">scan_line:</span> <span class="number">4</span></span><br><span class="line">  <span class="attr">blind:</span> <span class="number">0.5</span></span><br><span class="line">  <span class="attr">timestamp_unit:</span> <span class="number">3</span></span><br><span class="line">  <span class="attr">scan_rate:</span> <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="attr">mapping:</span></span><br><span class="line">  <span class="attr">fov_degree:</span> <span class="number">360.0</span></span><br><span class="line">  <span class="attr">det_range:</span> <span class="number">100.0</span></span><br><span class="line">  <span class="attr">extrinsic_est_en:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="attr">publish:</span></span><br><span class="line">  <span class="attr">map_en:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">scan_publish_en:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">dense_publish_en:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="attr">pcd_save:</span></span><br><span class="line">  <span class="attr">pcd_save_en:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">interval:</span> <span class="number">-1</span></span><br></pre></td></tr></table></figure><p>其中最重要的是：</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td><code>lid_topic</code></td><td>点云话题，保持 <code>/livox/lidar</code></td></tr><tr><td><code>imu_topic</code></td><td>IMU 话题，保持 <code>/livox/imu</code></td></tr><tr><td><code>lidar_type</code></td><td>Livox 系列为 <code>1</code></td></tr><tr><td><code>timestamp_unit</code></td><td>默认 <code>3</code>，不要随意改</td></tr><tr><td><code>extrinsic_est_en</code></td><td>默认 <code>true</code>，先让 FAST-LIO2 在线估计外参</td></tr></tbody></table><p>后续如果已经有准确的雷达-IMU 外参，再把 <code>extrinsic_est_en</code> 改为 <code>false</code>，并填写自己的 <code>extrinsic_T</code> 和 <code>extrinsic_R</code>。</p><h2 id="五、启动-FAST-LIO2">五、启动 FAST-LIO2</h2><p>开两个终端。</p><h3 id="终端-1：启动雷达">终端 1：启动雷达</h3><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><h3 id="终端-2：启动-FAST-LIO2">终端 2：启动 FAST-LIO2</h3><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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/fastlio_ws/install/setup.bash</span><br><span class="line">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></span><br></pre></td></tr></table></figure><p>若不设置rviz:=false，执行后 RViz2 会自动打开，不需要额外再运行 <code>rviz2</code>。</p><p>刚启动后先让雷达静止几秒，再缓慢移动。第一次测试建议在房间、走廊等有明显结构的环境中进行。</p><h2 id="六、查看建图结果">六、查看建图结果</h2><p>RViz2 中重点看这些内容：</p><figure class="highlight text"><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">Fixed Frame: camera_init</span><br><span class="line">PointCloud2: /Laser_map</span><br><span class="line">PointCloud2: /cloud_registered</span><br><span class="line">Odometry: /Odometry</span><br><span class="line">Path: /path</span><br></pre></td></tr></table></figure><p>如果 RViz2 中没有地图，先检查 FAST-LIO2 是否有输出：</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">ros2 topic hz /Laser_map</span><br><span class="line">ros2 topic hz /Odometry</span><br></pre></td></tr></table></figure><p>如果有频率，多半是 RViz2 的 <code>Fixed Frame</code> 或显示项没设对。</p><h2 id="七、保存地图">七、保存地图</h2><p>配置中已经开启：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">pcd_save:</span></span><br><span class="line">  <span class="attr">pcd_save_en:</span> <span class="literal">true</span></span><br><span class="line">  <span class="attr">interval:</span> <span class="number">-1</span></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">ros2 service list | grep map</span><br></pre></td></tr></table></figure><p>如果看到 <code>/map_save</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">ros2 service call /map_save std_srvs/srv/Trigger</span><br></pre></td></tr></table></figure><p>这个 ROS2 版本的 <code>/map_save</code> 会按照配置文件里的 <code>map_file_path</code> 保存地图。<code>mid360.yaml</code> 默认是：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">map_file_path:</span> <span class="string">&quot;./test.pcd&quot;</span></span><br></pre></td></tr></table></figure><p>这里的 <code>./test.pcd</code> 是相对路径，实际会保存在启动 FAST-LIO2 时终端所在的目录。比如在 <code>~</code> 目录启动 FAST-LIO2，地图就是 <code>~/test.pcd</code>。</p><p>建议改成绝对路径，避免保存后找不到文件：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">map_file_path:</span> <span class="string">&quot;/home/dreamer198/fastlio_ws/src/FAST_LIO_ROS2/PCD/test.pcd&quot;</span></span><br></pre></td></tr></table></figure><p>重新启动 FAST-LIO2 后，再调用 <code>/map_save</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">ls</span> -lh ~/fastlio_ws/src/FAST_LIO_ROS2/PCD</span><br></pre></td></tr></table></figure><p>查看 PCD 地图：</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">pcl_viewer ~/fastlio_ws/src/FAST_LIO_ROS2/PCD/test.pcd</span><br></pre></td></tr></table></figure><h2 id="八、录包测试">八、录包测试</h2><p>实机调试时可以录一段 bag，后面离线反复测试：</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">mkdir</span> -p ~/bags</span><br><span class="line"><span class="built_in">cd</span> ~/bags</span><br><span class="line">ros2 bag record /livox/lidar /livox/imu</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">ros2 bag play &lt;bag目录&gt;</span><br></pre></td></tr></table></figure><p>离线测试时，先启动 FAST-LIO2：</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 class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/fastlio_ws/install/setup.bash</span><br><span class="line">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml</span><br></pre></td></tr></table></figure><p>再播放 bag：</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">ros2 bag play ~/bags/&lt;bag目录&gt;</span><br></pre></td></tr></table></figure><h2 id="九、常见问题">九、常见问题</h2><h3 id="1-FAST-LIO2-没有点云输入">1. FAST-LIO2 没有点云输入</h3><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">ros2 topic info /livox/lidar</span><br></pre></td></tr></table></figure><p>应该是：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">livox_ros_driver2/msg/CustomMsg</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">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><h3 id="2-找不到-livox-ros-driver2-msg-CustomMsg">2. 找不到 <code>livox_ros_driver2/msg/CustomMsg</code></h3><p>编译或运行 FAST-LIO2 前，需要先加载 Livox 工作空间：</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/fastlio_ws/install/setup.bash</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 class="built_in">cd</span> ~/fastlio_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/livox_ws/install/setup.bash</span><br><span class="line">colcon build --symlink-install</span><br></pre></td></tr></table></figure><h3 id="3-Jetson-通过-SSH-启动时报-RViz2-显示错误">3. Jetson 通过 SSH 启动时报 RViz2 显示错误</h3><p>如果在 Jetson 上通过 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">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml</span><br></pre></td></tr></table></figure><p>出现类似错误：</p><figure class="highlight text"><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">qt.qpa.xcb: could not connect to display</span><br><span class="line">Could not load the Qt platform plugin &quot;xcb&quot;</span><br></pre></td></tr></table></figure><p>这说明当前 SSH 终端没有图形显示环境，RViz2 打不开。这个错误只影响 RViz2，不代表 FAST-LIO2 本身一定有问题。</p><p><code>mapping.launch.py</code> 默认会启动 RViz2，如果 Jetson 是无屏幕运行，或者只是通过 SSH 远程跑算法，可以先关闭 RViz2：</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">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></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></pre></td><td class="code"><pre><span class="line">ros2 topic list</span><br><span class="line">ros2 topic hz /Odometry</span><br><span class="line">ros2 topic hz /Laser_map</span><br></pre></td></tr></table></figure><p>如果两台电脑配置了 ROS 2 多机通信，也可以在自己的主力电脑上单独打开 RViz2 来看 Jetson 发布的话题。</p><h3 id="4-Jetson-运行时报-libusb-set-option">4. Jetson 运行时报 <code>libusb_set_option</code></h3><p>如果 FAST-LIO2 节点直接退出，并出现：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/lib/aarch64-linux-gnu/libpcl_io.so.1.12: undefined symbol: libusb_set_option</span><br></pre></td></tr></table></figure><p>这通常不是 FAST-LIO2 参数问题，而是 PCL 加载 <code>libusb</code> 时发生了动态库版本冲突。常见原因是系统里同时存在多个 <code>libusb-1.0.so</code>，例如 <code>/usr/local/lib</code> 下的旧版本 <code>libusb</code> 覆盖了 Ubuntu apt 安装的版本，导致 <code>/lib/aarch64-linux-gnu/libpcl_io.so.1.12</code> 找不到 <code>libusb_set_option</code> 符号。</p><p>先检查 PCL 实际链接到了哪个 <code>libusb</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">ldd /lib/aarch64-linux-gnu/libpcl_io.so.1.12 | grep usb</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">nm -D /lib/aarch64-linux-gnu/libusb-1.0.so.0 | grep libusb_set_option</span><br></pre></td></tr></table></figure><p>如果第二条命令能看到 <code>libusb_set_option</code>，说明系统自带的 <code>libusb</code> 是正常的，多半是运行时加载了别处的旧库。可以先用 <code>LD_PRELOAD</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">LD_PRELOAD=/lib/aarch64-linux-gnu/libusb-1.0.so.0 \</span><br><span class="line">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></span><br></pre></td></tr></table></figure><p>如果这样能启动，说明就是 <code>libusb</code> 动态库冲突。接着检查是否存在 <code>/usr/local/lib</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">ls</span> -l /usr/local/lib | grep libusb</span><br><span class="line">ldconfig -p | grep libusb</span><br></pre></td></tr></table></figure><p>如果确认 <code>/usr/local/lib/libusb-1.0.so*</code> 是旧版本残留，可以先备份再刷新动态库缓存：</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> <span class="built_in">mkdir</span> -p /usr/local/lib/libusb_backup</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">mv</span> /usr/local/lib/libusb-1.0.so* /usr/local/lib/libusb_backup/</span><br><span class="line"><span class="built_in">sudo</span> ldconfig</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">ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml rviz:=<span class="literal">false</span></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"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install --reinstall -y libusb-1.0-0 libusb-1.0-0-dev libpcl-dev ros-humble-pcl-ros</span><br></pre></td></tr></table></figure><p>建议处理顺序：</p><ol><li>先加 <code>rviz:=false</code>，排除远程图形界面问题。</li><li>再用 <code>ldd</code> 和 <code>nm</code> 检查 <code>libusb</code> 是否冲突。</li><li>用 <code>LD_PRELOAD</code> 做临时验证。</li><li>确认后清理 <code>/usr/local/lib</code> 里的旧 <code>libusb</code>。</li></ol><h3 id="5-地图漂移明显">5. 地图漂移明显</h3><p>先检查：</p><ul><li>启动后是否静止了几秒。</li><li>是否在玻璃、空旷区域、长直走廊等退化场景。</li><li><code>/livox/lidar</code> 和 <code>/livox/imu</code> 是否稳定发布。</li><li><code>mid360.yaml</code> 中的话题名是否正确。</li><li>外参是否和实际安装差别很大。</li></ul><p>第一次跑通建议先保持 <code>mid360.yaml</code> 默认参数。后续追求精度时，再重点处理时间同步、外参标定和安装刚性。</p><h3 id="6-编译内存不够">6. 编译内存不够</h3><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"><span class="built_in">cd</span> ~/fastlio_ws</span><br><span class="line">colcon build --symlink-install --parallel-workers 2</span><br></pre></td></tr></table></figure><h2 id="参考资料">参考资料</h2><ul><li>FAST-LIO2 官方仓库：<a href="https://github.com/hku-mars/FAST_LIO">https://github.com/hku-mars/FAST_LIO</a></li><li>FAST-LIO2 ROS2 移植版本：<a href="https://github.com/Ericsii/FAST_LIO_ROS2">https://github.com/Ericsii/FAST_LIO_ROS2</a></li><li>livox_ros_driver2：<a href="https://github.com/Livox-SDK/livox_ros_driver2">https://github.com/Livox-SDK/livox_ros_driver2</a></li><li>Livox-SDK2：<a href="https://github.com/Livox-SDK/Livox-SDK2">https://github.com/Livox-SDK/Livox-SDK2</a></li></ul><h2 id="总结">总结</h2><p>Mid-360S 跑 FAST-LIO2 的核心流程：</p><figure class="highlight text"><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">启动 Mid-360S：msg_MID360s_launch.py</span><br><span class="line">编译 FAST_LIO_ROS2</span><br><span class="line">使用配置文件：mid360.yaml</span><br><span class="line">启动 FAST-LIO2：ros2 launch fast_lio mapping.launch.py config_file:=mid360.yaml</span><br><span class="line">RViz2 查看 /Laser_map、/Odometry、/path</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">前言 前面已经跑通了 Mid-360S，接下来把 /livox/lidar 和 /livox/imu 接入 FAST-LIO2，实现实时激光惯性里程计和建图。 本文环境： 1 2 3 4 Ubuntu 22.04 ROS 2 Humble Livox Mid-360S FASTLIOROS2 一、准备环境…</summary>
    
    
    
    <category term="SLAM与定位" scheme="https://dreamer198.top/categories/SLAM%E4%B8%8E%E5%AE%9A%E4%BD%8D/"/>
    
    
    <category term="Livox" scheme="https://dreamer198.top/tags/Livox/"/>
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="FAST-LIO2" scheme="https://dreamer198.top/tags/FAST-LIO2/"/>
    
    <category term="SLAM" scheme="https://dreamer198.top/tags/SLAM/"/>
    
  </entry>
  
  <entry>
    <title>Mid-360S 使用教程</title>
    <link href="https://dreamer198.top/2026/05/06/Mid360s%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/05/06/Mid360s%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</id>
    <published>2026-05-06T06:57:08.000Z</published>
    <updated>2026-07-08T12:44:19.490Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>Livox Mid-360S 是一款常用于移动机器人、无人机、SLAM、建图和避障的 3D 激光雷达。它通过以太网输出数据，在 ROS 2 中通常配合 <code>Livox-SDK2</code> 和 <code>livox_ros_driver2</code> 使用。</p><p>本文记录 Ubuntu 22.04 + ROS 2 Humble 下从硬件连接、网络配置、驱动安装到点云查看的完整流程。</p><span id="more"></span><h2 id="一、硬件与网络准备">一、硬件与网络准备</h2><h3 id="1-连接设备">1. 连接设备</h3><figure class="highlight text"><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">Mid-360S</span><br><span class="line">  ├── 电源：DC 9~27 V</span><br><span class="line">  └── 网口：连接电脑网口或交换机</span><br></pre></td></tr></table></figure><p>注意事项：</p><ul><li>电脑和雷达必须在同一个网段。</li><li>多网卡电脑要确认使用的是连接雷达的有线网卡。</li><li>初次调试建议关闭防火墙，避免 UDP 数据被拦截。</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> ufw <span class="built_in">disable</span></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 addr</span><br></pre></td></tr></table></figure><p>常见网卡名有 <code>enp108s0</code>、<code>eno1</code>、<code>eth0</code> 等。注意：<code>ip addr</code> 输出中接口名后面的冒号只是显示格式，命令里一般不要带这个冒号。</p><h3 id="2-配置电脑-IP">2. 配置电脑 IP</h3><p>电脑 IP 只需要满足两点：</p><ul><li>和雷达 IP 在同一网段。</li><li>不与雷达或局域网中其他设备冲突。</li></ul><p>例如雷达在 <code>192.168.1.0/24</code> 网段，可以把电脑网卡设置为 <code>192.168.1.5</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> ip addr add 192.168.1.5/24 dev enp108s0</span><br><span class="line"><span class="built_in">sudo</span> ip <span class="built_in">link</span> <span class="built_in">set</span> enp108s0 up</span><br></pre></td></tr></table></figure><p>其中 <code>enp108s0</code> 需要替换为自己的有线网卡名。</p><h3 id="3-确认雷达-IP">3. 确认雷达 IP</h3><p>如果已经知道雷达 IP，直接跳到下一步。否则可以通过以下方式获取：</p><ol><li>看设备标签、说明书或出厂配置文档。</li><li>用官方的 Livox 工具在同网段内发现设备。</li><li>使用 Mid-360 协议中的广播发现功能获取 <code>lidar_ip</code> 和 <code>cmd_port</code>。</li></ol><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">ping 192.168.1.100</span><br></pre></td></tr></table></figure><p>如果 ping 不通，优先检查电源、网线、网卡名、电脑 IP 和雷达 IP 是否正确。</p><h2 id="二、安装-SDK-与-ROS-驱动">二、安装 SDK 与 ROS 驱动</h2><h3 id="1-安装-Livox-SDK2">1. 安装 Livox-SDK2</h3><p><code>Livox-SDK2</code> 是 Livox 雷达的底层通信 SDK，<code>livox_ros_driver2</code> 编译时需要依赖它。</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y git cmake build-essential</span><br><span class="line"><span class="built_in">mkdir</span> -p ~/code</span><br><span class="line"><span class="built_in">cd</span> ~/code</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/Livox-SDK/Livox-SDK2.git</span><br><span class="line"><span class="built_in">cd</span> Livox-SDK2</span><br><span class="line"><span class="built_in">mkdir</span> -p build &amp;&amp; <span class="built_in">cd</span> build</span><br><span class="line">cmake ..</span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br><span class="line"><span class="built_in">sudo</span> make install</span><br></pre></td></tr></table></figure><p>如果运行时报找不到动态库，将 <code>/usr/local/lib</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">echo</span> <span class="string">&#x27;export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="2-安装-livox-ros-driver2">2. 安装 livox_ros_driver2</h3><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="built_in">mkdir</span> -p ~/livox_ws/src</span><br><span class="line"><span class="built_in">cd</span> ~/livox_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/Livox-SDK/livox_ros_driver2.git</span><br><span class="line"><span class="built_in">cd</span> ~/livox_ws/src/livox_ros_driver2</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">./build.sh humble</span><br></pre></td></tr></table></figure><p>加载 ROS 2 工作空间：</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> ~/livox_ws</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br></pre></td></tr></table></figure><p>也可以写入 <code>~/.bashrc</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">echo</span> <span class="string">&#x27;source ~/livox_ws/install/setup.bash&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h2 id="三、修改-Mid-360S-配置">三、修改 Mid-360S 配置</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/livox_ws/src/livox_ros_driver2/config</span><br><span class="line"><span class="built_in">ls</span></span><br></pre></td></tr></table></figure><p>通常会看到 <code>MID360_config.json</code>、<code>HAP_config.json</code>、<code>mixed_HAP_MID360_config.json</code> 等文件。Mid-360S 可以按 Mid-360 系列流程配置；如果驱动版本提供了 <code>MID360S_config.json</code> 或 <code>MID360s_config.json</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">gedit MID360s_config.json</span><br></pre></td></tr></table></figure><p>重点检查：</p><table><thead><tr><th>字段</th><th>含义</th></tr></thead><tbody><tr><td><code>host_ip</code></td><td>电脑有线网卡 IP，例如 <code>192.168.1.5</code></td></tr><tr><td><code>lidar_configs[].ip</code></td><td>雷达 IP，例如 <code>192.168.1.100</code></td></tr><tr><td><code>pcl_data_type</code></td><td>点云数据类型，常用 <code>1</code></td></tr><tr><td><code>pattern_mode</code></td><td>扫描模式，常用 <code>0</code>，表示非重复扫描</td></tr><tr><td><code>extrinsic_parameter</code></td><td>Livox 驱动层外参，不等同于 ROS TF</td></tr></tbody></table><p>示例配置片段：</p><figure class="highlight json"><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></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;MID360s&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;lidar_net_info&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;cmd_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56100</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;push_msg_port&quot;</span><span class="punctuation">:</span> <span class="number">56200</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;point_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56300</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;imu_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56400</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;log_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56500</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;host_net_info&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">      <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;host_ip&quot;</span><span class="punctuation">:</span> <span class="string">&quot;192.168.1.5&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;cmd_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56101</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;push_msg_port&quot;</span><span class="punctuation">:</span> <span class="number">56201</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;point_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56301</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;imu_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56401</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;log_data_port&quot;</span><span class="punctuation">:</span> <span class="number">56501</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;lidar_configs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;ip&quot;</span><span class="punctuation">:</span> <span class="string">&quot;192.168.1.100&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;pcl_data_type&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;pattern_mode&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;extrinsic_parameter&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;roll&quot;</span><span class="punctuation">:</span> <span class="number">0.0</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;pitch&quot;</span><span class="punctuation">:</span> <span class="number">0.0</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;yaw&quot;</span><span class="punctuation">:</span> <span class="number">0.0</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;x&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;y&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;z&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><code>extrinsic_parameter</code> 的建议用法：</p><figure class="highlight text"><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">只验证通信：保持全 0</span><br><span class="line">让驱动直接输出补偿后的点云：在 json 中设置外参</span><br><span class="line">让 ROS 系统知道雷达和机体的坐标关系：发布 TF 或在算法中设置外参</span><br></pre></td></tr></table></figure><p>不要在 <code>MID360s_config.json</code>、TF 和 SLAM 配置里重复补偿同一个安装角度。</p><p>如果修改源码目录中的配置后没有生效，可能是运行时加载了 install 目录中的旧配置。建议重新编译并使用软链接安装：</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">cd</span> ~/livox_ws</span><br><span class="line">colcon build --symlink-install</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br></pre></td></tr></table></figure><h2 id="四、启动与验证">四、启动与验证</h2><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">cd</span> ~/livox_ws</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br></pre></td></tr></table></figure><h3 id="1-启动-RViz-查看点云">1. 启动 RViz 查看点云</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">ros2 launch livox_ros_driver2 rviz_MID360_launch.py</span><br></pre></td></tr></table></figure><p>如果有 Mid-360S 专用 launch 文件，则使用对应文件名：</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">ros2 launch livox_ros_driver2 rviz_MID360s_launch.py</span><br></pre></td></tr></table></figure><p>只查看雷达原始坐标系下的点云时，RViz 的 <code>Fixed Frame</code> 通常设置为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">livox_frame</span><br></pre></td></tr></table></figure><h3 id="2-只启动数据发布节点">2. 只启动数据发布节点</h3><p>如果不需要打开 RViz，可以启动消息发布 launch：</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">ros2 launch livox_ros_driver2 msg_MID360s_launch.py</span><br></pre></td></tr></table></figure><p>具体文件名以 <code>launch_ROS2</code> 目录中的实际内容为准。</p><h3 id="3-查看-ROS-2-话题">3. 查看 ROS 2 话题</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">ros2 topic list</span><br></pre></td></tr></table></figure><p>常见话题：</p><figure class="highlight text"><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">/livox/lidar</span><br><span class="line">/livox/imu</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></pre></td><td class="code"><pre><span class="line">ros2 topic info /livox/lidar</span><br><span class="line">ros2 topic info /livox/imu</span><br><span class="line">ros2 topic hz /livox/lidar</span><br><span class="line">ros2 topic hz /livox/imu</span><br></pre></td></tr></table></figure><p>如果 <code>/livox/lidar</code> 是 <code>sensor_msgs/msg/PointCloud2</code>，通常可以直接给 RViz、SLAM、建图或避障算法使用。</p><h3 id="4-录制-rosbag">4. 录制 rosbag</h3><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">mkdir</span> -p ~/bags</span><br><span class="line"><span class="built_in">cd</span> ~/bags</span><br><span class="line">ros2 bag record /livox/lidar /livox/imu</span><br></pre></td></tr></table></figure><p>播放 rosbag：</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">ros2 bag play &lt;bag目录&gt;</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">ros2 bag record -a</span><br></pre></td></tr></table></figure><h2 id="五、接入算法时需要确认什么">五、接入算法时需要确认什么</h2><p>Mid-360S 常用于 FAST-LIO、LIO-SAM、VINS、YOPO 等感知或定位模块。接入算法时重点检查三件事。</p><h3 id="1-话题名称">1. 话题名称</h3><figure class="highlight yaml"><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="attr">pointCloudTopic:</span> <span class="string">&quot;/livox/lidar&quot;</span></span><br><span class="line"><span class="attr">imuTopic:</span> <span class="string">&quot;/livox/imu&quot;</span></span><br></pre></td></tr></table></figure><h3 id="2-坐标系">2. 坐标系</h3><p>驱动默认 frame 常见为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">livox_frame</span><br></pre></td></tr></table></figure><p>如果算法使用 <code>base_link</code>、<code>body</code>、<code>camera_init</code> 等坐标系，需要让 TF、算法配置和消息中的 <code>frame_id</code> 保持一致。</p><h3 id="3-外参位置">3. 外参位置</h3><p>三类外参作用不同，不要混用：</p><figure class="highlight text"><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></pre></td><td class="code"><pre><span class="line">MID360s_config.json / extrinsic_parameter</span><br><span class="line">  -&gt; 影响驱动发布出来的点云坐标</span><br><span class="line">  -&gt; 不会发布 ROS TF</span><br><span class="line"></span><br><span class="line">ROS TF / static_transform_publisher / robot_state_publisher</span><br><span class="line">  -&gt; 描述 ROS 坐标系之间的关系</span><br><span class="line">  -&gt; RViz 和部分算法会按 TF 做坐标变换</span><br><span class="line"></span><br><span class="line">SLAM / 控制算法中的 extrinsic_T、extrinsic_R</span><br><span class="line">  -&gt; 算法内部使用的雷达到机体或雷达到 IMU 外参</span><br></pre></td></tr></table></figure><p>如果雷达前倾 30 度、安装在机体上方 10 cm，可以选择下面两种方式之一。</p><p>方式一：在 json 中设置 <code>extrinsic_parameter</code>，让驱动直接输出补偿后的点云：</p><figure class="highlight json"><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="attr">&quot;extrinsic_parameter&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;roll&quot;</span><span class="punctuation">:</span> <span class="number">0.0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;pitch&quot;</span><span class="punctuation">:</span> <span class="number">-30.0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;yaw&quot;</span><span class="punctuation">:</span> <span class="number">0.0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;x&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;y&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;z&quot;</span><span class="punctuation">:</span> <span class="number">100</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>方式二：保持 json 外参为 0，单独发布静态 TF：</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></pre></td><td class="code"><pre><span class="line">ros2 run tf2_ros static_transform_publisher \</span><br><span class="line">  --x 0 --y 0 --z 0.10 \</span><br><span class="line">  --roll 0 --pitch -0.5236 --yaw 0 \</span><br><span class="line">  --frame-id base_link \</span><br><span class="line">  --child-frame-id livox_frame</span><br></pre></td></tr></table></figure><p>然后将 RViz 的 <code>Fixed Frame</code> 设置为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">base_link</span><br></pre></td></tr></table></figure><p>调试阶段可以先在算法中使用零外参：</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></pre></td><td class="code"><pre><span class="line"><span class="attr">extrinsic_T:</span> [<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>]</span><br><span class="line"><span class="attr">extrinsic_R:</span> [<span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>,</span><br><span class="line">              <span class="number">0.0</span>, <span class="number">1.0</span>, <span class="number">0.0</span>,</span><br><span class="line">              <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>]</span><br></pre></td></tr></table></figure><p>实际运行 SLAM 或控制算法时，再根据安装位置修改。核心原则是：同一个角度或位移只补偿一次。</p><h2 id="六、常见问题">六、常见问题</h2><h3 id="1-ping-不通雷达">1. ping 不通雷达</h3><ul><li>检查雷达电源和网线。</li><li>确认电脑 IP 和雷达 IP 在同一网段。</li><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">ip addr</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ip addr flush dev enp108s0</span><br><span class="line"><span class="built_in">sudo</span> ip addr add 192.168.1.5/24 dev enp108s0</span><br><span class="line"><span class="built_in">sudo</span> ip <span class="built_in">link</span> <span class="built_in">set</span> enp108s0 up</span><br></pre></td></tr></table></figure><h3 id="2-编译时报找不到-Livox-SDK2">2. 编译时报找不到 Livox-SDK2</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">ls</span> /usr/local/lib | grep livox</span><br><span class="line"><span class="built_in">ls</span> /usr/local/include | grep livox</span><br></pre></td></tr></table></figure><p>如果没有结果，重新安装 SDK：</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 class="built_in">cd</span> ~/code/Livox-SDK2/build</span><br><span class="line">cmake ..</span><br><span class="line">make -j$(<span class="built_in">nproc</span>)</span><br><span class="line"><span class="built_in">sudo</span> make install</span><br></pre></td></tr></table></figure><h3 id="3-运行时报找不到动态库">3. 运行时报找不到动态库</h3><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">error while loading shared libraries: liblivox_lidar_sdk_shared.so</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">echo</span> <span class="string">&#x27;export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure><h3 id="4-RViz-打开后没有点云">4. RViz 打开后没有点云</h3><ul><li>只看原始点云时，<code>Fixed Frame</code> 设为 <code>livox_frame</code>。</li><li>通过 TF 看机体系点云时，<code>Fixed Frame</code> 设为 <code>base_link</code>。</li><li>确认 <code>/livox/lidar</code> 存在，并且有发布频率。</li><li>确认 <code>host_ip</code> 是电脑网卡 IP，<code>lidar_configs[].ip</code> 是雷达 IP。</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">ros2 topic list</span><br><span class="line">ros2 topic hz /livox/lidar</span><br></pre></td></tr></table></figure><h3 id="5-改了-json-外参后点云没有变化或还是歪">5. 改了 json 外参后点云没有变化或还是歪</h3><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">grep -n <span class="string">&quot;user_config_path&quot;</span> ~/livox_ws/src/livox_ros_driver2/launch_ROS2/*.py</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/livox_ws</span><br><span class="line">colcon build --symlink-install</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br></pre></td></tr></table></figure><p>如果通过 TF 查看点云，检查 TF 是否存在：</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">ros2 run tf2_ros tf2_echo base_link livox_frame</span><br></pre></td></tr></table></figure><h3 id="6-点云很卡">6. 点云很卡</h3><p>打开对应 launch 文件，适当降低 <code>publish_freq</code>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="string">&quot;publish_freq&quot;</span>: <span class="number">5.0</span>&#125;</span><br></pre></td></tr></table></figure><h3 id="7-多雷达时无法区分话题">7. 多雷达时无法区分话题</h3><p>可以启用 <code>multi_topic</code>，让不同雷达发布到独立话题。配置思路：</p><figure class="highlight text"><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">multi_topic = 1</span><br><span class="line">每个雷达配置不同的 lidar_ip</span><br><span class="line">必要时给每个雷达配置不同网段或不同网卡</span><br></pre></td></tr></table></figure><h2 id="参考资料">参考资料</h2><ul><li>Livox-SDK2：<a href="https://github.com/Livox-SDK/Livox-SDK2">https://github.com/Livox-SDK/Livox-SDK2</a></li><li>livox_ros_driver2：<a href="https://github.com/Livox-SDK/livox_ros_driver2">https://github.com/Livox-SDK/livox_ros_driver2</a></li><li>Mid-360(S) 通信协议：<a href="https://livox-wiki-en.readthedocs.io/en/latest/">https://livox-wiki-en.readthedocs.io/en/latest/</a></li></ul><h2 id="总结">总结</h2><p>Mid-360S 的基本使用流程可以概括为：</p><figure class="highlight text"><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><br><span class="line">  -&gt; 配置电脑网卡 IP</span><br><span class="line">  -&gt; 安装 Livox-SDK2</span><br><span class="line">  -&gt; 编译 livox_ros_driver2</span><br><span class="line">  -&gt; 修改 json 中的 host_ip 和 lidar_configs[].ip</span><br><span class="line">  -&gt; ros2 launch 启动</span><br><span class="line">  -&gt; RViz 查看点云或录制 rosbag</span><br></pre></td></tr></table></figure><p>接入 SLAM 或机器人系统时，重点关注点云话题、IMU 话题、<code>frame_id</code> 和雷达到机体外参。<code>MID360s_config.json</code> 中的 <code>extrinsic_parameter</code> 会影响驱动输出点云，但它不是 ROS TF。</p>]]></content>
    
    
    <summary type="html">前言 Livox Mid-360S 是一款常用于移动机器人、无人机、SLAM、建图和避障的 3D 激光雷达。它通过以太网输出数据，在 ROS 2 中通常配合 Livox-SDK2 和 livoxrosdriver2 使用。 本文记录 Ubuntu 22.04 + ROS 2 Humble 下从硬件连接、网络…</summary>
    
    
    
    <category term="传感器硬件" scheme="https://dreamer198.top/categories/%E4%BC%A0%E6%84%9F%E5%99%A8%E7%A1%AC%E4%BB%B6/"/>
    
    
    <category term="Livox" scheme="https://dreamer198.top/tags/Livox/"/>
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="Mid-360S" scheme="https://dreamer198.top/tags/Mid-360S/"/>
    
    <category term="激光雷达" scheme="https://dreamer198.top/tags/%E6%BF%80%E5%85%89%E9%9B%B7%E8%BE%BE/"/>
    
  </entry>
  
  <entry>
    <title>Docker使用教程</title>
    <link href="https://dreamer198.top/2026/04/30/Docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/"/>
    <id>https://dreamer198.top/2026/04/30/Docker%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</id>
    <published>2026-04-30T07:00:00.000Z</published>
    <updated>2026-08-11T02:27:58.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>Docker 是日常开发里很常用的容器工具，可以把程序和运行环境打包在一起，减少“换台机器就跑不起来”的问题。</p><p>这篇文章先整理 Docker 的基本概念，介绍在 Ubuntu 上的安装方法和常用命令，再给出一个 ROS Noetic 图形化容器的实际示例，方便后续继续搭建自己的仿真环境。</p><span id="more"></span><h2 id="一、先理解镜像和容器">一、先理解镜像和容器</h2><p>可以先把两者理解成下面这个关系：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">镜像 -&gt; 创建 -&gt; 容器</span><br></pre></td></tr></table></figure><ul><li>镜像（Image）：一个静态模板，里面包含程序代码、依赖库和运行环境。</li><li>容器（Container）：由镜像创建出来的运行实例，可以直接启动和使用。</li></ul><p>例如，下面这条命令会基于 <code>ubuntu</code> 镜像启动一个 Ubuntu 容器，并进入容器终端：</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">docker run -it ubuntu bash</span><br></pre></td></tr></table></figure><p>一个镜像可以创建多个容器，而多个容器之间彼此独立。</p><h2 id="二、在-Ubuntu-安装-Docker">二、在 Ubuntu 安装 Docker</h2><p>从下一章开始，本文默认宿主机已经安装 Docker Engine。下面以 Ubuntu 为例，通过 Docker 官方 APT 仓库安装最新版 Docker Engine，同时安装 Buildx 和 Docker Compose 插件。</p><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"><span class="built_in">cat</span> /etc/os-release</span><br><span class="line">dpkg --print-architecture</span><br></pre></td></tr></table></figure><p>下面的方式适用于 Docker 官方当前支持的 64 位 Ubuntu 版本，<code>amd64</code> 和 <code>arm64</code> 都可以使用。支持的 Ubuntu 版本可能会随时间变化，安装前可以在 <a href="https://docs.docker.com/engine/install/ubuntu/">Docker 官方 Ubuntu 安装文档</a> 中确认。其他 Linux 发行版请参考 <a href="https://docs.docker.com/engine/install/">Docker Engine 安装索引</a>。</p><h3 id="1-移除可能冲突的软件包">1. 移除可能冲突的软件包</h3><p>这一步只需要处理系统中已经安装的冲突软件包。下面的命令会先筛选出实际已安装的软件包：如果存在就交给 APT 卸载，如果不存在则只输出提示，不会出现一串“没有找到相匹配的软件包”的信息。</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></pre></td><td class="code"><pre><span class="line">packages_to_remove=<span class="string">&quot;<span class="subst">$(</span></span></span><br><span class="line"><span class="subst"><span class="string">  dpkg-query -W -f=&#x27;$&#123;binary:Package&#125;\t$&#123;db:Status-Abbrev&#125;\n&#x27; \</span></span></span><br><span class="line"><span class="subst"><span class="string">    docker.io docker-compose docker-compose-v2 docker-doc \</span></span></span><br><span class="line"><span class="subst"><span class="string">    docker-buildx podman-docker containerd runc 2&gt;/dev/null | \</span></span></span><br><span class="line"><span class="subst"><span class="string">    awk &#x27;$2 == <span class="string">&quot;ii&quot;</span> &#123; print $1 &#125;&#x27;</span></span></span><br><span class="line"><span class="subst"><span class="string">)</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [ -n <span class="string">&quot;<span class="variable">$packages_to_remove</span>&quot;</span> ]; <span class="keyword">then</span></span><br><span class="line">  <span class="built_in">sudo</span> apt remove <span class="variable">$packages_to_remove</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;未检测到冲突软件包，可以继续安装 Docker。&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>这一步只移除可能与 Docker 官方软件包冲突的程序，不会自动删除 <code>/var/lib/docker</code> 里的已有镜像、容器和卷。如果这台机器已经在使用其他容器环境，先确认这些软件包没有被其他项目依赖，再执行卸载。</p><h3 id="2-添加-Docker-官方-APT-仓库">2. 添加 Docker 官方 APT 仓库</h3><p>先安装下载和证书工具，并添加 Docker 的 GPG 密钥：</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="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y ca-certificates curl</span><br><span class="line"><span class="built_in">sudo</span> install -m 0755 -d /etc/apt/keyrings</span><br><span class="line"><span class="built_in">sudo</span> curl -fsSL https://download.docker.com/linux/ubuntu/gpg \</span><br><span class="line">  -o /etc/apt/keyrings/docker.asc</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chmod</span> a+r /etc/apt/keyrings/docker.asc</span><br></pre></td></tr></table></figure><p>再添加与当前 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><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/apt/sources.list.d/docker.sources &gt; /dev/null &lt;&lt;<span class="string">EOF</span></span><br><span class="line"><span class="string">Types: deb</span></span><br><span class="line"><span class="string">URIs: https://download.docker.com/linux/ubuntu</span></span><br><span class="line"><span class="string">Suites: $(. /etc/os-release &amp;&amp; echo &quot;$&#123;UBUNTU_CODENAME:-$VERSION_CODENAME&#125;&quot;)</span></span><br><span class="line"><span class="string">Components: stable</span></span><br><span class="line"><span class="string">Architectures: $(dpkg --print-architecture)</span></span><br><span class="line"><span class="string">Signed-By: /etc/apt/keyrings/docker.asc</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> apt update</span><br></pre></td></tr></table></figure><h3 id="3-安装并验证-Docker">3. 安装并验证 Docker</h3><p>安装 Docker Engine、命令行工具、containerd、Buildx 和 Compose 插件：</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="built_in">sudo</span> apt install -y \</span><br><span class="line">  docker-ce \</span><br><span class="line">  docker-ce-cli \</span><br><span class="line">  containerd.io \</span><br><span class="line">  docker-buildx-plugin \</span><br><span class="line">  docker-compose-plugin</span><br></pre></td></tr></table></figure><p>启动 Docker，并设置为开机自动启动：</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">enable</span> --now docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl status docker --no-pager</span><br></pre></td></tr></table></figure><p>先查看 Docker Engine 和 Compose 的版本：</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> docker version</span><br><span class="line">docker compose version</span><br></pre></td></tr></table></figure><p><code>docker version</code> 同时显示 <code>Client</code> 和 <code>Server</code> 信息，并且前面的 Docker 服务状态为 <code>active</code>，就说明 Docker Engine 已经安装并启动成功。<code>docker compose version</code> 能输出版本号，则说明 Compose 插件也已安装。</p><p>接下来运行官方测试容器，额外检查 Docker Hub 网络、镜像拉取和容器运行是否都正常：</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> docker run --<span class="built_in">rm</span> hello-world</span><br></pre></td></tr></table></figure><p>第一次执行时出现下面这行是正常现象，它只表示本地还没有这个镜像，Docker 正准备从 Docker Hub 下载：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Unable to find image &#x27;hello-world:latest&#x27; locally</span><br></pre></td></tr></table></figure><p>终端最终出现 <code>Hello from Docker!</code>，说明 Docker 已经可以正常拉取并运行容器。如果随后出现 <code>i/o timeout</code>、<code>EOF</code> 或连接超时，而 <code>docker version</code> 的 Client 和 Server 信息正常，通常是 Docker Hub 网络或代理问题，并不代表 Docker 安装失败，可以参考后文“拉取镜像卡住、超时或出现 EOF”。</p><h3 id="4-配置当前用户免-sudo-使用-Docker">4. 配置当前用户免 <code>sudo</code> 使用 Docker</h3><p>后面的示例直接使用 <code>docker</code> 命令。为了不在每条命令前添加 <code>sudo</code>，可以把当前用户加入 <code>docker</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> usermod -aG docker <span class="string">&quot;<span class="variable">$USER</span>&quot;</span></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">newgrp docker</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">docker run --<span class="built_in">rm</span> hello-world</span><br></pre></td></tr></table></figure><p>需要注意，<code>docker</code> 用户组能够控制 Docker daemon，实际拥有接近 <code>root</code> 的系统权限。只应把可信用户加入这个组；如果不希望授予这项权限，就保留默认配置，并在 Docker 命令前使用 <code>sudo</code>。更多说明可以参考 <a href="https://docs.docker.com/engine/install/linux-postinstall/">Docker 官方 Linux 安装后配置</a>。</p><h3 id="5-使用-NVIDIA-GPU-前的额外准备">5. 使用 NVIDIA GPU 前的额外准备</h3><p>安装 Docker Engine 并不会自动提供 GPU 容器支持。后文的 <code>--gpus all</code> 参数还要求宿主机已经正确安装 NVIDIA 驱动和 NVIDIA Container Toolkit。</p><p>先在宿主机执行 <code>nvidia-smi</code>，确认显卡驱动正常，再按照 <a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html">NVIDIA Container Toolkit 官方安装文档</a> 安装工具包。安装后配置 Docker runtime 并重启 Docker：</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> nvidia-ctk runtime configure --runtime=docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart docker</span><br></pre></td></tr></table></figure><p>可以用 NVIDIA 官方示例检查容器是否能够访问显卡：</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">docker run --<span class="built_in">rm</span> --runtime=nvidia --gpus all ubuntu nvidia-smi</span><br></pre></td></tr></table></figure><p>如果不需要使用 NVIDIA GPU，跳过这一小节，并在后面的 <code>docker run</code> 命令中去掉 <code>--gpus all</code> 和 NVIDIA 相关环境变量即可。</p><h2 id="三、常用命令速查">三、常用命令速查</h2><table><thead><tr><th>功能</th><th>命令</th></tr></thead><tbody><tr><td>查看 Docker 状态</td><td><code>docker info</code></td></tr><tr><td>查看本地镜像</td><td><code>docker images</code></td></tr><tr><td>拉取镜像</td><td><code>docker pull &lt;镜像名&gt;</code></td></tr><tr><td>查看正在运行的容器</td><td><code>docker ps</code></td></tr><tr><td>查看所有容器</td><td><code>docker ps -a</code></td></tr><tr><td>查看容器日志</td><td><code>docker logs &lt;容器名或容器ID&gt;</code></td></tr><tr><td>停止容器</td><td><code>docker stop &lt;容器名或容器ID&gt;</code></td></tr><tr><td>启动并进入容器</td><td><code>docker start -ai &lt;容器名或容器ID&gt;</code></td></tr><tr><td>进入正在运行的容器</td><td><code>docker exec -it &lt;容器名或容器ID&gt; bash</code></td></tr><tr><td>删除容器</td><td><code>docker rm &lt;容器名或容器ID&gt;</code></td></tr><tr><td>删除镜像</td><td><code>docker rmi &lt;镜像名或镜像ID&gt;</code></td></tr></tbody></table><p>下面把这些命令按使用场景简单展开一下。</p><h3 id="1-查看-Docker-是否正常工作">1. 查看 Docker 是否正常工作</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">docker info</span><br></pre></td></tr></table></figure><p>这个命令可以查看 Docker 的整体运行状态，比如版本、容器数量、镜像数量、存储目录和系统信息。</p><p>如果执行时提示无法连接 Docker daemon，可以先检查服务状态：</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 docker</span><br></pre></td></tr></table></figure><p>如果 Docker 服务没有启动，再执行：</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 start docker</span><br></pre></td></tr></table></figure><h3 id="2-查看本地镜像">2. 查看本地镜像</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">docker images</span><br></pre></td></tr></table></figure><p>用于查看本机已经下载或构建好的镜像。</p><h3 id="3-拉取镜像">3. 拉取镜像</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">docker pull osrf/ros:noetic-desktop-full</span><br></pre></td></tr></table></figure><p>这条命令会下载 ROS Noetic 的桌面完整版镜像，后面运行 ROS 图形程序时会直接用到。</p><p>如果是在 Jetson 这类 ARM64 设备上，只需要 ROS Noetic 的基础运行环境，可以拉取更轻量的 <code>ros-base</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">docker pull --platform linux/arm64/v8 ros:noetic-ros-base-focal</span><br></pre></td></tr></table></figure><p>在 Jetson 本机上执行时，Docker 通常会自动选择 ARM64 镜像；这里加上 <code>--platform linux/arm64/v8</code> 是为了把架构写得更明确。需要注意的是，<code>ros-base</code> 不包含 RViz、Gazebo 这类桌面图形工具，后面如果要运行图形程序，仍然需要安装对应软件包或使用桌面版镜像。</p><h3 id="4-查看容器">4. 查看容器</h3><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">docker ps</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">docker ps -a</span><br></pre></td></tr></table></figure><p>如果容器状态是 <code>Exited</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">docker logs &lt;容器名或容器ID&gt;</span><br></pre></td></tr></table></figure><h3 id="5-停止、重启和进入容器">5. 停止、重启和进入容器</h3><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">docker stop &lt;容器名或容器ID&gt;</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">docker start -ai &lt;容器名或容器ID&gt;</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">docker <span class="built_in">exec</span> -it &lt;容器名或容器ID&gt; bash</span><br></pre></td></tr></table></figure><h3 id="6-删除容器和镜像">6. 删除容器和镜像</h3><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">docker <span class="built_in">rm</span> &lt;容器名或容器ID&gt;</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">docker rmi &lt;镜像名或镜像ID&gt;</span><br></pre></td></tr></table></figure><p>如果镜像仍被某个容器占用，需要先删除对应容器。</p><h2 id="四、创建一个-ROS-Noetic-图形化容器">四、创建一个 ROS Noetic 图形化容器</h2><p>这一节给一个比较常见的使用方式：在 Docker 里运行 ROS Noetic，同时把宿主机的图形界面和整个 <code>/root</code> 目录挂载进去。</p><h3 id="1-允许容器访问宿主机图形界面">1. 允许容器访问宿主机图形界面</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><p>这一步的作用是允许本机 <code>root</code> 用户访问当前图形显示服务，容器里的 RViz、rqt、Gazebo 等图形程序才能在宿主机屏幕上显示出来。</p><p>相比 <code>xhost +local:root</code>，这里更推荐 <code>xhost +SI:localuser:root</code>，因为它只放行本机的 <code>root</code> 用户，范围更收敛一些。很多 ROS / Gazebo 镜像默认就是以 <code>root</code> 身份运行，所以通常够用。</p><h3 id="2-创建并进入容器">2. 创建并进入容器</h3><p>在执行 <code>docker run</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">mkdir</span> -p ~/docker/ros_root</span><br></pre></td></tr></table></figure><p>这样可以避免路径写错时被 Docker 悄悄新建成空目录，也更方便你确认权限和目录位置。</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></pre></td><td class="code"><pre><span class="line">docker run -it \</span><br><span class="line">  --name ros_noetic \</span><br><span class="line">  --network host \</span><br><span class="line">  --ipc host \</span><br><span class="line">  --gpus all \</span><br><span class="line">  -e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">  -e XAUTHORITY=/root/.Xauthority \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  -e NVIDIA_DRIVER_CAPABILITIES=all \</span><br><span class="line">  -v /tmp/.X11-unix:/tmp/.X11-unix \</span><br><span class="line">  -v <span class="variable">$HOME</span>/.Xauthority:/root/.Xauthority:ro \</span><br><span class="line">  -v ~/docker/ros_root:/root \</span><br><span class="line">  osrf/ros:noetic-desktop-full \</span><br><span class="line">  bash</span><br></pre></td></tr></table></figure><p>这条命令会创建并进入一个名为 <code>ros_noetic</code> 的容器。几个关键参数的作用如下：</p><ul><li><code>--network host</code>：共享宿主机网络，ROS 通信更省事。</li><li><code>--ipc host</code>：共享 IPC，某些图形和仿真程序更稳定。</li><li><code>--gpus all</code>：把 GPU 暴露给容器；如果机器没有 NVIDIA 环境，可以先去掉这一项。</li><li><code>-e DISPLAY=$DISPLAY</code> 和 <code>-v /tmp/.X11-unix:/tmp/.X11-unix</code>：让容器里的图形程序能显示到宿主机。</li><li><code>-e XAUTHORITY=/root/.Xauthority</code> 和 <code>-v $HOME/.Xauthority:/root/.Xauthority:ro</code>：把宿主机当前图形会话的 X11 认证信息挂进容器，很多情况下可以减少手动执行 <code>xhost</code> 的需要。</li><li><code>-v ~/docker/ros_root:/root</code>：把容器内整个 <code>/root</code> 目录挂载到宿主机，代码、配置和 home 目录下的其他文件都会一起保存。</li></ul><p>如果后续需要让容器里的程序访问宿主机串口或 USB 设备，例如 <code>/dev/ttyACM0</code>、<code>/dev/ttyUSB0</code>、<code>/dev/ttyTHS1</code>，通常要在创建容器时就加上 <code>--device</code> 参数：</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><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">docker run -it \</span><br><span class="line">  --name ros_noetic \</span><br><span class="line">  --network host \</span><br><span class="line">  --ipc host \</span><br><span class="line">  --runtime=nvidia \</span><br><span class="line">  --gpus all \</span><br><span class="line">  --device=/dev/ttyACM0 \</span><br><span class="line">  --device=/dev/ttyTHS1 \</span><br><span class="line">  -e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">  -e XAUTHORITY=/root/.Xauthority \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  -e NVIDIA_DRIVER_CAPABILITIES=all \</span><br><span class="line">  -v /tmp/.X11-unix:/tmp/.X11-unix \</span><br><span class="line">  -v <span class="variable">$HOME</span>/.Xauthority:/root/.Xauthority:ro \</span><br><span class="line">  -v ~/docker/ros_root:/root \</span><br><span class="line">  osrf/ros:noetic-desktop-full \</span><br><span class="line">  bash</span><br></pre></td></tr></table></figure><p>这里有几个需要注意的点：</p><ul><li><code>--network host</code> 和目录挂载并不会自动让容器获得串口访问权限，设备需要单独通过 <code>--device</code> 暴露进去。</li><li><code>--device=/dev/ttyACM0</code> 这种写法要求宿主机上当前确实存在这个设备，否则容器创建时会报错。</li><li>如果你创建容器时没有指定 <code>--device</code>，后续不能通过 <code>docker start</code> 或 <code>docker exec</code> 再补上，一般需要删除旧容器后重新创建。</li><li>如果只是不确定设备号，可以先在宿主机执行 <code>ls /dev/ttyUSB* /dev/ttyACM* /dev/ttyTHS*</code> 确认实际存在的设备，再决定要不要加到启动命令里。</li></ul><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">exit</span></span><br></pre></td></tr></table></figure><h3 id="3-后续重新进入容器">3. 后续重新进入容器</h3><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">docker start -ai ros_noetic</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">docker <span class="built_in">exec</span> -it ros_noetic bash</span><br></pre></td></tr></table></figure><h2 id="五、在容器里编译-Diff-Planner">五、在容器里编译 Diff-Planner</h2><p>下面以 <code>Diff-Planner</code> 为例，演示如何在容器里编译一个 ROS1 工程。</p><p>依赖安装<br>apt update<br>apt-get install git</p><h3 id="1-进入源码目录并下载项目">1. 进入源码目录并下载项目</h3><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">mkdir</span> -p /root/catkin_ws/src</span><br><span class="line"><span class="built_in">cd</span> /root/catkin_ws/src</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/DifferentialRobotics/Diff-Planner.git</span><br></pre></td></tr></table></figure><h3 id="2-安装依赖并编译">2. 安装依赖并编译</h3><p><code>Diff-Planner</code> 项目本身就是一个 <code>catkin</code> 工作空间，因此这里直接进入项目目录编译：</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><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /root/catkin_ws/src/Diff-Planner</span><br><span class="line"></span><br><span class="line">apt update</span><br><span class="line">apt install -y \</span><br><span class="line">  python3-catkin-tools \</span><br><span class="line">  ros-noetic-cmake-modules \</span><br><span class="line">  ros-noetic-pcl-ros \</span><br><span class="line">  ros-noetic-pcl-conversions \</span><br><span class="line">  ros-noetic-mavros \</span><br><span class="line">  ros-noetic-mavros-msgs \</span><br><span class="line">  libarmadillo-dev \</span><br><span class="line">  libeigen3-dev \</span><br><span class="line">  libpcl-dev \</span><br><span class="line">  qtbase5-dev \</span><br><span class="line">  zsh</span><br><span class="line"></span><br><span class="line"><span class="built_in">source</span> /opt/ros/noetic/setup.bash</span><br><span class="line">catkin_make</span><br></pre></td></tr></table></figure><h3 id="3-启动测试">3. 启动测试</h3><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">cd</span> /root/catkin_ws/src/Diff-Planner</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">roslaunch diff_planner run_sim_swarm.launch</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">docker <span class="built_in">exec</span> -it ros_noetic bash</span><br><span class="line"><span class="built_in">cd</span> /root/catkin_ws/src/Diff-Planner</span><br><span class="line"><span class="built_in">source</span> devel/setup.bash</span><br><span class="line">./sh_files/pub_swarm_trigger.sh</span><br></pre></td></tr></table></figure><h2 id="六、挂载-root-的作用">六、挂载 <code>/root</code> 的作用</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">-v ~/docker/ros_root:/root</span><br></pre></td></tr></table></figure><p>表示宿主机目录 <code>~/docker/ros_root</code> 和容器目录 <code>/root</code> 是同步的。</p><p>也就是说：</p><ul><li>你在宿主机里修改 <code>~/docker/ros_root</code> 的内容，容器里会同步变化。</li><li>你在容器里放在 <code>/root</code> 下的代码、配置、日志和脚本，宿主机里也能直接看到。</li></ul><p>这样即使容器删掉了，<code>/root</code> 目录下的数据依然保存在宿主机，不容易丢。</p><p>不过要注意，挂载 <code>/root</code> 只能保存用户目录里的内容，不能代替整个容器系统本身。比如通过 <code>apt install</code> 安装到 <code>/usr</code>、<code>/etc</code>、<code>/var</code> 下的系统级依赖，仍然属于容器环境的一部分。</p><h2 id="七、如何保存容器环境">七、如何保存容器环境</h2><p>前面挂载 <code>/root</code> 可以保存代码、配置和日志，但如果你在容器里额外安装了软件包、改了系统环境，单纯保留 <code>/root</code> 还不够。这时候通常有两种方式保存容器环境。</p><h3 id="1-用-docker-commit-保存当前容器">1. 用 <code>docker commit</code> 保存当前容器</h3><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">docker commit ros_noetic ros_noetic_saved:latest</span><br></pre></td></tr></table></figure><p>这条命令会把当前容器 <code>ros_noetic</code> 保存成一个新的镜像 <code>ros_noetic_saved:latest</code>。</p><p>这里要注意：<code>docker commit</code> 保存的是容器当前文件系统状态，但像 <code>-v ~/docker/ros_root:/root</code> 这种挂载到宿主机的目录，本来就不属于镜像本体，所以不会被重新打包进镜像里。不过这部分数据已经在宿主机上，一般也不需要靠 <code>commit</code> 再保存一次。</p><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></pre></td><td class="code"><pre><span class="line">docker run -it \</span><br><span class="line">  --name ros_noetic \</span><br><span class="line">  --network host \</span><br><span class="line">  --ipc host \</span><br><span class="line">  --gpus all \</span><br><span class="line">  -e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  -e NVIDIA_DRIVER_CAPABILITIES=all \</span><br><span class="line">  -v /tmp/.X11-unix:/tmp/.X11-unix \</span><br><span class="line">  -v ~/docker/ros_root:/root \</span><br><span class="line">  ros_noetic_saved:latest \</span><br><span class="line">  bash</span><br></pre></td></tr></table></figure><p>如果你之后需要补 <code>--device</code>、修改挂载目录，或者调整其他启动参数，比较常见的做法就是：</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">docker commit ros_noetic ros_noetic_saved:latest</span><br><span class="line">docker stop ros_noetic</span><br><span class="line">docker <span class="built_in">rm</span> ros_noetic</span><br></pre></td></tr></table></figure><p>然后再用新的 <code>docker run</code> 命令重新创建。</p><p><code>docker commit</code> 的优点是快，适合先把当前能跑通的环境存下来；缺点是步骤不可追踪，后面时间长了不容易回忆当初到底改过什么。</p><h3 id="2-用-Dockerfile-固化环境">2. 用 <code>Dockerfile</code> 固化环境</h3><p>如果这个环境后续要长期维护，或者以后要迁移到别的机器，更推荐把安装步骤写成 <code>Dockerfile</code>。</p><p>例如：</p><figure class="highlight dockerfile"><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="keyword">FROM</span> osrf/ros:noetic-desktop-full</span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="language-bash"> apt update &amp;&amp; apt install -y \</span></span><br><span class="line"><span class="language-bash">  python3-catkin-tools \</span></span><br><span class="line"><span class="language-bash">  ros-noetic-cmake-modules \</span></span><br><span class="line"><span class="language-bash">  ros-noetic-pcl-ros \</span></span><br><span class="line"><span class="language-bash">  ros-noetic-pcl-conversions \</span></span><br><span class="line"><span class="language-bash">  ros-noetic-mavros \</span></span><br><span class="line"><span class="language-bash">  ros-noetic-mavros-msgs \</span></span><br><span class="line"><span class="language-bash">  libarmadillo-dev \</span></span><br><span class="line"><span class="language-bash">  libeigen3-dev \</span></span><br><span class="line"><span class="language-bash">  libpcl-dev \</span></span><br><span class="line"><span class="language-bash">  qtbase5-dev \</span></span><br><span class="line"><span class="language-bash">  zsh</span></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">docker build -t ros_noetic_custom:latest .</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></pre></td><td class="code"><pre><span class="line">docker run -it \</span><br><span class="line">  --name ros_noetic \</span><br><span class="line">  --network host \</span><br><span class="line">  --ipc host \</span><br><span class="line">  --gpus all \</span><br><span class="line">  -e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">  -e QT_X11_NO_MITSHM=1 \</span><br><span class="line">  -e NVIDIA_DRIVER_CAPABILITIES=all \</span><br><span class="line">  -v /tmp/.X11-unix:/tmp/.X11-unix \</span><br><span class="line">  -v ~/docker/ros_root:/root \</span><br><span class="line">  ros_noetic_custom:latest \</span><br><span class="line">  bash</span><br></pre></td></tr></table></figure><p><code>Dockerfile</code> 的优点是可复现、可维护、方便迁移；缺点是第一次整理会比 <code>docker commit</code> 麻烦一些。</p><h3 id="3-该怎么选">3. 该怎么选</h3><ul><li>只是临时保存当前能跑通的环境：优先用 <code>docker commit</code></li><li>想长期维护，或者准备换机器复现：更推荐写 <code>Dockerfile</code></li><li>比较实用的做法是：先 <code>docker commit</code> 兜底，再有空把环境整理成 <code>Dockerfile</code></li></ul><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">docker save -o ros_noetic_saved.tar ros_noetic_saved:latest</span><br><span class="line">docker load -i ros_noetic_saved.tar</span><br></pre></td></tr></table></figure><h2 id="八、常见问题">八、常见问题</h2><h3 id="1-无法连接-Docker-daemon">1. 无法连接 Docker daemon</h3><p>先检查 Docker 服务：</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 docker</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 start docker</span><br></pre></td></tr></table></figure><h3 id="2-拉取镜像卡住、超时或出现-EOF">2. 拉取镜像卡住、超时或出现 EOF</h3><p>如果 <code>docker version</code> 能正常显示 Client 和 Server，但执行 <code>docker pull</code> 或 <code>docker run</code> 时长时间停在下面这行：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Unable to find image &#x27;hello-world:latest&#x27; locally</span><br></pre></td></tr></table></figure><p>或者最终出现类似报错：</p><figure class="highlight text"><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">dial tcp &lt;IP地址&gt;:443: i/o timeout</span><br><span class="line">failed to do request: ... EOF</span><br></pre></td></tr></table></figure><p>说明 Docker Engine 本身已经启动，问题发生在访问 Docker Hub 的过程中。</p><h4 id="先检查宿主机能否直连-Docker-Hub">先检查宿主机能否直连 Docker Hub</h4><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">curl -I --connect-timeout 10 https://registry-1.docker.io/v2/</span><br><span class="line">curl -I --connect-timeout 10 https://auth.docker.io/</span><br></pre></td></tr></table></figure><p>只要能够收到 HTTP 响应，就说明对应地址可达。其中 <code>registry-1.docker.io</code> 返回 <code>401 Unauthorized</code> 是正常现象，表示已经连到 Registry，只是当前请求没有携带镜像仓库的认证令牌；<code>auth.docker.io</code> 根路径返回 <code>404</code> 也不代表网络失败。</p><p>如果两条命令都连接超时，可以先更换网络或使用手机热点复测。如果当前网络必须通过代理访问外网，则需要为 Docker daemon 单独配置代理。Docker daemon 是系统服务，不会自动继承浏览器、桌面环境或当前终端里的代理设置。</p><h4 id="为-Docker-daemon-配置-HTTP-代理">为 Docker daemon 配置 HTTP 代理</h4><p>先在代理软件中确认 HTTP 或 Mixed 代理端口。下面以本机端口 <code>7890</code> 为例，实际使用时必须换成代理软件显示的端口：</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">PROXY_URL=http://127.0.0.1:7890</span><br><span class="line"></span><br><span class="line">curl -I -x <span class="string">&quot;<span class="variable">$PROXY_URL</span>&quot;</span> --connect-timeout 10 \</span><br><span class="line">  https://registry-1.docker.io/v2/</span><br></pre></td></tr></table></figure><p>如果通过代理能够收到 <code>401 Unauthorized</code>，说明这条代理链路可以访问 Docker Registry。接着执行：</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 edit docker</span><br></pre></td></tr></table></figure><p>在打开的编辑器顶部非注释区域填入以下内容，并把两个 <code>7890</code> 都替换为实际端口：</p><figure class="highlight ini"><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="section">[Service]</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;HTTP_PROXY=http://127.0.0.1:7890&quot;</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;HTTPS_PROXY=http://127.0.0.1:7890&quot;</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;NO_PROXY=localhost,127.0.0.1,::1&quot;</span></span><br></pre></td></tr></table></figure><p>对于 Clash 这类 HTTP / Mixed 代理，即使目标网站使用 HTTPS，<code>HTTPS_PROXY</code> 的代理地址通常仍写成 <code>http://127.0.0.1:&lt;端口&gt;</code>，由 HTTP CONNECT 建立加密连接。不要把 SOCKS 端口误填到这个示例里。</p><p>保存退出后，重新加载 systemd 配置并重启 Docker：</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> systemctl daemon-reload</span><br><span class="line"><span class="built_in">sudo</span> systemctl restart docker</span><br><span class="line"><span class="built_in">sudo</span> systemctl show --property=Environment docker</span><br></pre></td></tr></table></figure><p>确认输出中已经包含 <code>HTTP_PROXY</code> 和 <code>HTTPS_PROXY</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> docker pull hello-world</span><br><span class="line"><span class="built_in">sudo</span> docker run --<span class="built_in">rm</span> hello-world</span><br></pre></td></tr></table></figure><p>这种配置依赖本机代理软件持续运行。如果代理软件退出或端口发生变化，Docker Hub 拉取会再次失败。完整配置说明可以参考 <a href="https://docs.docker.com/engine/daemon/proxy/">Docker 官方 daemon 代理文档</a>。如果没有可用代理，可以使用学校、单位或自己维护的可信 Registry Mirror，不建议随意添加来源不明的公共镜像站。</p><h4 id="代理可用但仍然出现-EOF">代理可用但仍然出现 <code>EOF</code></h4><p>有时 Registry 首页通过代理可以访问，但 Docker 在请求镜像清单时仍然出现 <code>EOF</code>。可以分别测试默认协议和 HTTP/1.1：</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></pre></td><td class="code"><pre><span class="line">PROXY_URL=http://127.0.0.1:7890</span><br><span class="line">MANIFEST_URL=https://registry-1.docker.io/v2/library/hello-world/manifests/latest</span><br><span class="line"></span><br><span class="line">curl -I -x <span class="string">&quot;<span class="variable">$PROXY_URL</span>&quot;</span> --connect-timeout 10 <span class="string">&quot;<span class="variable">$MANIFEST_URL</span>&quot;</span></span><br><span class="line">curl --http1.1 -I -x <span class="string">&quot;<span class="variable">$PROXY_URL</span>&quot;</span> --connect-timeout 10 <span class="string">&quot;<span class="variable">$MANIFEST_URL</span>&quot;</span></span><br></pre></td></tr></table></figure><p>如果第一条稳定出现 <code>unexpected EOF</code>，而第二条立即返回正常的 <code>401 Unauthorized</code>，说明当前代理链路的 HTTP/2 通信存在兼容问题。可以先尝试更新代理软件或更换代理节点；如果问题仍然存在，再执行：</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 edit docker</span><br></pre></td></tr></table></figure><p>在原有代理配置中增加 <code>GODEBUG</code>，完整内容类似下面这样：</p><figure class="highlight ini"><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="section">[Service]</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;HTTP_PROXY=http://127.0.0.1:7890&quot;</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;HTTPS_PROXY=http://127.0.0.1:7890&quot;</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;NO_PROXY=localhost,127.0.0.1,::1&quot;</span></span><br><span class="line"><span class="attr">Environment</span>=<span class="string">&quot;GODEBUG=http2client=0&quot;</span></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></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 restart docker</span><br><span class="line"><span class="built_in">sudo</span> docker pull hello-world</span><br></pre></td></tr></table></figure><p><code>GODEBUG=http2client=0</code> 会让 Docker daemon 使用的 Go HTTP 客户端禁用 HTTP/2，回退到 HTTP/1.1。它是针对上述特定兼容问题的处理方式，不应作为所有电脑的默认配置。相关机制可以参考 <a href="https://go.dev/doc/godebug">Go 官方 GODEBUG 文档</a>。</p><p>如果还无法判断具体失败位置，可以查看 Docker 最近的服务日志：</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> journalctl -u docker --since <span class="string">&quot;5 minutes ago&quot;</span> --no-pager</span><br></pre></td></tr></table></figure><h3 id="3-Gazebo-启动时报图形权限错误">3. Gazebo 启动时报图形权限错误</h3><p>如果宿主机重启后，在容器里运行 <code>gazebo</code> 出现下面这个报错：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Authorization required, but no authorization protocol specified</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><p>通常在宿主机重启、注销重登，或者 X / 图形会话重启后，都需要再执行一遍。因为 <code>xhost</code> 授权是给当前图形会话的，不是永久系统配置。</p><p>如果你希望每次登录图形界面后自动执行，可以把下面这行加入宿主机的登录启动脚本，例如 <code>~/.profile</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><p>这样做的效果更接近“半永久配置”：不是系统级永久放行，而是每次图形会话启动后自动重新授权。</p><p>如果想再干净一点，可以在创建容器时正确挂载 X11 认证文件：</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">-e DISPLAY=<span class="variable">$DISPLAY</span> \</span><br><span class="line">-e XAUTHORITY=/root/.Xauthority \</span><br><span class="line">-v /tmp/.X11-unix:/tmp/.X11-unix:rw \</span><br><span class="line">-v <span class="variable">$HOME</span>/.Xauthority:/root/.Xauthority:ro</span><br></pre></td></tr></table></figure><p>这种方式会把宿主机当前用户的 <code>.Xauthority</code> 挂到容器内，很多情况下即使不手动执行 <code>xhost</code>，容器里的 Gazebo、RViz 也能直接连接显示服务。</p><p>不过在 ROS / Gazebo 的 Docker 环境里，最省事的做法通常还是：宿主机每次开机并进入桌面后，先执行一次：</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">xhost +SI:localuser:root</span><br></pre></td></tr></table></figure><p>再进入容器启动 Gazebo 或 RViz。</p><h3 id="4-没有-NVIDIA-显卡怎么办">4. 没有 NVIDIA 显卡怎么办</h3><p>如果你的机器没有配置 NVIDIA 驱动或 Docker GPU 环境，创建容器时先去掉：</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">--gpus all</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">-e NVIDIA_DRIVER_CAPABILITIES=all</span><br></pre></td></tr></table></figure><h2 id="总结">总结</h2><p>Docker 的核心思路并不复杂：先有镜像，再由镜像创建容器。真正高频使用的内容，其实就是拉镜像、启容器、进容器、挂目录，以及排查网络和图形权限这几件事。</p><p>把这些基础操作熟悉之后，再继续搭 ROS、Gazebo、PX4 之类的开发环境会顺很多。</p>]]></content>
    
    
    <summary type="html">前言 Docker 是日常开发里很常用的容器工具，可以把程序和运行环境打包在一起，减少“换台机器就跑不起来”的问题。 这篇文章先整理 Docker 的基本概念，介绍在 Ubuntu 上的安装方法和常用命令，再给出一个 ROS Noetic 图形化容器的实际示例，方便后续继续搭建自己的仿真环境。 一、先理解镜…</summary>
    
    
    
    <category term="开发工具" scheme="https://dreamer198.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Linux" scheme="https://dreamer198.top/tags/Linux/"/>
    
    <category term="Docker" scheme="https://dreamer198.top/tags/Docker/"/>
    
    <category term="ROS" scheme="https://dreamer198.top/tags/ROS/"/>
    
  </entry>
  
  <entry>
    <title>YOPO-ROS2 运行指南</title>
    <link href="https://dreamer198.top/2026/03/31/YOPO-ROS2-RUN/"/>
    <id>https://dreamer198.top/2026/03/31/YOPO-ROS2-RUN/</id>
    <published>2026-03-30T16:00:00.000Z</published>
    <updated>2026-08-13T08:17:01.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="YOPO-ROS2-运行指南">YOPO-ROS2 运行指南</h2><span id="more"></span><blockquote><p><strong>项目地址</strong>：<a href="https://github.com/dreamer198/YOPO-ROS2.git">https://github.com/dreamer198/YOPO-ROS2.git</a></p></blockquote><h2 id="环境依赖">环境依赖</h2><table><thead><tr><th>依赖</th><th>版本要求</th></tr></thead><tbody><tr><td>ROS 2</td><td>Humble</td></tr><tr><td>CUDA</td><td>12.6+</td></tr><tr><td>GCC/G++</td><td>11</td></tr><tr><td>PyTorch</td><td>2.4.1+cu118</td></tr><tr><td>Eigen3, PCL, OpenCV, yaml-cpp</td><td>系统安装</td></tr></tbody></table><h2 id="一、构建">一、构建</h2><h3 id="1-构建-Controller-工作空间">1. 构建 Controller 工作空间</h3><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">cd</span> ~/code/YOPO-ROS2/Controller</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">colcon build --packages-select cmake_utils --symlink-install</span><br><span class="line">colcon build</span><br></pre></td></tr></table></figure><h3 id="2-构建-Simulator-工作空间">2. 构建 Simulator 工作空间</h3><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">cd</span> ~/code/YOPO-ROS2/Simulator</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">colcon build</span><br></pre></td></tr></table></figure><h3 id="3-安装-YOPO-Python-依赖">3. 安装 YOPO Python 依赖</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">cd</span> ~/code/YOPO-ROS2/YOPO</span><br><span class="line">pip install -r requirements.txt</span><br></pre></td></tr></table></figure><h2 id="二、运行（5-个终端）">二、运行（5 个终端）</h2><h3 id="终端-1：四旋翼仿真器">终端 1：四旋翼仿真器</h3><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">cd</span> ~/code/YOPO-ROS2/Controller</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br><span class="line">ros2 launch so3_quadrotor_simulator simulator_attitude_control.launch.py</span><br></pre></td></tr></table></figure><h3 id="终端-2：CUDA-传感器仿真器">终端 2：CUDA 传感器仿真器</h3><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">cd</span> ~/code/YOPO-ROS2/Simulator</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> install/setup.bash</span><br><span class="line">ros2 run sensor_simulator sensor_simulator_cuda</span><br></pre></td></tr></table></figure><h3 id="终端-3：YOPO-策略节点">终端 3：YOPO 策略节点</h3><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">cd</span> ~/code/YOPO-ROS2/YOPO</span><br><span class="line"><span class="built_in">source</span> /opt/ros/humble/setup.bash</span><br><span class="line"><span class="built_in">source</span> ~/code/YOPO-ROS2/Controller/install/setup.bash</span><br><span class="line">python3 test_yopo_ros.py</span><br></pre></td></tr></table></figure><h3 id="终端-4：RViz-可视化">终端 4：RViz 可视化</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">rviz2 -d ~/code/YOPO-ROS2/YOPO/yopo2.rviz</span><br></pre></td></tr></table></figure><h3 id="终端-5：发送目标点">终端 5：发送目标点</h3><p>YOPO 默认目标为 <code>[50, 0, 2]</code>，无人机可能很快到达。可通过以下命令发送新目标点使其重新飞行：</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">source</span> /opt/ros/humble/setup.bash</span><br><span class="line">ros2 topic pub --once /move_base_simple/goal geometry_msgs/msg/PoseStamped \</span><br><span class="line">  <span class="string">&quot;&#123;header: &#123;frame_id: &#x27;world&#x27;&#125;, pose: &#123;position: &#123;x: 100.0, y: 20.0, z: 2.0&#125;, orientation: &#123;w: 1.0&#125;&#125;&#125;&quot;</span></span><br></pre></td></tr></table></figure><blockquote><p>目标坐标可自行修改 <code>x</code>, <code>y</code>, <code>z</code> 的值。</p></blockquote><h2 id="三、常见问题">三、常见问题</h2><h3 id="1-CUDA-版本不匹配">1. CUDA 版本不匹配</h3><p>Simulator CMakeLists.txt 默认要求 CUDA 12.8。如果你的 CUDA 版本不同（如 12.6），需修改 <code>Simulator/src/CMakeLists.txt</code> 第 46 行：</p><figure class="highlight cmake"><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="comment"># 将</span></span><br><span class="line"><span class="keyword">find_package</span>(CUDA <span class="number">12.8</span> EXACT REQUIRED)</span><br><span class="line"><span class="comment"># 改为</span></span><br><span class="line"><span class="keyword">find_package</span>(CUDA REQUIRED)</span><br></pre></td></tr></table></figure><h3 id="2-RViz-报错找不到-uav-dae">2. RViz 报错找不到 uav.dae</h3><p>源码中硬编码了作者本地路径。需修改 <code>Controller/src/so3_quadrotor_simulator/src/quadrotor_simulator_so3.cpp</code> 第 377 行，改为使用 <code>ament_index_cpp</code> 动态获取包路径：</p><figure class="highlight cpp"><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">meshROS.mesh_resource = <span class="string">&quot;file:///home/hu/YOPO_backup/YOPO/Controller/src/so3_quadrotor_simulator/config/uav.dae&quot;</span>;</span><br><span class="line"><span class="comment">// 改为</span></span><br><span class="line">std::string pkg_path = ament_index_cpp::<span class="built_in">get_package_share_directory</span>(<span class="string">&quot;so3_quadrotor_simulator&quot;</span>);</span><br><span class="line">meshROS.mesh_resource = <span class="string">&quot;file://&quot;</span> + pkg_path + <span class="string">&quot;/config/uav.dae&quot;</span>;</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"><span class="built_in">cd</span> ~/code/YOPO-ROS2/Controller</span><br><span class="line">colcon build --packages-select so3_quadrotor_simulator</span><br></pre></td></tr></table></figure><h3 id="3-pip-依赖冲突">3. pip 依赖冲突</h3><p>安装 Python 依赖时可能出现 numpy/empy 版本冲突警告（与 ultralytics、bloom 等包），不影响 YOPO 本身运行，可忽略。</p>]]></content>
    
    
    <summary type="html">记录 YOPO-ROS2 在 ROS 2 Humble 环境中的依赖配置、工作空间构建、模型准备与运行步骤。</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="路径规划" scheme="https://dreamer198.top/tags/%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92/"/>
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="YOPO" scheme="https://dreamer198.top/tags/YOPO/"/>
    
  </entry>
  
  <entry>
    <title>Gazebo仿真环境中的无人机视觉定位测试</title>
    <link href="https://dreamer198.top/2026/03/30/Gazebo%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%9A%84%E6%97%A0%E4%BA%BA%E6%9C%BA%E8%A7%86%E8%A7%89%E5%AE%9A%E4%BD%8D%E6%B5%8B%E8%AF%95/"/>
    <id>https://dreamer198.top/2026/03/30/Gazebo%E4%BB%BF%E7%9C%9F%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%9A%84%E6%97%A0%E4%BA%BA%E6%9C%BA%E8%A7%86%E8%A7%89%E5%AE%9A%E4%BD%8D%E6%B5%8B%E8%AF%95/</id>
    <published>2026-03-30T09:00:00.000Z</published>
    <updated>2026-07-08T12:44:19.491Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言">前言</h2><p>本文记录在自定义的Gazebo仿真环境（赤壁场景）中，通过图像匹配技术实现无人机定位的测试过程。主要涉及PX4无人机仿真、ROS2话题桥接、MAVROS控制以及基于图像重定位的视觉定位系统。</p><h2 id="系统架构">系统架构</h2><p>整个测试系统包含以下组件：</p><ol><li><strong>Gazebo仿真环境</strong>：赤壁自定义场景</li><li><strong>PX4无人机</strong>：搭载下视单目相机的x500模型</li><li><strong>ROS2桥接</strong>：连接Gazebo和ROS2生态系统</li><li><strong>MAVROS</strong>：实现无人机控制</li><li><strong>视觉定位系统</strong>：基于图像匹配的定位服务</li></ol><h2 id="环境准备">环境准备</h2><h3 id="版本兼容性说明">版本兼容性说明</h3><blockquote><p><strong>重要</strong>：ROS2 Humble与Gazebo Harmonic版本匹配问题</p><p>在桥接话题时需要安装 <code>ros-humble-ros-gzharmonic</code>，而不是ROS2自带的 <code>ros-humble-ros-gz</code></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"><span class="built_in">sudo</span> apt install ros-humble-ros-gzharmonic</span><br></pre></td></tr></table></figure><h3 id="启动仿真环境">启动仿真环境</h3><h4 id="终端1：启动Gazebo仿真环境">终端1：启动Gazebo仿真环境</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置Gazebo模型路径</span></span><br><span class="line"><span class="built_in">export</span> GZ_SIM_RESOURCE_PATH=<span class="variable">$HOME</span>/PX4-Autopilot/Tools/simulation/gz/models</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动仿真（-s表示不启动GUI，减少显存占用）</span></span><br><span class="line">gz sim -s -r ~/PX4-Autopilot/Tools/simulation/gz/worlds/chibi.sdf</span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>-s</code>：无GUI模式（headless），降低资源占用</li><li><code>-r</code>：运行模式</li><li><code>chibi.sdf</code>：赤壁场景文件</li></ul><h4 id="终端2：启动PX4无人机">终端2：启动PX4无人机</h4><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/PX4-Autopilot</span><br><span class="line"></span><br><span class="line">PX4_GZ_WORLD=chibi \</span><br><span class="line">PX4_SYS_AUTOSTART=4001 \</span><br><span class="line">PX4_SIM_MODEL=gz_x500_mono_cam_down \</span><br><span class="line">PX4_GZ_MODEL_POSE=<span class="string">&quot;0,0&quot;</span> \</span><br><span class="line">./build/px4_sitl_default/bin/px4 -i 0</span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>PX4_GZ_WORLD=chibi</code>：指定赤壁世界</li><li><code>PX4_SYS_AUTOSTART=4001</code>：无人机系统ID</li><li><code>PX4_SIM_MODEL=gz_x500_mono_cam_down</code>：搭载下视单目相机的x500</li><li><code>PX4_GZ_MODEL_POSE=&quot;0,0&quot;</code>：初始位置</li><li><code>-i 0</code>：实例编号</li></ul><h2 id="ROS2集成">ROS2集成</h2><h3 id="桥接相机图像">桥接相机图像</h3><h4 id="终端3：桥接Gazebo图像到ROS2">终端3：桥接Gazebo图像到ROS2</h4><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">ros2 run ros_gz_image image_bridge \</span><br><span class="line">  /world/chibi/model/x500_mono_cam_down_0/link/camera_link/sensor/camera/image \</span><br><span class="line">  --ros-args \</span><br><span class="line">  -r /world/chibi/model/x500_mono_cam_down_0/link/camera_link/sensor/camera/image:=/my_camera/image_raw</span><br></pre></td></tr></table></figure><p>这会将Gazebo中的相机图像桥接到ROS2话题 <code>/my_camera/image_raw</code></p><p><strong>注意</strong>：实际话题名称需要根据Gazebo模型结构调整，可以使用以下命令查看：</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">gz topic -l | grep image</span><br></pre></td></tr></table></figure><h3 id="启动MAVROS">启动MAVROS</h3><h4 id="终端4：启动MAVROS控制接口">终端4：启动MAVROS控制接口</h4><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"># 设置ROS2域ID（避免与其他ROS2系统冲突）</span></span><br><span class="line"><span class="built_in">export</span> ROS_DOMAIN_ID=1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动MAVROS</span></span><br><span class="line">ros2 launch onboard_control mainTree.launch.py \</span><br><span class="line">  fcu_url:=<span class="string">&quot;udp://:14540@127.0.0.1:14557&quot;</span></span><br></pre></td></tr></table></figure><p>参数说明：</p><ul><li><code>ROS_DOMAIN_ID=1</code>：设置ROS2域ID</li><li><code>fcu_url</code>：飞控连接URL（UDP协议）</li></ul><h2 id="视觉定位系统">视觉定位系统</h2><h3 id="启动匹配服务器">启动匹配服务器</h3><h4 id="终端5：启动图像匹配服务">终端5：启动图像匹配服务</h4><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/ros2_ws/src/onboard/headless_resection_service</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置ROS2域ID</span></span><br><span class="line"><span class="built_in">export</span> ROS_DOMAIN_ID=1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动服务</span></span><br><span class="line">bash start_ros_service.sh</span><br></pre></td></tr></table></figure><p>匹配服务器提供以下功能：</p><ul><li>接收实时相机图像</li><li>与预先生成的数据库进行匹配</li><li>返回相机位姿估计结果</li></ul><h3 id="启动匹配客户端">启动匹配客户端</h3><h4 id="终端6：启动测试客户端">终端6：启动测试客户端</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 设置ROS2域ID</span></span><br><span class="line"><span class="built_in">export</span> ROS_DOMAIN_ID=1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动客户端</span></span><br><span class="line">python3 test_client_ros_realtime.py</span><br></pre></td></tr></table></figure><p>客户端功能：</p><ul><li>订阅相机图像话题</li><li>发送匹配请求到服务器</li><li>接收并显示定位结果</li></ul><h2 id="完整启动流程总结">完整启动流程总结</h2><p>按照以下顺序启动各个组件：</p><ol><li><p><strong>终端1</strong>：启动Gazebo仿真环境（无GUI）</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">export</span> GZ_SIM_RESOURCE_PATH=<span class="variable">$HOME</span>/PX4-Autopilot/Tools/simulation/gz/models</span><br><span class="line">gz sim -s -r ~/PX4-Autopilot/Tools/simulation/gz/worlds/chibi.sdf</span><br></pre></td></tr></table></figure></li><li><p><strong>终端2</strong>：启动PX4无人机</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">cd</span> ~/PX4-Autopilot</span><br><span class="line">PX4_GZ_WORLD=chibi PX4_SYS_AUTOSTART=4001 PX4_SIM_MODEL=gz_x500_mono_cam_down PX4_GZ_MODEL_POSE=<span class="string">&quot;0,0&quot;</span> \</span><br><span class="line">./build/px4_sitl_default/bin/px4 -i 0</span><br></pre></td></tr></table></figure></li><li><p><strong>终端3</strong>：桥接相机图像</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">ros2 run ros_gz_image image_bridge /world/chibi/model/x500_mono_cam_down_0/link/camera_link/sensor/camera/image --ros-args -r /world/chibi/model/x500_mono_cam_down_0/link/camera_link/sensor/camera/image:=/my_camera/image_raw</span><br></pre></td></tr></table></figure></li><li><p><strong>终端4</strong>：启动MAVROS</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">export</span> ROS_DOMAIN_ID=1</span><br><span class="line">ros2 launch onboard_control mainTree.launch.py fcu_url:=<span class="string">&quot;udp://:14540@127.0.0.1:14557&quot;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>终端5</strong>：启动匹配服务器</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">cd</span> ~/ros2_ws/src/onboard/headless_resection_service</span><br><span class="line"><span class="built_in">export</span> ROS_DOMAIN_ID=1</span><br><span class="line">bash start_ros_service.sh</span><br></pre></td></tr></table></figure></li><li><p><strong>终端6</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">export</span> ROS_DOMAIN_ID=1</span><br><span class="line">python3 test_client_ros_realtime.py</span><br></pre></td></tr></table></figure></li></ol><h2 id="测试验证">测试验证</h2><h3 id="检查话题连接">检查话题连接</h3><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="comment"># 查看所有ROS2话题</span></span><br><span class="line">ros2 topic list</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看相机图像</span></span><br><span class="line">ros2 topic <span class="built_in">echo</span> /my_camera/image_raw --no-arr</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看匹配结果（根据实际话题名调整）</span></span><br><span class="line">ros2 topic <span class="built_in">echo</span> /matching/result</span><br></pre></td></tr></table></figure><h3 id="检查Gazebo话题">检查Gazebo话题</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 列出所有Gazebo话题</span></span><br><span class="line">gz topic -l</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看相机图像话题</span></span><br><span class="line">gz topic -e -t /world/chibi/model/x500_mono_cam_down_0/link/camera_link/sensor/camera/image</span><br></pre></td></tr></table></figure><h3 id="可视化">可视化</h3><p>使用RViz2可视化定位结果：</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">rviz2</span><br></pre></td></tr></table></figure><p>在RViz2中添加：</p><ul><li>Image显示：查看相机图像</li><li>Pose显示：查看定位位姿</li><li>TF显示：查看坐标系关系</li></ul><h2 id="性能优化">性能优化</h2><h3 id="减少资源占用">减少资源占用</h3><ol><li><strong>使用无GUI模式</strong>：<code>gz sim -s</code>（已在上面的命令中使用）</li><li><strong>降低图像分辨率</strong>：在相机配置中调整分辨率参数</li><li><strong>减少特征点数量</strong>：在匹配算法中调整特征提取参数</li></ol><h3 id="提高定位精度">提高定位精度</h3><ol><li><strong>确保数据库质量</strong>：生成覆盖完整、光照均匀的数据库</li><li><strong>调整匹配阈值</strong>：根据实际场景调整匹配相似度阈值</li><li><strong>添加时间戳同步</strong>：确保图像和IMU数据时间对齐</li></ol><h2 id="常见问题">常见问题</h2><h3 id="1-话题名称不匹配">1. 话题名称不匹配</h3><p><strong>现象</strong>：无法接收到图像数据</p><p><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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检查Gazebo中的实际话题名</span></span><br><span class="line">gz topic -l | grep image</span><br><span class="line"></span><br><span class="line"><span class="comment"># 根据实际话题名调整桥接命令</span></span><br></pre></td></tr></table></figure><h3 id="2-ROS2域ID冲突">2. ROS2域ID冲突</h3><p><strong>现象</strong>：不同节点之间无法通信</p><p><strong>解决方法</strong>：确保所有节点使用相同的ROS2域ID</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">export</span> ROS_DOMAIN_ID=1</span><br></pre></td></tr></table></figure><h3 id="3-匹配服务器无响应">3. 匹配服务器无响应</h3><p><strong>检查项</strong>：</p><ul><li>服务是否正常启动</li><li>数据库是否正确加载</li><li>网络通信是否正常</li></ul><h3 id="4-MAVROS连接失败">4. MAVROS连接失败</h3><p><strong>解决方法</strong>：</p><ul><li>检查PX4是否正常启动</li><li>验证UDP端口配置</li><li>查看MAVROS日志</li></ul><h2 id="扩展应用">扩展应用</h2><h3 id="1-自定义场景">1. 自定义场景</h3><p>可以根据需要创建其他Gazebo世界：</p><ul><li>室内环境</li><li>城市场景</li><li>特定任务场景</li></ul><h3 id="2-多机协同">2. 多机协同</h3><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">./build/px4_sitl_default/bin/px4 -i 1</span><br><span class="line">./build/px4_sitl_default/bin/px4 -i 2</span><br></pre></td></tr></table></figure><h3 id="3-算法对比">3. 算法对比</h3><p>可以测试不同的视觉定位算法：</p><ul><li>特征点匹配</li><li>深度学习定位</li><li>混合方法</li></ul><h2 id="参考资源">参考资源</h2><ul><li><a href="https://docs.px4.io/">PX4开发指南</a></li><li><a href="https://gazebosim.org/docs/harmonic">Gazebo Harmonic文档</a></li><li><a href="https://github.com/gazebosim/ros_gz">ROS GZ Bridge</a></li><li><a href="https://github.com/mavlink/mavros">MAVROS文档</a></li></ul><h2 id="总结">总结</h2><p>本文介绍了在Gazebo仿真环境中进行无人机视觉定位测试的完整流程。通过PX4、ROS2和图像匹配技术的结合，可以快速验证视觉定位算法的有效性，为后续实际部署提供可靠的技术基础。</p><p>关键要点：</p><ol><li>注意ROS2和Gazebo版本兼容性</li><li>正确配置话题桥接</li><li>统一ROS2域ID</li><li>按照正确顺序启动各个组件</li><li>及时验证各环节的数据流</li></ol>]]></content>
    
    
    <summary type="html">前言 本文记录在自定义的Gazebo仿真环境（赤壁场景）中，通过图像匹配技术实现无人机定位的测试过程。主要涉及PX4无人机仿真、ROS2话题桥接、MAVROS控制以及基于图像重定位的视觉定位系统。 系统架构 整个测试系统包含以下组件： 1. Gazebo仿真环境：赤壁自定义场景 2. PX4无人机：搭载下视…</summary>
    
    
    
    <category term="无人机开发" scheme="https://dreamer198.top/categories/%E6%97%A0%E4%BA%BA%E6%9C%BA%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="ROS2" scheme="https://dreamer198.top/tags/ROS2/"/>
    
    <category term="PX4" scheme="https://dreamer198.top/tags/PX4/"/>
    
    <category term="Gazebo" scheme="https://dreamer198.top/tags/Gazebo/"/>
    
    <category term="视觉定位" scheme="https://dreamer198.top/tags/%E8%A7%86%E8%A7%89%E5%AE%9A%E4%BD%8D/"/>
    
  </entry>
  
</feed>
