<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>stydxm&#39;s blog</title>
  
  
  <link href="https://stydxm.com/atom.xml" rel="self"/>
  
  <link href="https://stydxm.com/"/>
  <updated>2025-11-25T21:31:43.286Z</updated>
  <id>https://stydxm.com/</id>
  
  <author>
    <name>stydxm</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>AOSC OS上LoongArch ROS2移植</title>
    <link href="https://stydxm.com/2025/10/01/aosc-ros2/"/>
    <id>https://stydxm.com/2025/10/01/aosc-ros2/</id>
    <published>2025-10-01T03:52:18.000Z</published>
    <updated>2025-11-25T21:31:43.286Z</updated>
    
    <content type="html"><![CDATA[<p>想在机器人领域使用一下龙芯，但目前ROS2官方并未发预构建二进制包</p><p>调研时发现了<a href="https://loongros.cn/">loongros2</a>，证明源码直接或小改后在龙芯上运行没有太大问题，遂决定开坑移植</p><span id="more"></span><p>2025.11.7更新：得益于AOSC的构建系统，也幸好ros2源码中没有太多架构特定的代码，ROS Jazzy desktop除Mimick相关的所有包在amd64/arm64/loongarch64/riscv64/ppc64el/mips均构建通过（但还未测试，估计也不会有人在ppc和mips这些平台上用）</p><h2 id="准备工作">准备工作</h2><h3 id="colcon"><a href="http://colcon.readthedocs.io/">colcon</a></h3><p>colcon是一个用于构建软件包集的命令行工具，大家都用它来编译ROS2项目。因此虽然它不属于ROS的一部分，还是先把它打包出来</p><p>colcon是完全由Python写的，不涉及到编译，也没有架构问题。虽然包的数量不少（Ubuntu的<code>python3-colcon</code>加上推荐依赖是二十多个），但构建也不麻烦，直接手动写一下再复制复制粘贴几次</p><p>相关PR：<a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12771">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12771</a></p><h3 id="flake8"><a href="https://flake8.pycqa.org/">flake8</a></h3><p>flake8是一个代码检查工具，虽然没有直接用到，但ROS里很多Python包都依赖它，因此也要打包一下</p><p>相关PR：<a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12817">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12817</a></p><h3 id="其他包">其他包</h3><p>主要涉及到mypy、lttng等一大堆东西</p><p><s>相关PR：<a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12823">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12823</a></s></p><p>2025.11.7更新：由于社区反馈建议分开，就拆成了多个PR：</p><ul><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12838">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12838</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12839">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12839</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12843">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12843</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12844">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12844</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12845">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12845</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12846">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12846</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12847">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12847</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12881">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12881</a></li><li><a href="https://github.com/AOSC-Dev/aosc-os-abbs/pull/12883">https://github.com/AOSC-Dev/aosc-os-abbs/pull/12883</a></li></ul><h2 id="ROS构建目标">ROS构建目标</h2><p>在<a href="https://docs.ros.org/en/rolling/Releases.html">所有的ROS版本中</a>，现在还在生命周期中的正式版有三个。其中Kilted不是LTS版本，而Humble比较老，依赖版本比较低，因此选择最新的LTS版本Jazzy。查看<a href="https://www.ros.org/reps/rep-2000.html#jazzy-jalisco-may-2024-may-2029">文档中对依赖库版本的需求</a>均能满足，决定将目标distribution定为Jazzt Jalisco</p><p><a href="https://www.ros.org/reps/rep-2001.html#jazzy-jalisco-may-2024-may-2029">REP 2001</a>中规定了每个版本的不同变种，Jazzy有六个，其中最低的是ROS Core，其次是ROS Base</p><p><s>我暂时将目标Variant定为base，如进展顺利再尝试打包更多包以达到desktop</s></p><p>2025.11.7更新：打包base时没有遇到太多问题，然后再花了一些时间打出了dekstop</p><h2 id="编写脚本生成打包脚本">编写脚本生成打包脚本</h2><p>因为ROS的包数量大、相似度高，手工编写打包脚本远不如用脚本批量生成</p><p>个人比较熟悉Python，就用它来写了</p><h3 id="编写软件包的类">编写软件包的类</h3><p>参照<a href="https://wiki.aosc.io/zh/developer/packaging/package-styling-manual/">AOSC的指南</a>，编写一个Python类</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Package</span>:</span><br><span class="line">    version: <span class="built_in">str</span></span><br><span class="line">    src: <span class="built_in">str</span></span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    dependencies: <span class="built_in">list</span>[<span class="built_in">str</span>]</span><br><span class="line">    build_dependencies: <span class="built_in">list</span>[<span class="built_in">str</span>]</span><br><span class="line">    description: <span class="built_in">str</span></span><br><span class="line">    autobuild_type: <span class="built_in">str</span></span><br></pre></td></tr></table></figure><h3 id="获取包列表">获取包列表</h3><p>ros官方提供了一个工具<code>rosinstall-generator</code>，可以生成包源码的结构化数据，我们可通过<a href="https://wiki.ros.org/rosinstall_generator">提供的文档</a>来了解它的用法</p><p>使用pip安装后，尝试获取一份包含依赖项的core级别所有包的源码</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">rosinstall_generator ros_core --rosdistro jazzy --deps</span><br></pre></td></tr></table></figure><p>使用python解析</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Literal</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> yaml</span><br><span class="line"></span><br><span class="line">raw_package_type = <span class="built_in">dict</span>[</span><br><span class="line">    <span class="type">Literal</span>[<span class="string">&quot;git&quot;</span>], <span class="built_in">dict</span>[<span class="type">Literal</span>[<span class="string">&quot;local-name&quot;</span>, <span class="string">&quot;uri&quot;</span>, <span class="string">&quot;version&quot;</span>], <span class="built_in">str</span>]</span><br><span class="line">]</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    rosinstall_generator_output = subprocess.run(</span><br><span class="line">        [<span class="string">&quot;rosinstall_generator&quot;</span>, <span class="string">&quot;ros_base&quot;</span>, <span class="string">&quot;--rosdistro&quot;</span>, <span class="string">&quot;jazzy&quot;</span>, <span class="string">&quot;--deps&quot;</span>],</span><br><span class="line">        capture_output=<span class="literal">True</span>,</span><br><span class="line">        check=<span class="literal">True</span>,</span><br><span class="line">    )</span><br><span class="line">    raw_packages_str = rosinstall_generator_output.stdout</span><br><span class="line">    raw_packages_data: raw_package_type = yaml.safe_load(raw_packages_str)</span><br><span class="line"><span class="keyword">except</span> subprocess.CalledProcessError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;命令执行失败: <span class="subst">&#123;e.returncode&#125;</span>&quot;</span>, file=sys.stderr)</span><br><span class="line">    <span class="built_in">print</span>(e.stderr)</span><br><span class="line"><span class="keyword">except</span> FileNotFoundError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;找不到命令，rosinstall-generator 未安装&quot;</span>, file=sys.stderr)</span><br><span class="line"><span class="keyword">except</span> yaml.YAMLError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;返回值解析错误: <span class="subst">&#123;e&#125;</span>&quot;</span>, file=sys.stderr)</span><br></pre></td></tr></table></figure><p>注意<a href="https://wiki.ros.org/rosinstall">rosinstall</a>和<a href="https://github.com/ros-infrastructure/rosinstall_generator">rosinstall-generator</a>是两个不同的包</p><h3 id="解析包信息">解析包信息</h3><h4 id="版本">版本</h4><p>读取到信息中的版本号均为<code>x.y.z-revision</code>的格式</p><p>按照<a href="https://wiki.aosc.io/zh/developer/packaging/package-styling-manual/#ruan-jian-bao-ban-ben">AOSC的规定</a>：</p><blockquote><p>情形：版本号带有短横线（-）<br>应当采取的措施：将短横线替换为加号（+）<br>举例：ImageMagick 6.9.10-23 -&gt; VER=6.9.10+23</p></blockquote><p>将其进行字符串替换，改为<code>x.y.z+revision</code></p><h4 id="源码">源码</h4><p><code>rosinstall_generator</code>输出的均为git仓库，因此只需要做简单拼接<code>&quot;git::commit=tags/{version}::{uri}&quot;</code></p><p>但由于已经单独提出了<code>VER</code>变量，在<code>SRC</code>中再硬编码版本号就显得不太合适，所以稍作修改</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">semver = version.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">self.version = semver.replace(<span class="string">&quot;-&quot;</span>, <span class="string">&quot;+&quot;</span>)</span><br><span class="line">version_prefix = <span class="string">&quot;/&quot;</span>.join(version.split(<span class="string">&quot;/&quot;</span>)[:-<span class="number">1</span>])</span><br><span class="line">self.src = <span class="string">f&quot;git::commit=tags/<span class="subst">&#123;version_prefix&#125;</span>/<span class="subst">&#123;<span class="string">&#x27;$&#123;VER/+/-&#125;&#x27;</span>&#125;</span>::<span class="subst">&#123;uri&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><p>这样处理后，例如ament_package这个包的生成内容就是</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">VER=0.16.4+1</span><br><span class="line">SRCS=<span class="string">&quot;git::commit=tags/release/jazzy/ament_package/<span class="variable">$&#123;VER/+/-&#125;</span>::https://github.com/ros2-gbp/ament_package-release.git&quot;</span></span><br></pre></td></tr></table></figure><h4 id="更新检查">更新检查</h4><p>样式指南要求尽量使用anitya，但要把如此数量庞大的包都加到anitya过于麻烦，就选了<a href="https://github.com/AOSC-Dev/aosc-findupdate/blob/master/docs/config.zh-CN.md#github-api">使用GitHub格式</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">repo = uri[<span class="number">19</span>:-<span class="number">4</span>]</span><br><span class="line">pattern = <span class="string">&quot;/&quot;</span>.join(version.split(<span class="string">&quot;/&quot;</span>)[:-<span class="number">1</span>]) + <span class="string">&quot;/.*&quot;</span></span><br><span class="line">check_update = <span class="string">f&quot;github::repo=<span class="subst">&#123;repo&#125;</span>;pattern=<span class="subst">&#123;pattern&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><h4 id="包名">包名</h4><p>观察发现，rosinstall_generator输出的local-name都是<code>包所属“大包”名/包名</code><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>的格式，且包名一段是由下划线分割的。参考官方打包的deb包名，可<strong>推测出</strong>最终包名的格式</p><figure class="highlight python"><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">original_name = local_name.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">package_name = <span class="string">&quot;ros-jazzy-&quot;</span> + original_name.replace(<span class="string">&quot;_&quot;</span>, <span class="string">&quot;-&quot;</span>)</span><br></pre></td></tr></table></figure><p>由于包名是推测的，没有找到相关文档，还需要进行验证</p><details><summary>验证过程</summary><p>利用ROS官方的apt仓库，通过检查目录是否存在来判断</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> tqdm <span class="keyword">import</span> tqdm</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> package <span class="keyword">in</span> tqdm(raw_packages_data):</span><br><span class="line">    local_name = package[<span class="string">&quot;git&quot;</span>][<span class="string">&quot;local-name&quot;</span>]</span><br><span class="line">    original_name = local_name.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">    package_name = <span class="string">&quot;ros-jazzy-&quot;</span> + original_name.replace(<span class="string">&quot;_&quot;</span>, <span class="string">&quot;-&quot;</span>)</span><br><span class="line">    resp = requests.get(</span><br><span class="line">        <span class="string">&quot;http://packages.ros.org/ros2/ubuntu/pool/main/r/&quot;</span> + package_name</span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">if</span> resp.status_code != <span class="number">200</span>:</span><br><span class="line">        <span class="built_in">print</span>(local_name)</span><br></pre></td></tr></table></figure></details><p>结果为全部通过，证实了我的猜测</p><h4 id="依赖">依赖</h4><p>这是非常重要又很难处理的一部分，每个包都有一个<code>package.xml</code>文件<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>，里面记录了包的依赖情况</p><p>依赖分为两种，编译时依赖和构建时依赖，用<code>&lt;build_depend&gt;</code>（编译依赖库）/<code>&lt;buildtool_depend&gt;</code>（编译时需要的编译工具）和<code>&lt;depend&gt;</code>（构建和运行都用）/<code>&lt;exec_depend&gt;</code>（只有运行用）表示</p><p>这些依赖有都有两个来源，其他的ros包和非ros的第三方库。ros包的命名方式很简单，通过前述生成包名的方式可以很容易找到；官方获取第三方库在包管理中的包名的实现方式是维护<a href="https://github.com/ros/rosdistro/blob/master/rosdep/base.yaml">一个大的映射表</a>，我们使用其中的Arch作为基准，在此基础上手动进行修改：</p><ul><li><p>AOSC中的包通常不会以<code>-dev</code>或者<code>-devel</code>结尾，因此遇到该后缀就去掉</p></li><li><p>AOSC打包的Python库不以<code>python3-</code>开头，因此遇到该前缀就去掉</p></li><li><p>上面两条中有个特例是Ubuntu中的包<code>python3-dev</code>，匹配上两条后啥都不剩了，所以要放在最前面做特殊处理</p></li><li><p>AOSC打包的包名中若原来不含<code>lib</code>前缀的，不会加上该前缀，但本来就有的则会保留；若对比同一包在多个系统中的包名，有些带<code>lib</code>前缀而有些没有，就可以大胆猜测它不是原项目名的一部分，将它去除</p></li></ul><p><s>这里用一个简单粗暴（但不一定准确）的方法来区分：含有下划线<code>_</code>的认为是ros包，带有横杠<code>-</code>的是第三方库。</s> 两种来源的包可以通过映射表中是否存在来判断</p><p>基于以上分析内容，写出python代码</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> yaml</span><br><span class="line"></span><br><span class="line">rosdep_map: <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="built_in">dict</span>] = yaml.safe_load(</span><br><span class="line">    requests.get(</span><br><span class="line">        <span class="string">&quot;https://github.com/ros/rosdistro/raw/refs/heads/master/rosdep/base.yaml&quot;</span></span><br><span class="line">    ).text</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_rosdep_package_list</span>(<span class="params"></span></span><br><span class="line"><span class="params">    rosdep_package_list: <span class="built_in">list</span>[<span class="built_in">str</span>] | <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="built_in">list</span>[<span class="built_in">str</span>]],</span></span><br><span class="line"><span class="params"></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    system_package_name = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(rosdep_package_list, <span class="built_in">list</span>):</span><br><span class="line">        system_package_name = rosdep_package_list[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">for</span> system <span class="keyword">in</span> rosdep_package_list.keys():</span><br><span class="line">            <span class="keyword">if</span> rosdep_package_list[system]:</span><br><span class="line">                system_package_name = rosdep_package_list[system][<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">assert</span> system_package_name</span><br><span class="line">    <span class="keyword">return</span> system_package_name</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rosdep_key_to_package_name</span>(<span class="params">rosdep_key: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    package_name = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> rosdep_key <span class="keyword">in</span> rosdep_map.keys():</span><br><span class="line">        current_package = rosdep_map[rosdep_key]</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;arch&quot;</span> <span class="keyword">in</span> current_package:</span><br><span class="line">            package_name = process_rosdep_package_list(current_package[<span class="string">&quot;arch&quot;</span>])</span><br><span class="line">        <span class="keyword">elif</span> <span class="string">&quot;ubuntu&quot;</span> <span class="keyword">in</span> current_package:</span><br><span class="line">            package_name = process_rosdep_package_list(current_package[<span class="string">&quot;ubuntu&quot;</span>])</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            package_name = process_rosdep_package_list(</span><br><span class="line">                current_package[<span class="built_in">list</span>(current_package.keys())[<span class="number">0</span>]]</span><br><span class="line">            )</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        original_name = rosdep_key.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">        package_name = <span class="string">f&quot;ros-<span class="subst">&#123;rosdistro&#125;</span>-&quot;</span> + original_name.replace(<span class="string">&quot;_&quot;</span>, <span class="string">&quot;-&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> package_name == <span class="string">&quot;python3-dev&quot;</span> <span class="keyword">or</span> package_name == <span class="string">&quot;python3&quot;</span>:</span><br><span class="line">        package_name = <span class="string">&quot;python-3&quot;</span></span><br><span class="line">    <span class="keyword">if</span> package_name.startswith(<span class="string">f&quot;ros-<span class="subst">&#123;rosdistro&#125;</span>-python3-&quot;</span>):</span><br><span class="line">        package_name = package_name[<span class="number">13</span> + <span class="built_in">len</span>(rosdistro) :]</span><br><span class="line">    <span class="keyword">if</span> package_name.startswith(<span class="string">&quot;python3-&quot;</span>):</span><br><span class="line">        package_name = package_name[<span class="number">8</span>:]</span><br><span class="line">    <span class="keyword">if</span> package_name.endswith(<span class="string">&quot;-dev&quot;</span>):</span><br><span class="line">        package_name = package_name[:-<span class="number">4</span>]</span><br><span class="line">    <span class="keyword">return</span> package_name</span><br></pre></td></tr></table></figure><p>这里还要注意有个特殊的包<code>ros-jazzy-ros-workspace</code>，负责提供<code>/opt/ros/jazzy/setup.{sh,bash,zsh}</code>等脚本，它是除了本身及其依赖外所有包的依赖项</p><h4 id="描述">描述</h4><p><code>package.xml</code>中有<code>&lt;description&gt;</code>字段包含了对包的描述；但它可能有多行，因此只取第一行</p><p>有个别包中的描述中含有反斜杠，需要注意转义</p><h4 id="构建方式">构建方式</h4><p>绝大多数包都是使用CMake编译或不需要编译的Python包，我选择根据是否存在<code>CMakeLists.txt</code>和<code>setup.py</code>判断；如遇到特殊情况手动处理</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">repo = uri[<span class="number">19</span>:-<span class="number">4</span>]</span><br><span class="line">file_list = requests.get(<span class="string">f&quot;https://api.github.com/repos/<span class="subst">&#123;repo&#125;</span>/contents?ref=<span class="subst">&#123;version&#125;</span>&quot;</span>).json()</span><br><span class="line">has_pyproject = <span class="literal">False</span></span><br><span class="line">has_setup_py = <span class="literal">False</span></span><br><span class="line">has_cmake_lists = <span class="literal">False</span></span><br><span class="line"><span class="keyword">for</span> file <span class="keyword">in</span> file_list:</span><br><span class="line">    <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;pyproject.toml&quot;</span>:</span><br><span class="line">        has_pyproject = <span class="literal">True</span></span><br><span class="line">    <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;setup.py&quot;</span>:</span><br><span class="line">        has_setup_py = <span class="literal">True</span></span><br><span class="line">    <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;CMakeLists.txt&quot;</span>:</span><br><span class="line">        has_cmake_lists = <span class="literal">True</span></span><br><span class="line"><span class="keyword">if</span> has_cmake_lists:</span><br><span class="line">    self.build_type = <span class="string">&quot;cmakeninja&quot;</span></span><br><span class="line"><span class="keyword">elif</span> has_pyproject:</span><br><span class="line">    self.build_type = <span class="string">&quot;pep517&quot;</span></span><br><span class="line"><span class="keyword">elif</span> has_setup_py:</span><br><span class="line">    self.build_type = <span class="string">&quot;python&quot;</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    self.build_type = <span class="string">&quot;unknown&quot;</span></span><br></pre></td></tr></table></figure><p>若包为Python写的，还要加上NOPYTHON2和noarch声明</p><h3 id="生成ACBS文件">生成ACBS文件</h3><p>通过以上步骤，整合代码，写出完整程序</p><p>这里需要注意的是，由于GitHub的风控机制，需要分多次运行或填入个人apikey</p><details><summary>展开程序</summary><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><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><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> subprocess</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> xml.etree.ElementTree <span class="keyword">as</span> ET</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> concurrent.futures <span class="keyword">import</span> ThreadPoolExecutor</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Literal</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> yaml</span><br><span class="line"><span class="keyword">from</span> tqdm <span class="keyword">import</span> tqdm</span><br><span class="line"></span><br><span class="line">rosdistro = <span class="string">&quot;jazzy&quot;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Fetching rosdep map...&quot;</span>)</span><br><span class="line">rosdep_map: <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="built_in">dict</span>] = yaml.safe_load(</span><br><span class="line">    requests.get(</span><br><span class="line">        <span class="string">&quot;https://github.com/ros/rosdistro/raw/refs/heads/master/rosdep/base.yaml&quot;</span></span><br><span class="line">    ).text</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">process_rosdep_package_list</span>(<span class="params"></span></span><br><span class="line"><span class="params">    rosdep_package_list: <span class="built_in">list</span>[<span class="built_in">str</span>] | <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="built_in">list</span>[<span class="built_in">str</span>]],</span></span><br><span class="line"><span class="params"></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    system_package_name = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(rosdep_package_list, <span class="built_in">list</span>):</span><br><span class="line">        system_package_name = rosdep_package_list[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">for</span> system <span class="keyword">in</span> rosdep_package_list.keys():</span><br><span class="line">            <span class="keyword">if</span> rosdep_package_list[system]:</span><br><span class="line">                system_package_name = rosdep_package_list[system][<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">assert</span> system_package_name</span><br><span class="line">    <span class="keyword">return</span> system_package_name</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">rosdep_key_to_package_name</span>(<span class="params">rosdep_key: <span class="built_in">str</span></span>) -&gt; <span class="built_in">str</span>:</span><br><span class="line">    package_name = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> rosdep_key <span class="keyword">in</span> rosdep_map.keys():</span><br><span class="line">        current_package = rosdep_map[rosdep_key]</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;arch&quot;</span> <span class="keyword">in</span> current_package:</span><br><span class="line">            package_name = process_rosdep_package_list(current_package[<span class="string">&quot;arch&quot;</span>])</span><br><span class="line">        <span class="keyword">elif</span> <span class="string">&quot;ubuntu&quot;</span> <span class="keyword">in</span> current_package:</span><br><span class="line">            package_name = process_rosdep_package_list(current_package[<span class="string">&quot;ubuntu&quot;</span>])</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            package_name = process_rosdep_package_list(</span><br><span class="line">                current_package[<span class="built_in">list</span>(current_package.keys())[<span class="number">0</span>]]</span><br><span class="line">            )</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        original_name = rosdep_key.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">        package_name = <span class="string">f&quot;ros-<span class="subst">&#123;rosdistro&#125;</span>-&quot;</span> + original_name.replace(<span class="string">&quot;_&quot;</span>, <span class="string">&quot;-&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> package_name == <span class="string">&quot;python3-dev&quot;</span> <span class="keyword">or</span> package_name == <span class="string">&quot;python3&quot;</span>:</span><br><span class="line">        package_name = <span class="string">&quot;python-3&quot;</span></span><br><span class="line">    <span class="keyword">if</span> package_name.startswith(<span class="string">f&quot;ros-<span class="subst">&#123;rosdistro&#125;</span>-python3-&quot;</span>):</span><br><span class="line">        package_name = package_name[<span class="number">13</span> + <span class="built_in">len</span>(rosdistro) :]</span><br><span class="line">    <span class="keyword">if</span> package_name.startswith(<span class="string">&quot;python3-&quot;</span>):</span><br><span class="line">        package_name = package_name[<span class="number">8</span>:]</span><br><span class="line">    <span class="keyword">if</span> package_name.endswith(<span class="string">&quot;-dev&quot;</span>):</span><br><span class="line">        package_name = package_name[:-<span class="number">4</span>]</span><br><span class="line">    <span class="keyword">return</span> package_name</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Package</span>:</span><br><span class="line">    version: <span class="built_in">str</span></span><br><span class="line">    src: <span class="built_in">str</span></span><br><span class="line">    check_update: <span class="built_in">str</span></span><br><span class="line">    name: <span class="built_in">str</span></span><br><span class="line">    dependencies: <span class="built_in">list</span>[<span class="built_in">str</span>]</span><br><span class="line">    build_dependencies: <span class="built_in">list</span>[<span class="built_in">str</span>]</span><br><span class="line">    description: <span class="built_in">str</span></span><br><span class="line">    autobuild_type: <span class="built_in">str</span></span><br><span class="line">    file_list_url: <span class="built_in">str</span></span><br><span class="line">    package_xml_url: <span class="built_in">str</span></span><br><span class="line">    package_info: ET.Element</span><br><span class="line">    build_type: <span class="built_in">str</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, local_name: <span class="built_in">str</span>, uri: <span class="built_in">str</span>, version: <span class="built_in">str</span></span>):</span><br><span class="line">        semver = version.split(<span class="string">&quot;/&quot;</span>)[-<span class="number">1</span>]</span><br><span class="line">        self.version = semver.replace(<span class="string">&quot;-&quot;</span>, <span class="string">&quot;+&quot;</span>)</span><br><span class="line">        version_prefix = <span class="string">&quot;/&quot;</span>.join(version.split(<span class="string">&quot;/&quot;</span>)[:-<span class="number">1</span>])</span><br><span class="line">        self.src = <span class="string">f&quot;git::commit=tags/<span class="subst">&#123;version_prefix&#125;</span>/<span class="subst">&#123;<span class="string">&#x27;$&#123;VER/+/-&#125;&#x27;</span>&#125;</span>::<span class="subst">&#123;uri&#125;</span>&quot;</span></span><br><span class="line">        repo = uri[<span class="number">19</span>:-<span class="number">4</span>]</span><br><span class="line">        pattern = <span class="string">&quot;/&quot;</span>.join(version.split(<span class="string">&quot;/&quot;</span>)[:-<span class="number">1</span>]) + <span class="string">&quot;/.*&quot;</span></span><br><span class="line">        self.check_update = <span class="string">f&quot;github::repo=<span class="subst">&#123;repo&#125;</span>;pattern=<span class="subst">&#123;pattern&#125;</span>&quot;</span></span><br><span class="line">        self.name = rosdep_key_to_package_name(local_name)</span><br><span class="line">        self.file_list_url = (</span><br><span class="line">            <span class="string">f&quot;https://api.github.com/repos/<span class="subst">&#123;repo&#125;</span>/contents?ref=<span class="subst">&#123;version&#125;</span>&quot;</span></span><br><span class="line">        )</span><br><span class="line">        self.package_xml_url = (</span><br><span class="line">            <span class="string">f&quot;<span class="subst">&#123;uri[:-<span class="number">4</span>]&#125;</span>/raw/refs/tags/<span class="subst">&#123;version&#125;</span>/package.xml&quot;</span></span><br><span class="line">        )</span><br><span class="line">        self.dependencies = [<span class="string">f&quot;ros-<span class="subst">&#123;rosdistro&#125;</span>-ros-workspace&quot;</span>]</span><br><span class="line">        self.build_dependencies = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">get_metadata</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">assert</span> self.package_xml_url</span><br><span class="line">        package_xml_string = requests.get(self.package_xml_url).text</span><br><span class="line">        packge_info = ET.fromstring(package_xml_string)</span><br><span class="line">        pkgdep = (</span><br><span class="line">            packge_info.findall(<span class="string">&quot;depend&quot;</span>)</span><br><span class="line">            + packge_info.findall(<span class="string">&quot;exec_depend&quot;</span>)</span><br><span class="line">            + packge_info.findall(<span class="string">&quot;build_export_depend&quot;</span>)</span><br><span class="line">            + packge_info.findall(<span class="string">&quot;buildtool_export_depend&quot;</span>)</span><br><span class="line">        )</span><br><span class="line">        builddep = (</span><br><span class="line">            packge_info.findall(<span class="string">&quot;buildtool_depend&quot;</span>)</span><br><span class="line">            + packge_info.findall(<span class="string">&quot;build_depend&quot;</span>)</span><br><span class="line">            + packge_info.findall(<span class="string">&quot;test_depend&quot;</span>)</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">for</span> package <span class="keyword">in</span> pkgdep:</span><br><span class="line">            packge_name = rosdep_key_to_package_name(package)</span><br><span class="line">            <span class="keyword">if</span> packge_name <span class="keyword">not</span> <span class="keyword">in</span> self.dependencies:</span><br><span class="line">                self.dependencies.append(packge_name)</span><br><span class="line">        <span class="keyword">for</span> package <span class="keyword">in</span> builddep:</span><br><span class="line">            package_name = rosdep_key_to_package_name(package)</span><br><span class="line">            <span class="keyword">if</span> (</span><br><span class="line">                package_name <span class="keyword">not</span> <span class="keyword">in</span> self.dependencies</span><br><span class="line">                <span class="keyword">and</span> package_name <span class="keyword">not</span> <span class="keyword">in</span> self.build_dependencies</span><br><span class="line">            ):</span><br><span class="line">                self.build_dependencies.append(package_name)</span><br><span class="line">        raw_description = packge_info.findall(<span class="string">&quot;description&quot;</span>)[<span class="number">0</span>].text.splitlines()</span><br><span class="line">        self.description = self.name</span><br><span class="line">        <span class="keyword">for</span> line <span class="keyword">in</span> raw_description:</span><br><span class="line">            clean_line = line.strip()</span><br><span class="line">            <span class="keyword">if</span> clean_line:</span><br><span class="line">                self.description = clean_line</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">if</span> self.description.endswith(<span class="string">&quot;.&quot;</span>):</span><br><span class="line">            self.description = self.description[:-<span class="number">1</span>]</span><br><span class="line">        self.description = self.description.replace(<span class="string">&quot;`&quot;</span>, <span class="string">&quot;\\`&quot;</span>)</span><br><span class="line">        file_list = requests.get(self.file_list_url).json()</span><br><span class="line">        has_pyproject = <span class="literal">False</span></span><br><span class="line">        has_setup_py = <span class="literal">False</span></span><br><span class="line">        has_cmake_lists = <span class="literal">False</span></span><br><span class="line">        <span class="keyword">for</span> file <span class="keyword">in</span> file_list:</span><br><span class="line">            <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;pyproject.toml&quot;</span>:</span><br><span class="line">                has_pyproject = <span class="literal">True</span></span><br><span class="line">            <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;setup.py&quot;</span>:</span><br><span class="line">                has_setup_py = <span class="literal">True</span></span><br><span class="line">            <span class="keyword">if</span> file[<span class="string">&quot;name&quot;</span>] == <span class="string">&quot;CMakeLists.txt&quot;</span>:</span><br><span class="line">                has_cmake_lists = <span class="literal">True</span></span><br><span class="line">        <span class="keyword">if</span> has_cmake_lists:</span><br><span class="line">            self.build_type = <span class="string">&quot;cmakeninja&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> has_pyproject:</span><br><span class="line">            self.build_type = <span class="string">&quot;pep517&quot;</span></span><br><span class="line">        <span class="keyword">elif</span> has_setup_py:</span><br><span class="line">            self.build_type = <span class="string">&quot;python&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            self.build_type = <span class="string">&quot;unknown&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_spec</span>(<span class="params">self</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">f&quot;&quot;&quot;VER=<span class="subst">&#123;self.version&#125;</span></span></span><br><span class="line"><span class="string">SRCS=&quot;<span class="subst">&#123;self.src&#125;</span>&quot;</span></span><br><span class="line"><span class="string">CHKSUMS=&quot;SKIP&quot;</span></span><br><span class="line"><span class="string">CHKUPDATE=&quot;<span class="subst">&#123;self.check_update&#125;</span>&quot;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">generate_defines</span>(<span class="params">self</span>):</span><br><span class="line">        defines_content = <span class="string">f&quot;&quot;&quot;PKGNAME=<span class="subst">&#123;self.name&#125;</span></span></span><br><span class="line"><span class="string">PKGSEC=ros</span></span><br><span class="line"><span class="string">PKGDEP=&quot;<span class="subst">&#123;<span class="string">&quot; &quot;</span>.join(self.dependencies)&#125;</span>&quot;</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> self.build_dependencies:</span><br><span class="line">            defines_content += <span class="string">f&#x27;BUILDDEP=&quot;<span class="subst">&#123;<span class="string">&quot; &quot;</span>.join(self.build_dependencies)&#125;</span>&quot;&#x27;</span></span><br><span class="line">        defines_content += <span class="string">f&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">PKGDES=&quot;<span class="subst">&#123;self.description&#125;</span>&quot;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">ABTYPE=<span class="subst">&#123;self.build_type&#125;</span></span></span><br><span class="line"><span class="string">PREFIX=&quot;/opt/ros/<span class="subst">&#123;rosdistro&#125;</span>&quot;</span></span><br><span class="line"><span class="string">ABSPLITDBG=0</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> self.build_type == <span class="string">&quot;pep517&quot;</span> <span class="keyword">or</span> self.build_type == <span class="string">&quot;python&quot;</span>:</span><br><span class="line">            defines_content += <span class="string">&quot;&quot;&quot;NOPYTHON2=1</span></span><br><span class="line"><span class="string">ABHOST=noarch</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            defines_content += <span class="string">&quot;NOSTATIC=0\n&quot;</span></span><br><span class="line">        <span class="keyword">if</span> self.build_type == <span class="string">&quot;cmakeninja&quot;</span>:</span><br><span class="line">            defines_content += <span class="string">f&quot;&quot;&quot;CMAKE_AFTER=(</span></span><br><span class="line"><span class="string">    &quot;-DCMAKE_PREFIX_PATH=/opt/ros/jazzy&quot;</span></span><br><span class="line"><span class="string">    &quot;-DBUILD_TESTING=OFF&quot;</span></span><br><span class="line"><span class="string">)</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> defines_content</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">write_file</span>(<span class="params">new_package: Package</span>):</span><br><span class="line">    dirname = <span class="string">f&quot;runtime-ros/<span class="subst">&#123;new_package.name&#125;</span>&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(dirname):</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            new_package.get_metadata()</span><br><span class="line">        <span class="keyword">except</span> TypeError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;API rate limit exceeded&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">except</span> requests.exceptions.SSLError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;API rate limit exceeded&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">except</span> requests.exceptions.ConnectionError:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;API rate limit exceeded&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        spec = new_package.generate_spec()</span><br><span class="line">        defines = new_package.generate_defines()</span><br><span class="line">        prepare = <span class="string">f&quot;&quot;&quot;export PYTHONPATH=/opt/ros/<span class="subst">&#123;rosdistro&#125;</span>/lib/python$&#123;&#123;ABPY3VER&#125;&#125;/site-packages/</span></span><br><span class="line"><span class="string">export PKG_CONFIG_PATH=/opt/ros/jazzy/lib/pkgconfig</span></span><br><span class="line"><span class="string">. /opt/ros/jazzy/setup.sh</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">        os.mkdir(dirname)</span><br><span class="line">        os.mkdir(dirname + <span class="string">&quot;/autobuild&quot;</span>)</span><br><span class="line">        <span class="built_in">open</span>(dirname + <span class="string">&quot;/spec&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>).write(spec)</span><br><span class="line">        <span class="built_in">open</span>(dirname + <span class="string">&quot;/autobuild/defines&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>).write(defines)</span><br><span class="line">        <span class="built_in">open</span>(dirname + <span class="string">&quot;/autobuild/prepare&quot;</span>, <span class="string">&quot;w&quot;</span>, encoding=<span class="string">&quot;utf-8&quot;</span>).write(prepare)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Scanning packages ...&quot;</span>)</span><br><span class="line">raw_package_type = <span class="built_in">dict</span>[</span><br><span class="line">    <span class="type">Literal</span>[<span class="string">&quot;git&quot;</span>], <span class="built_in">dict</span>[<span class="type">Literal</span>[<span class="string">&quot;local-name&quot;</span>, <span class="string">&quot;uri&quot;</span>, <span class="string">&quot;version&quot;</span>], <span class="built_in">str</span>]</span><br><span class="line">]</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    rosinstall_generator_output = subprocess.run(</span><br><span class="line">        [<span class="string">&quot;rosinstall_generator&quot;</span>, <span class="string">&quot;desktop&quot;</span>, <span class="string">&quot;--rosdistro&quot;</span>, rosdistro, <span class="string">&quot;--deps&quot;</span>],</span><br><span class="line">        capture_output=<span class="literal">True</span>,</span><br><span class="line">        check=<span class="literal">True</span>,</span><br><span class="line">    )</span><br><span class="line">    raw_packages_str = rosinstall_generator_output.stdout</span><br><span class="line">    raw_packages_data: raw_package_type = yaml.safe_load(raw_packages_str)</span><br><span class="line"><span class="keyword">except</span> subprocess.CalledProcessError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;命令执行失败: <span class="subst">&#123;e.returncode&#125;</span>&quot;</span>, file=sys.stderr)</span><br><span class="line">    <span class="built_in">print</span>(e.stderr)</span><br><span class="line"><span class="keyword">except</span> FileNotFoundError:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;找不到命令，rosinstall-generator 未安装&quot;</span>, file=sys.stderr)</span><br><span class="line"><span class="keyword">except</span> yaml.YAMLError <span class="keyword">as</span> e:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;返回值解析错误: <span class="subst">&#123;e&#125;</span>&quot;</span>, file=sys.stderr)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;Generating files...&quot;</span>)</span><br><span class="line">futures = []</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(<span class="string">&quot;runtime-ros&quot;</span>):</span><br><span class="line">    os.mkdir(<span class="string">&quot;runtime-ros&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 多线程运行</span></span><br><span class="line"><span class="keyword">with</span> ThreadPoolExecutor(max_workers=<span class="number">10</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    <span class="keyword">for</span> package <span class="keyword">in</span> tqdm(raw_packages_data):</span><br><span class="line">        current_data = package[<span class="string">&quot;git&quot;</span>]</span><br><span class="line">        current_package = Package(</span><br><span class="line">            current_data[<span class="string">&quot;local-name&quot;</span>], current_data[<span class="string">&quot;uri&quot;</span>], current_data[<span class="string">&quot;version&quot;</span>]</span><br><span class="line">        )</span><br><span class="line">        future = executor.submit(write_file, current_package)</span><br><span class="line">        futures.append(future)</span><br><span class="line">    <span class="keyword">for</span> future <span class="keyword">in</span> tqdm(futures):</span><br><span class="line">        future.result()</span><br></pre></td></tr></table></figure><details><h2 id="手动处理依赖">手动处理依赖</h2><p>虽然大多数依赖都使用脚本自动化完成了，还有一些依赖包名需要手动处理，例如<code>eigen3-dev</code>和<code>eigen-3</code>等包名不一致的，还有<code>importlib-*</code>等属于另一个包的一部分的情况，以及<code>ros-jazzy-ros-workspace</code>会产生循环依赖等等</p><p>这部分工作量不大且无法自动化，需要人工手动完成</p><h2 id="处理架构不兼容的包">处理架构不兼容的包</h2><p>打包过程中遇到了一个特殊的包<code>Mimick</code>，它用于C的测试</p><p>它使用了<a href="https://github.com/Snaipe/Mimick/tree/master/src/asm">一些汇编代码</a>，而且只有x86和arm，甚至<a href="https://github.com/Snaipe/Mimick/pull/34">添加RV支持的PR</a>几年了也没有被上游合并。即使不考虑上游，学会写这么多架构的汇编也不是一件简单的事。所幸它只是一个用于测试的mock库，可以直接在不支持的架构上不依赖它并设置<code>-DBUILD_TESTING=OFF</code>，顺利解决</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>大包是我自己的不正规的叫法，指的是如ament等需要多个组件组成的包 <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p>该文件有三种版本，以<code>&lt;package&gt;</code>节点的<code>format</code>属性判断，分别是<a href="https://ros.org/reps/rep-0127.html">&quot;1&quot;</a>、<a href="https://ros.org/reps/rep-0140.html">“2”</a>、<a href="https://ros.org/reps/rep-0149.html">“3”</a>，观察发现好像版本2的比较多 <a href="#fnref2" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;想在机器人领域使用一下龙芯，但目前ROS2官方并未发预构建二进制包&lt;/p&gt;
&lt;p&gt;调研时发现了&lt;a href=&quot;https://loongros.cn/&quot;&gt;loongros2&lt;/a&gt;，证明源码直接或小改后在龙芯上运行没有太大问题，遂决定开坑移植&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="ROS" scheme="https://stydxm.com/tags/ROS/"/>
    
  </entry>
  
  <entry>
    <title>使用SSH作为跳板机</title>
    <link href="https://stydxm.com/2025/03/24/ssh-jumpserver/"/>
    <id>https://stydxm.com/2025/03/24/ssh-jumpserver/</id>
    <published>2025-03-24T15:36:55.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>在以前，云服务商提供的国内服务器带宽都很有限或非常昂贵，前段时间阿里云发布低价的200M带宽的轻量应用服务器，使得带宽成本大大降低</p><p>一些在已有机器上的服务迁移麻烦，或者因为储存/算力的原因无法迁移，那么就自然而然地想到可以用大带宽机器中转，同时享受到大带宽和高算力/大储存</p><span id="more"></span><h1>要求</h1><p>首先原有服务器 <code>A</code> 和200M的服务器 <code>B</code> 要在同一可用区，这样才可以通过千兆内网传输数据</p><blockquote><p>这里的A不一定是轻量应用服务器，即使没有公网IP，只要与B内网相通都可以用这个方法，比如ECS、裸金属服务器，甚至开了SSH的serverless也行</p><p>B必须要能使用SSH登陆，如果不能的话需要考虑其他方式进行端口转发</p></blockquote><h1>配置SSH密钥登陆</h1><p>密码连接需要手动介入，为了能在脚本中自动化运行，需要使用密钥或者证书登陆</p><p>这里不写教程了，如有需要可以参考<a href="https://wangdoc.com/ssh/key">WangDoc上的教程</a>，需要将客户端的密钥添加到A和B上</p><h1>获取连接地址</h1><p>打开阿里云的控制台，在基本信息的位置获取服务器A的内网地址（图中<code>主私网IP</code>）和服务器B的公网地址（图中<code>公网IP</code>）</p><p><img src="https://s21.ax1x.com/2025/03/24/pEB6PIJ.png" alt=""></p><p>如果有防火墙、黑白名单等需要注意，客户端需要能连接到B，同时B要能连接到A</p><p>因为到A的流量会从B中转，不经过A的公网地址，所以客户端能否连接到A并不重要</p><h1>使用SSH协议的服务</h1><p>对于使用SSH连接的服务，比如SSH本身，或者sftp scp rsync等，可以使用ProxyJump来将B作为跳板机连接A</p><h2 id="在命令行中测试连接">在命令行中测试连接</h2><p>先打开任意一个终端，输入命令测试到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">ssh -J 用户名B@B的内网IP 用户名A@A的公网IP</span><br></pre></td></tr></table></figure><p>如果使用的不是标准的22端口，则是</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 -J 用户名B@B的内网IP:B的端口 用户名A@A的公网IP -p A的端口</span><br></pre></td></tr></table></figure><p>如果正常的话，这时应该会进入A的终端</p><h2 id="将连接信息写入到配置文件">将连接信息写入到配置文件</h2><p>在<code>~/.ssh/config</code>中添加如下内容（这个文件不存在就手动新建）</p><figure class="highlight plaintext"><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">Host ServerB</span><br><span class="line">  HostName B的公网IP</span><br><span class="line">  User B的用户名</span><br><span class="line"></span><br><span class="line">Host ServerA</span><br><span class="line">  HostName A的内网IP</span><br><span class="line">  User A的用户名</span><br><span class="line">  ProxyJump ServerB</span><br></pre></td></tr></table></figure><p>说明：</p><ul><li><p>这里的Host后面的名字是主机名，可以自己改</p></li><li><p>ProxyJump后面的内容也可以直接写IP的，但建议还是把B单写出来，这样在连接服务器的时候可以直接使用<code>ssh ServerB</code></p></li><li><p>如果端口不是标准的22，可以在对应服务器的配置下面加<code>Port 端口号</code></p></li><li><p>用户名与当前登陆的用户一致，就可以不写User字段，后面都同理</p></li><li><p>如有需要可以“以此类推”地串联下去，如果使用命令则是<code>ssh 用户名3@服务器3的内网IP -J 用户名1@服务器1的公网IP,用户名2@服务器2的内网IP</code>，连接顺序是1-&gt;2-&gt;3</p></li></ul><h2 id="使用">使用</h2><p>完成配置后，在使用rsync和scp等工具时可以用ServerA（或者自己设的其他名字）来代替服务器A的地址</p><p>例如使用rsync同步本地和远程的<code>/home/user/</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">rsync -a /home/user/ ServerA:/home/user/</span><br></pre></td></tr></table></figure><h1>非SSH协议的服务</h1><p>比如http等不使用SSH的协议，那么就无法通过ProxyJump的方式来连接，需要通过服务器B的SSH对A上的服务进行端口转发</p><blockquote><p>注意这里的HTTP服务指控制面板等，并不适用于需要公开访问的网站，那种情况请使用Nginx等软件进行反代</p></blockquote><blockquote><p>其实这种方法对于SSH协议也生效，只是步入ProxyJump方便</p></blockquote><h2 id="命令行中使用">命令行中使用</h2><p>这里服务器A只需要填IP和端口，连接的其他信息如用户名等在下一步连接时提供</p><p>本地端口可以任意填，可以和服务器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">ssh -L -N -f 本地端口:服务器A的内网IP:用户名A@服务器A的服务端口 用户名B@B的公网IP</span><br></pre></td></tr></table></figure><blockquote><p>如果没有写<code>-f</code>参数，则终端会被阻塞没有响应，这是正常的，退出则需要按<code>Ctrl+C</code></p></blockquote><p>连接上后，就可以使用127.0.0.1和本地端口来连接服务器A上的服务了</p><p>比如http服务，就可以在浏览器打开<code>http://127.0.0.1:本地端口</code></p><h2 id="写入配置文件">写入配置文件</h2><p>本地转发也能在写入到配置文件中 <s>虽然不太常见</s></p><figure class="highlight plaintext"><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 ServerB</span><br><span class="line">  HostName B的公网IP</span><br><span class="line">  User B的用户名</span><br><span class="line">  LocalForward 本地端口 A的内网IP:A的端口</span><br></pre></td></tr></table></figure><h1>参考</h1><p><a href="https://www.redhat.com/en/blog/ssh-proxy-bastion-proxyjump">SSH to remote hosts through a proxy or bastion with ProxyJump</a></p><p><a href="https://wangdoc.com/ssh/port-forwarding">SSH 端口转发</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;在以前，云服务商提供的国内服务器带宽都很有限或非常昂贵，前段时间阿里云发布低价的200M带宽的轻量应用服务器，使得带宽成本大大降低&lt;/p&gt;
&lt;p&gt;一些在已有机器上的服务迁移麻烦，或者因为储存/算力的原因无法迁移，那么就自然而然地想到可以用大带宽机器中转，同时享受到大带宽和高算力/大储存&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
  </entry>
  
  <entry>
    <title>其他语言中的 password_hash()</title>
    <link href="https://stydxm.com/2025/02/04/password-hash-in-python/"/>
    <id>https://stydxm.com/2025/02/04/password-hash-in-python/</id>
    <published>2025-02-04T14:56:17.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>PHP中有“一对函数”<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup><code>password_hash()</code>和<code>password_verify()</code>，用于对密码的加密和验证</p><p>若用其他语言重写PHP后端，通常不会影响到其他数据，但密码比较特殊：它被hash了。因此重写后若想保留用户数据就需要用相同的方式来验证，而且hash算法也是不可逆的，不可能使用其他算法重新编码。所以就需要想办法在其他语言中实现这两个函数</p><span id="more"></span><p>翻一下<a href="https://www.php.net/manual/en/function.password-hash.php">手册</a>可以看到，这两个函数默认使用<a href="https://en.wikipedia.org/wiki/Bcrypt"><code>bcrypt</code></a>算法，那么就可以很轻松地在其他语言中找到第三方库的实现</p><table><thead><tr><th style="text-align:center">语言</th><th style="text-align:center">库</th></tr></thead><tbody><tr><td style="text-align:center">Python</td><td style="text-align:center"><a href="https://pypi.org/project/bcrypt/">bcrypt</a></td></tr><tr><td style="text-align:center">JS</td><td style="text-align:center"><a href="https://www.npmjs.com/package/bcrypt">node.bcrypt.js</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center"><a href="https://pkg.go.dev/golang.org/x/crypto/bcrypt">crypto/bcrypt</a></td></tr></tbody></table><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>PHP里密码哈希相关的还有两个<code>argon2i</code>和<code>argon2id</code>，不过它需要额外编译或安装，我想应该没啥人用？ <a href="#fnref1" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;PHP中有“一对函数”&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;code&gt;password_hash()&lt;/code&gt;和&lt;code&gt;password_verify()&lt;/code&gt;，用于对密码的加密和验证&lt;/p&gt;
&lt;p&gt;若用其他语言重写PHP后端，通常不会影响到其他数据，但密码比较特殊：它被hash了。因此重写后若想保留用户数据就需要用相同的方式来验证，而且hash算法也是不可逆的，不可能使用其他算法重新编码。所以就需要想办法在其他语言中实现这两个函数&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
  </entry>
  
  <entry>
    <title>在RISC-V上使用mid360</title>
    <link href="https://stydxm.com/2025/01/28/mid360-riscv/"/>
    <id>https://stydxm.com/2025/01/28/mid360-riscv/</id>
    <published>2025-01-28T18:58:39.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<h1>起因</h1><p>因为学习需要用了一段时间的<a href="https://www.ros.org/">ROS2</a>，又因为实习工作原因在用RISC-V架构的开发板，于是自然而然的想到了在RISC-V上使用ROS2</p><p>刚巧<a href="https://docs.revyos.dev/desktop/software/ROS2/">RevyOS完成了适配</a>，在手头的LicheePi4A上安装后发现一切都很顺利，就想尝试实际应用一下了</p><p>众所周知啊，ROS2是用于机器人相关开发的，必然要使用到很多硬件；但在此同时呢，绝大多数硬件开发时完全不可能考虑到它在rv架构的兼容性，所以恐怕都比较寄。但是呢，手头刚好有一些mid360激光雷达，它使用TCP/IP来传输数据，兼容性上不容易出问题；同时它也不像工业相机那样有很大的数据量，这块<s>性能羸弱的</s>开发板应该可以正常收发，于是简单尝试了一下</p><h1>运行</h1><p>非常出人意料的是，<a href="https://github.com/Livox-SDK/Livox-SDK2">Livox提供的sdk</a><s>的屎代码</s>虽然使用高版本gcc无法编译<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>，但是我切换到RevyOS源中的gcc-12后就可以正常编译了，只有几个<code>WARNING</code>，与x86上无异</p><h1>结果</h1><p>然后运行<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup><code>livox-ros-driver2</code>，就可以在rviz中看到雷达获取的点云了</p><blockquote><p>这里要吐槽一下，他家有<code>livox_ros_driver</code> <code>livox_ros_driver2</code> <code>livox_ros2_driver</code>，不光代码很屎山，名字还起的这么绕</p></blockquote><p><img src="https://s21.ax1x.com/2025/01/29/pEVNpdA.jpg" alt=""></p><p>用<code>ros2 topic hz</code>查看点云的帧率是正常的，但在rviz中看非常卡，如果要移动视角的话更是低于1fps</p><p>这大概是因为缺少gpu驱动，这个xfce桌面没有使用图形加速；而cpu本身性能就不行，还要负担这么重的图形计算任务，结果就是非常糟糕的体验</p><p>等有了性能足够的芯片以及在图形方面足够的软件支持，或许可以尝试一下把现在做的东西迁移到risc-v上来</p><h1>遇到的问题</h1><p>这里也不是完全没有遇到问题，列出一些我遇到的供参考：</p><h2 id="CMake-Warning">CMake Warning</h2><p>在CMake运行Findxxx.cmake找Eigen, PCL, Python, ROS等依赖时，都会报警告<code>Policy CMP0148 is not set</code>，参考<a href="https://cmake.org/cmake/help/latest/policy/CMP0148.html">文档</a>手动改一下，或者直接设参数<code>-Wno-dev</code>忽略即可，不会影响最终结果</p><h2 id="rviz2报错AT-SPI">rviz2报错AT-SPI</h2><p>运行时可能会有AT-SPI报错</p><figure class="highlight plaintext"><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">[rviz2-2] (rviz2:25390): dbind-WARNING **: 17:30:40.653: AT-SPI: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files</span><br><span class="line">[ERROR] [rviz2-2]: process has died [pid 25390, exit code -11, cmd &#x27;/opt/ros/humble/lib/rviz2/rviz2 --display-config /home/debian/livox_ros_driver2/launch/../config/display_point_cloud.rviz --ros-args&#x27;].</span><br></pre></td></tr></table></figure><p>使用apt安装<code>at-spi2-core</code>包即可解决</p><h2 id="rviz2报错Segmentation-fault">rviz2报错Segmentation fault</h2><p>启动rviz2时会报错<code>Segmentation fault</code>，无其他报错，即使将log-level设为DEBUG也看不到更多有用的信息</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">debian@revyos-lpi4a:~/livox_ros_driver2$ rviz2 --ros-args --log-level DEBUG</span><br><span class="line">[DEBUG] [1742465374.358684709] [rclcpp]: signal handler installed</span><br><span class="line">[DEBUG] [1742465374.358703710] [rclcpp]: deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall</span><br><span class="line">[DEBUG] [1742465374.359187059] [rcl]: Initializing node &#x27;rviz&#x27; in namespace &#x27;&#x27;</span><br><span class="line">[DEBUG] [1742465374.359362064] [rcl]: Using domain ID of &#x27;2&#x27;</span><br><span class="line">[DEBUG] [1742465374.402647462] [rcl]: Initializing publisher for topic name &#x27;/rosout&#x27;</span><br><span class="line">[DEBUG] [1742465374.402933138] [rcl]: Expanded and remapped topic name &#x27;/rosout&#x27;</span><br><span class="line">[DEBUG] [1742465374.410872061] [rcl]: Publisher initialized</span><br><span class="line">[DEBUG] [1742465374.411102068] [rcl]: Node initialized</span><br><span class="line">[DEBUG] [1742465374.411547749] [rcl]: Initializing service for service name &#x27;rviz/get_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.411706088] [rcl]: Expanded and remapped service name &#x27;/rviz/get_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.416014894] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.416214900] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/get_parametersRequest</span><br><span class="line">[DEBUG] [1742465374.416312570] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/get_parametersReply</span><br><span class="line">[DEBUG] [1742465374.416398239] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.416825587] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.417099595] [rcl]: Initializing service for service name &#x27;rviz/get_parameter_types&#x27;</span><br><span class="line">[DEBUG] [1742465374.417228266] [rcl]: Expanded and remapped service name &#x27;/rviz/get_parameter_types&#x27;</span><br><span class="line">[DEBUG] [1742465374.419828684] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.420026023] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/get_parameter_typesRequest</span><br><span class="line">[DEBUG] [1742465374.420121360] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/get_parameter_typesReply</span><br><span class="line">[DEBUG] [1742465374.420198029] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.420439370] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.420626043] [rcl]: Initializing service for service name &#x27;rviz/set_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.420747713] [rcl]: Expanded and remapped service name &#x27;/rviz/set_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.423464468] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.423708142] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/set_parametersRequest</span><br><span class="line">[DEBUG] [1742465374.423797145] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/set_parametersReply</span><br><span class="line">[DEBUG] [1742465374.423875148] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.424161157] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.424362830] [rcl]: Initializing service for service name &#x27;rviz/set_parameters_atomically&#x27;</span><br><span class="line">[DEBUG] [1742465374.424478500] [rcl]: Expanded and remapped service name &#x27;/rviz/set_parameters_atomically&#x27;</span><br><span class="line">[DEBUG] [1742465374.427344926] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.427545599] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/set_parameters_atomicallyRequest</span><br><span class="line">[DEBUG] [1742465374.427635269] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/set_parameters_atomicallyReply</span><br><span class="line">[DEBUG] [1742465374.427713938] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.427935945] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.428139619] [rcl]: Initializing service for service name &#x27;rviz/describe_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.428292290] [rcl]: Expanded and remapped service name &#x27;/rviz/describe_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.430942709] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.431134382] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/describe_parametersRequest</span><br><span class="line">[DEBUG] [1742465374.431227718] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/describe_parametersReply</span><br><span class="line">[DEBUG] [1742465374.431306388] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.431542062] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.431731401] [rcl]: Initializing service for service name &#x27;rviz/list_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.431875073] [rcl]: Expanded and remapped service name &#x27;/rviz/list_parameters&#x27;</span><br><span class="line">[DEBUG] [1742465374.434697164] [rmw_fastrtps_cpp]: ************ Service Details *********</span><br><span class="line">[DEBUG] [1742465374.434888170] [rmw_fastrtps_cpp]: Sub Topic rq/rviz/list_parametersRequest</span><br><span class="line">[DEBUG] [1742465374.434986173] [rmw_fastrtps_cpp]: Pub Topic rr/rviz/list_parametersReply</span><br><span class="line">[DEBUG] [1742465374.435067509] [rmw_fastrtps_cpp]: ***********</span><br><span class="line">[DEBUG] [1742465374.435447855] [rcl]: Service initialized</span><br><span class="line">[DEBUG] [1742465374.435672529] [rcl]: Initializing publisher for topic name &#x27;/parameter_events&#x27;</span><br><span class="line">[DEBUG] [1742465374.435807200] [rcl]: Expanded and remapped topic name &#x27;/parameter_events&#x27;</span><br><span class="line">[DEBUG] [1742465374.442996098] [rcl]: Publisher initialized</span><br><span class="line">[DEBUG] [1742465374.443965796] [rcl]: Initializing subscription for topic name &#x27;/parameter_events&#x27;</span><br><span class="line">[DEBUG] [1742465374.444178803] [rcl]: Expanded and remapped topic name &#x27;/parameter_events&#x27;</span><br><span class="line">[DEBUG] [1742465374.445930193] [rcl]: Subscription initialized</span><br><span class="line">[DEBUG] [1742465374.593761967] [rviz2]: Load pixmap at package://rviz_common/images/splash_overlay.png</span><br><span class="line">Segmentation fault</span><br></pre></td></tr></table></figure><p>解决方案在<a href="https://docs.revyos.dev/docs/desktop/software/ROS2/#rviz2">RevyOS的文档中</a>有写，按文档说明操作即可</p><blockquote><p>使用 <code>sudo switch-gl gl4es</code> 再重启后使用 <code>LIBGL_ALWAYS_SOFTWARE=true rviz2 here</code> 启动</p></blockquote><p>为了在<code>ros2 launch</code>时启动rviz2，我用export命令设置环境变量</p><h2 id="rviz2帧率太低">rviz2帧率太低</h2><p>rviz并不能支持th1520的GPU，所以要正常启动就必须将渲染模式改为软解，即设置上面提到的环境变量</p><p>因为CPU本来就很弱，使用软解只有个位数的帧率，操作体验非常难受</p><p>这里我选择使用ros2的多机通信功能，即在开发板上和另一台性能足够的电脑上设置相同的<code>ROS_DOMAIN_ID</code>，然后在电脑上启动rviz2，这样就能解决性能问题，流畅使用rviz2了</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>这是我之前就发现的问题，在x86_64也是这样的，与risc-v无关 <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p>这里使用的不是<a href="https://github.com/Livox-SDK/livox_ros_driver2">官方仓库</a>的，而是<a href="https://github.com/SMBU-PolarBear-Robotics-Team/livox_ros_driver2">一个第三方修改版</a>，实际功能等都没有变化，只是让代码变得没有那么💩。我后来还提交了<a href="https://github.com/SMBU-PolarBear-Robotics-Team/livox_ros_driver2/pull/2">一个PR</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">没想到啊，这么顺利？</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="ROS" scheme="https://stydxm.com/tags/ROS/"/>
    
  </entry>
  
  <entry>
    <title>构建Text2SQL多轮对话的benchmark</title>
    <link href="https://stydxm.com/2024/07/02/nl2sql-benchmark/"/>
    <id>https://stydxm.com/2024/07/02/nl2sql-benchmark/</id>
    <published>2024-07-02T07:27:17.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<h1>数据集</h1><h2 id="已有数据集">已有数据集</h2><p>经调研，目前已有的较完善的数据集有Chase<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>、SParC<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>、CoSQL<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p><p>其中SParC和CoSQL都是耶鲁大学与SalesForce合作的成果，Chase是西交和微软合作的成果，三个数据集中只有它是中文的</p><h2 id="统计">统计<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup></h2><table><thead><tr><th style="text-align:center">数据集</th><th style="text-align:center">数据量</th><th style="text-align:center">1轮</th><th style="text-align:center">2轮</th><th style="text-align:center">3轮</th><th style="text-align:center">4轮</th><th style="text-align:center">5轮</th><th style="text-align:center">6轮</th><th style="text-align:center">更多</th></tr></thead><tbody><tr><td style="text-align:center">Chase</td><td style="text-align:center">3949</td><td style="text-align:center">0</td><td style="text-align:center">697</td><td style="text-align:center">1858</td><td style="text-align:center">1033</td><td style="text-align:center">352</td><td style="text-align:center">9</td><td style="text-align:center">0</td></tr><tr><td style="text-align:center">SParC</td><td style="text-align:center">3034</td><td style="text-align:center">8</td><td style="text-align:center">793</td><td style="text-align:center">1551</td><td style="text-align:center">633</td><td style="text-align:center">44</td><td style="text-align:center">5</td><td style="text-align:center">0</td></tr><tr><td style="text-align:center">CoSQL</td><td style="text-align:center">2458</td><td style="text-align:center">2</td><td style="text-align:center">5</td><td style="text-align:center">64</td><td style="text-align:center">874</td><td style="text-align:center">675</td><td style="text-align:center">456</td><td style="text-align:center">382</td></tr></tbody></table><figure class="highlight python"><figcaption><span>Chase\SParC</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> json</span><br><span class="line">data=json.load(<span class="built_in">open</span>(<span class="string">&quot;train.json&quot;</span>))</span><br><span class="line">count=&#123;&#125;</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">list</span>(data):</span><br><span class="line">    l=<span class="built_in">len</span>(i[<span class="string">&quot;interaction&quot;</span>])</span><br><span class="line">    count[l]=count.get(l,<span class="number">0</span>)+<span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">list</span>(<span class="built_in">sorted</span>(count)):</span><br><span class="line">    <span class="built_in">print</span>(i,<span class="string">&quot; &quot;</span>,count[i])</span><br></pre></td></tr></table></figure><figure class="highlight python"><figcaption><span>CoSQL</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><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="keyword">import</span> json</span><br><span class="line">data=json.load(<span class="built_in">open</span>(<span class="string">&quot;cosql_all_info_dialogs.json&quot;</span>))</span><br><span class="line">count=&#123;&#125;</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">list</span>(data):</span><br><span class="line">    i=data[i]</span><br><span class="line">    l=<span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> j <span class="keyword">in</span> i[<span class="string">&quot;turns&quot;</span>]:</span><br><span class="line">        <span class="keyword">if</span> j[<span class="string">&quot;isUser&quot;</span>]:</span><br><span class="line">            l+=<span class="number">1</span></span><br><span class="line">    count[l]=count.get(l,<span class="number">0</span>)+<span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">list</span>(<span class="built_in">sorted</span>(count)):</span><br><span class="line">    <span class="built_in">print</span>(i,<span class="string">&quot; &quot;</span>,count[i])</span><br></pre></td></tr></table></figure><h2 id="exact-set-match-metrics">exact set match metrics</h2><p>浏览数据集的时候发现，chase的ground truth以两种形式同时呈现</p><p>比如，这个语句<code>SELECT name FROM department GROUP BY departmentID ORDER BY count(departmentID) DESC LIMIT 1;</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><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">&quot;sql&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;orderBy&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="string">&quot;desc&quot;</span><span class="punctuation">,</span> </span><br><span class="line">        <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">[</span></span><br><span class="line">                <span class="number">0</span><span class="punctuation">,</span> </span><br><span class="line">                <span class="punctuation">[</span></span><br><span class="line">                    <span class="number">3</span><span class="punctuation">,</span> </span><br><span class="line">                    <span class="number">5</span><span class="punctuation">,</span> </span><br><span class="line">                    <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">                <span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">                <span class="literal"><span class="keyword">null</span></span></span><br><span class="line">            <span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;from&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;table_units&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;table_unit&quot;</span><span class="punctuation">,</span> </span><br><span class="line">                <span class="number">1</span></span><br><span class="line">            <span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">        <span class="attr">&quot;conds&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><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;union&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;except&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;groupBy&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">[</span></span><br><span class="line">            <span class="number">0</span><span class="punctuation">,</span> </span><br><span class="line">            <span class="number">5</span><span class="punctuation">,</span> </span><br><span class="line">            <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">        <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;limit&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;intersect&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">null</span></span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;where&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;having&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">    <span class="attr">&quot;select&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span> </span><br><span class="line">        <span class="punctuation">[</span></span><br><span class="line">            <span class="punctuation">[</span></span><br><span class="line">                <span class="number">0</span><span class="punctuation">,</span> </span><br><span class="line">                <span class="punctuation">[</span></span><br><span class="line">                    <span class="number">0</span><span class="punctuation">,</span> </span><br><span class="line">                    <span class="punctuation">[</span></span><br><span class="line">                        <span class="number">0</span><span class="punctuation">,</span> </span><br><span class="line">                        <span class="number">6</span><span class="punctuation">,</span> </span><br><span class="line">                        <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">                    <span class="punctuation">]</span><span class="punctuation">,</span> </span><br><span class="line">                    <span class="literal"><span class="keyword">null</span></span></span><br><span class="line">                <span class="punctuation">]</span></span><br><span class="line">            <span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">]</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>可以看出来以操作的关键字结构化地分割了，但是这样做的目的，以及对象中的数字、布尔值、空值的意义都不明确</p><p>而在SParC的论文<sup class="footnote-ref"><a href="#fn2" id="fnref2:1">[2:1]</a></sup>中提到了，这是受Spider<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup>的启发</p><blockquote><p>Following Yu et al. (2018c), we use the exact set match metric to compute the accuracy between gold and predicted SQL answers. Instead of simply employing string match, Yu et al. (2018c) decompose predicted queries into different SQL clauses such as SELECT, WHERE, GROUP BY, and ORDER BY and compute scores for each clause using set matching separately.</p></blockquote><p>这么做的原因是为了提高打分的精确度，以及对不同操作进行更细粒度的评测</p><blockquote><p>We report the following two metrics: question match, the exact set matching score over all questions, and interaction match, the exact set matching score over all interactions. The exact set matching score is 1 for each question only if all predicted SQL clauses are correct, and 1 for each interaction only if there is an exact set match for every question in the interaction.</p></blockquote><p>spider也提供了<a href="https://github.com/taoyds/spider/blob/master/process_sql.py">一个转换脚本</a></p><h2 id="构建数据集">构建数据集</h2><p>上述Chase和SParC是已有工作中比较好的 <s>好像也没啥别的</s> 故采用他们提出的方法，针对项目的场景，构建自己的数据集</p><h1>评测</h1><p>使用耶鲁大学课题组与论文一同发布的<a href="https://github.com/taoyds/test-suite-sql-eval">评测代码</a>，并在此基础上做一些小修改以适应需求</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://xjtu-intsoft.github.io/chase/">Chase 1.0: A Large-Scale and Pragmatic Chinese Dataset for Cross-Database Context-Dependent Text-to-SQL</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://yale-lily.github.io/sparc">SParC 1.0: Yale &amp; Salesforce Semantic Parsing and Text-to-SQL in Context Challenge</a> <a href="#fnref2" class="footnote-backref">↩︎</a> <a href="#fnref2:1" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://yale-lily.github.io/cosql">CoSQL 1.0: A Conversational Text-to-SQL Challenge Towards Cross-Domain Natural Language Interfaces to Databases</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p>SParc中有4个“0轮”的，即该条数据的interaction键对应的数组长度为0，但因为final中储存了问题和答案，所以实际也是1轮，只是缺少了意图等数据 <a href="#fnref4" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://arxiv.org/abs/1809.08887">Spider: A Large-Scale Human-Labeled Dataset for Complex and Cross-Domain Semantic Parsing and Text-to-SQL Task</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">最近因项目需要做的一些调研，顺手记录一下了</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
  </entry>
  
  <entry>
    <title>Hi6285</title>
    <link href="https://stydxm.com/2024/03/09/hi6285/"/>
    <id>https://stydxm.com/2024/03/09/hi6285/</id>
    <published>2024-03-09T12:56:38.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>该样品和下面的Dieshot均来自于<a href="https://space.bilibili.com/427356495/">Kurnal</a></p><span id="more"></span><p><img src="https://s21.ax1x.com/2024/03/09/pFyZdMV.png" alt=""></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;该样品和下面的Dieshot均来自于&lt;a href=&quot;https://space.bilibili.com/427356495/&quot;&gt;Kurnal&lt;/a&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="数码" scheme="https://stydxm.com/categories/%E6%95%B0%E7%A0%81/"/>
    
    
  </entry>
  
  <entry>
    <title>HGAME Week1</title>
    <link href="https://stydxm.com/2024/01/30/hgame-week1/"/>
    <id>https://stydxm.com/2024/01/30/hgame-week1/</id>
    <published>2024-01-30T18:19:11.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<h1>HGAME</h1><p>出于一些机缘巧合，暑假里加入了<a href="https://vidar.club/">Vidar-Team</a>的新生群，逐渐开始了解CTF<span id="more"></span>，八月打了一段时间的hgame-mini，一度（指大佬还没来的时候）排到了第一页<br>虽然加入vidar基本没有可能，但也有幸认识了一些成员，这里特别要感谢<a href="https://4nsw3r.top/">Answer</a>和<a href="https://ek1ng.com/">eking</a>的无私奉献对我帮助非常大，也很感谢<a href="https://baimeow.cn/">baimeow</a>和<a href="https://potat0.cc/">potato</a>带我加入了<a href="https://dn11.top/">DN11</a>，对计网有了点更深入的了解<br>开学后各种事情以及开发任务逐渐增加，同时也很难找到队友，甚至于参加省赛时三个人的队伍剩下两个人一道题都没做出来，我只能选择放弃了安全这个方向。杭电的学长们一直在提的HGAME这会儿开始了，也就在寒假里抽空随便做点吧，应该是<s>根本不存在的</s>生涯中最后一赛了</p><h1>MISC</h1><h2 id="签到">签到</h2><p><img src="https://s11.ax1x.com/2024/02/25/pFaAenx.png" alt=""></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hgame&#123;welc0m3_t0_HGAME_2024&#125;</span><br></pre></td></tr></table></figure><p>到此一游</p><h2 id="SignIn">SignIn</h2><p>![try_another_way_to_see.png]<br>传到手机上从不同方向看</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hgame&#123;WOW_GREAT_YOU_SEE_IT_WONDERFUL&#125;</span><br></pre></td></tr></table></figure><h1>Web</h1><h2 id="ezHTTP">ezHTTP</h2><blockquote><p>前半段貌似hgame-mini同款</p></blockquote><p>访问靶机，返回<code>请从vidar.club访问这个页面</code><br>设置请求Headers</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Referer: vidar.club</span><br></pre></td></tr></table></figure><p>返回<code>请通过Mozilla/5.0 (Vidar; VidarOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0访问此页面</code>，接着改Headers</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">User-Agent: Mozilla/5.0 (Vidar; VidarOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0</span><br></pre></td></tr></table></figure><p>返回<code>请从本地访问这个页面</code>，设置<code>X-Forwarded-For: 127.0.0.1</code>，发现返回值没有变化，仔细一看返回Headers里有个<code>Hint: Not XFF</code><br><img src="https://s11.ax1x.com/2024/01/31/pFKR0Hg.png" alt=""><br>略加思索~~（指ChatGPT）~~，把XFF改成了<code>X-Real-IP</code>，返回值变成了<code>Ok, the flag has been given to you ^-^</code><br>返回Headers里有<code>Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJGMTRnIjoiaGdhbWV7SFRUUF8hc18xbVAwclQ0bnR9In0.VKMdRQllG61JTReFhmbcfIdq7MvJDncYpjaT7zttEDc</code>，一眼JWT的格式，base64解码之后得到flag</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hgame&#123;HTTP_!s_1mP0rT4nt&#125;</span><br></pre></td></tr></table></figure><h2 id="Bypass-it">Bypass it</h2><p>访问靶机，弹窗提示<code>欢迎使用用户管理系统，请先登陆</code>，点击确定后跳转到<code>/login.html</code><br><img src="https://s11.ax1x.com/2024/01/31/pFKRbgx.png" alt=""><br>点击注册按钮后跳转到<code>register_page.php</code>，虽然会弹窗<code>很抱歉，当前不允许注册</code><br>开始还以为题目名字的意思是SQL注入，试了好久也没成功，后来无意中发现注册页面可以看到元素</p><figure class="highlight html"><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="tag">&lt;<span class="name">li</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">label</span>&gt;</span>用户名:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">li</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">label</span>&gt;</span>密　码:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br></pre></td></tr></table></figure><p>根据表单构造请求，返回数据</p><figure class="highlight html"><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="tag">&lt;<span class="name">script</span> <span class="attr">language</span>=<span class="string">&#x27;javascript&#x27;</span> <span class="attr">defer</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">alert</span>(<span class="string">&#x27;注册成功&#x27;</span>);top.<span class="property">location</span>.<span class="property">href</span>=<span class="string">&#x27;login.html&#x27;</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>正常登录即可获取到flag<br><img src="https://s11.ax1x.com/2024/01/31/pFKROKK.png" alt=""></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hgame&#123;b5619c116a434cda50308d84f381ea85124d1b2e&#125;</span><br></pre></td></tr></table></figure><h2 id="Select-Courses">Select Courses</h2><p>打开靶机，啊这正方教务的UI……<br><img src="https://s11.ax1x.com/2024/01/31/pFKRrNj.png" alt=""><br>所有课都显示已满，点击有弹窗提示<code>课程已满！</code><br><img src="https://s11.ax1x.com/2024/02/25/pFaAEcR.png" alt=""><br>打开开发者工具，发现点击选课按钮的时候会POST请求<code>/api/courses</code>，payload为<code>{&quot;id&quot;:1}</code>，返回<code>{&quot;full&quot;:1,&quot;message&quot;:&quot;课程已满&quot;}</code><br>点右上选完了按钮会请求<code>/api/ok</code>并返回<code>{message: &quot;呜呜呜，还没选上课呢！&quot;}</code><br>查看源码还可以发现课程列表来自GET请求<code>/api/courses</code><br>然后……最近项目有点多，只能腾出这半小时，没时间研究了，就这样吧（</p><blockquote><p>后来看wp，原来是思路完全错了，要轮询抢课</p></blockquote>]]></content>
    
    
    <summary type="html">&lt;h1&gt;HGAME&lt;/h1&gt;
&lt;p&gt;出于一些机缘巧合，暑假里加入了&lt;a href=&quot;https://vidar.club/&quot;&gt;Vidar-Team&lt;/a&gt;的新生群，逐渐开始了解CTF</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="安全" scheme="https://stydxm.com/tags/%E5%AE%89%E5%85%A8/"/>
    
  </entry>
  
  <entry>
    <title>解包小米路由器固件</title>
    <link href="https://stydxm.com/2023/10/31/miwifi-firmware/"/>
    <id>https://stydxm.com/2023/10/31/miwifi-firmware/</id>
    <published>2023-10-31T16:36:27.000Z</published>
    <updated>2023-11-07T08:48:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>看到<a href="https://github.com/ljcbaby">Ljcbaby</a>在群里说，小米路由器开ssh的<a href="https://zhuanlan.zhihu.com/p/460949138">那个洞</a>在新机器可能依然存在，遂一起研究一下</p><span id="more"></span><blockquote><p>本文中的操作主要在Ubuntu23.04中完成</p></blockquote><p>先从<a href="http://miwifi.com/miwifi_download.html">小米官网</a>上下载固件，没什么好说的（还在用http，离谱）。<br>这里为了对比，下了ax3000t和ac2100的固件，这两台机器都有人在用，并且后者是已知有这个洞，而前者固件修复了这个问题但据说依然有办法。<br>不清楚新固件有没有修，ac2100就用了<a href="http://cdn.cnbj1.fds.api.mi-img.com/xiaoqiang/rom/r2100/miwifi_r2100_firmware_4b519_2.0.722.bin">2.0.722</a>的<code>miwifi_r2100_firmware_4b519_2.0.722.bin</code></p><h2 id="解包">解包<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></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">pip3 install ubi_reader</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">ubireader_extract_images miwifi_r2100_firmware_4b519_2.0.722.bin</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">UBI_File Warning: end_offset - start_offset length is not block aligned, could mean missing data.UBI_File Warning: end_offset - start_offset length is not block aligned, could mean missing data.</span><br></pre></td></tr></table></figure><p>有警告，不过问题不大，得到<code>img-1537728761_vol-ubi_rootfs.ubifs</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">unsquashfs img-1537728761_vol-ubi_rootfs.ubifs</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">Parallel unsquashfs: Using 6 processors</span><br><span class="line">2763 inodes (2453 blocks) to write</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">create_inode: could not create character device squashfs-root/dev/console, because you&#x27;re not superuser!</span><br><span class="line">[========================================================================================================================================/ ] 5215/5216  99%</span><br><span class="line"></span><br><span class="line">created 2422 files</span><br><span class="line">created 219 directories</span><br><span class="line">created 339 symlinks</span><br><span class="line">created 0 devices</span><br><span class="line">created 0 fifos</span><br><span class="line">created 0 sockets</span><br><span class="line">created 1 hardlink</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">ls</span></span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin  data  dev  etc  lib  mnt  overlay  proc  readonly  rom  root  sbin  sys  tmp  userdisk  usr  var  www</span><br></pre></td></tr></table></figure><p>代码在<code>/usr/lib/lua/xiaoqiang</code>里（好像代码层面小米路由器的代号是xiaoqiang？）<br>ax3000t的.bin文件可以解出<code>img-1508723001_vol-kernel.ubifs</code>和<code>img-1508723001_vol-rootfs.ubifs</code>两个文件，但kernel那个用<code>unsquashfs</code>解不开，应该只是系统内核，意义不大，rootfs那个里面就有目录结构了</p><h2 id="反编译">反编译</h2><p>这里用了<a href="https://github.com/NyaMisty/unluac_miwifi">一个github项目</a>，简单翻了下代码在<code>/src/unluac/</code>，但是没看到maven或者gradle之类的配置文件<br>后来在actions配置（<code>/.github/workflows/jarbuild.yml</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> build</span><br><span class="line">javac -d build -sourcepath src  src/unluac/*.java</span><br><span class="line">jar -cfm build/unluac.jar src/META-INF/MANIFEST.MF -C build  .</span><br></pre></td></tr></table></figure><p>得到编译产物unluac.jar，再扫两眼代码看下用法，<s>让ChatGPT</s>写个脚本</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"><span class="comment"># 创建decompiled目录（如果不存在）</span></span><br><span class="line"><span class="built_in">mkdir</span> -p decompiled</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用find命令递归查找source目录下的所有.lua文件</span></span><br><span class="line">find <span class="built_in">source</span> -<span class="built_in">type</span> f -name <span class="string">&quot;*.lua&quot;</span> -print0 | <span class="keyword">while</span> IFS= <span class="built_in">read</span> -r -d $<span class="string">&#x27;\0&#x27;</span> file; <span class="keyword">do</span></span><br><span class="line">    <span class="comment"># 构造输出文件路径（替换source为decompiled）</span></span><br><span class="line">    output_file=<span class="string">&quot;decompiled/<span class="subst">$(dirname <span class="string">&quot;<span class="variable">$&#123;file/source/decompiled&#125;</span>&quot;</span>)</span>/<span class="subst">$(basename <span class="string">&quot;<span class="variable">$file</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 创建输出文件的目录（如果不存在）</span></span><br><span class="line">    <span class="built_in">mkdir</span> -p <span class="string">&quot;<span class="subst">$(dirname <span class="string">&quot;<span class="variable">$output_file</span>&quot;</span>)</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 使用unluac.jar工具进行反编译，将输出写入到对应的文件中</span></span><br><span class="line">    java -jar ./unluac.jar <span class="string">&quot;<span class="variable">$file</span>&quot;</span> &gt; <span class="string">&quot;<span class="variable">$output_file</span>&quot;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 打印处理的文件路径</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;Decompiled: <span class="variable">$file</span> -&gt; <span class="variable">$output_file</span>&quot;</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>这是Windows的，但不知道为什么（可能是他程序的问题）子目录下的识别不到</p><figure class="highlight bat"><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">echo</span> off</span><br><span class="line"><span class="built_in">setlocal</span> enabledelayedexpansion</span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 设置源目录和目标目录</span></span><br><span class="line"><span class="built_in">set</span> &quot;sourceDir=source&quot;</span><br><span class="line"><span class="built_in">set</span> &quot;outputDir=decompiled&quot;</span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 创建目标目录</span></span><br><span class="line"><span class="built_in">mkdir</span> <span class="variable">%outputDir%</span> <span class="number">2</span>&gt;<span class="built_in">nul</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 递归处理源目录下的所有.lua文件</span></span><br><span class="line"><span class="keyword">for</span> /r &quot;<span class="variable">%sourceDir%</span>&quot; <span class="variable">%%f</span> <span class="keyword">in</span> (*.lua) <span class="keyword">do</span> (</span><br><span class="line">    <span class="built_in">set</span> &quot;sourceFile=<span class="variable">%%f</span>&quot;</span><br><span class="line">    <span class="built_in">set</span> &quot;outputFile=<span class="variable">!sourceFile:%sourceDir%=%outputDir%!</span>&quot;</span><br><span class="line">    java -jar unluac.jar &quot;<span class="variable">!sourceFile!</span>&quot; &gt; &quot;<span class="variable">!outputFile!</span>&quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> &quot;反编译完成&quot;</span><br><span class="line"><span class="built_in">pause</span></span><br></pre></td></tr></table></figure><p>至此，我们就获得了小米路由器的源码，虽然经过了混淆但还是有一定的意义，可以进行一些简单的字符串搜索或者扔给AI</p><p><strong>2023.11.7更新</strong><br>ljc看到称bug存在的那位说出了方法，就不再研究了<br>但是因为群是比较封闭的小群，也没问，所以就不把方法写出来了</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://bbs.hassbian.com/thread-17298-1-1.html">某论坛的一个帖子</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;看到&lt;a href=&quot;https://github.com/ljcbaby&quot;&gt;Ljcbaby&lt;/a&gt;在群里说，小米路由器开ssh的&lt;a href=&quot;https://zhuanlan.zhihu.com/p/460949138&quot;&gt;那个洞&lt;/a&gt;在新机器可能依然存在，遂一起研究一下&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="小米" scheme="https://stydxm.com/tags/%E5%B0%8F%E7%B1%B3/"/>
    
    <category term="路由器" scheme="https://stydxm.com/tags/%E8%B7%AF%E7%94%B1%E5%99%A8/"/>
    
  </entry>
  
  <entry>
    <title>Google I/O Connect 2023</title>
    <link href="https://stydxm.com/2023/09/07/google-io-connect-2023/"/>
    <id>https://stydxm.com/2023/09/07/google-io-connect-2023/</id>
    <published>2023-09-07T12:20:36.000Z</published>
    <updated>2023-11-07T08:48:00.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文图片可见<a href="https://gallery.stydxm.com/activity/google-io-connect-2023/">相册</a></p></blockquote><p>八月看到了谷歌发了篇<a href="https://mp.weixin.qq.com/s/ipdWxBQJUuuRwheACY7aIQ">推文</a>，反正暑假也没什么事，就报了个名<br>报名表里有职位这么个选项，同时又觉得谷歌开发者大会的审核可能并不容易通过（而且这是io connect而不是往年的summit），刚好在朋友的<s>空壳</s>公司挂了<s>虚职</s>CTO，就写了个C-level高管 <s>（不到二十岁，事CTO）</s> ，于是半个多月之后……<br><img src="https://s1.ax1x.com/2023/09/07/pPysskd.png" alt=""></p><h1>上海，我来力</h1><p><img src="https://s1.ax1x.com/2023/09/11/pPg1KYD.png" alt=""><br>说起来其实高考完这个暑假已经来了四次上海了，一次bw一次cj，还有一次是陪来上海的外地朋友，不过世博园这里倒是从世博会之后就没来过了<br>当时还小，对世博会已经没什么印象了，但这个中国馆总是给我种未曾谋面但非常熟悉的感觉<br><img src="https://s1.ax1x.com/2023/09/10/pPcXpct.png" alt=""></p><h1>混入其中</h1><p>订的酒店挺便宜的，离世博园也不远，走一个红绿灯就能看到世博会中国馆了，但到世博中心还得坐一站地铁<br><img src="https://s1.ax1x.com/2023/09/10/pPcXFHS.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/10/pPcX9jP.png" alt=""><br>到正面再来一张<br><img src="https://s1.ax1x.com/2023/09/10/pPcXPnf.png" alt=""><br>差不多准时到的，门口排队的人不多，目测一两百人（bw cj几万十几万人排队给我整出心理阴影了），人均双肩包+黑白T恤<br><img src="https://s1.ax1x.com/2023/09/10/pPcXiB8.png" alt=""><br>因为人少，没多久就排完队进场了<br><img src="https://s1.ax1x.com/2023/09/10/pPcXAAg.png" alt=""><br>Google标和标志性的Google色<br><img src="https://s1.ax1x.com/2023/09/10/pPcXe9s.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/10/pPcXENQ.png" alt=""></p><h2 id="会场">会场</h2><p>现场有一个装饰成鼓形的屏幕，挺有意思的，在旁边背景板前有机器可以拍照，拍完之后点同意就会展示在屏幕上，并且也可以用手机扫描二维码获取照片<br><img src="https://s1.ax1x.com/2023/09/10/pPcXm3n.png" alt=""><br>当时排队的人贼多，后来走之前几乎没人了就拍了几张，爆照就算了（<br>这就是那面背景墙<br><img src="https://s1.ax1x.com/2023/09/10/pPcXlHU.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/10/pPcXVhj.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/10/pPcXG4J.png" alt=""><br><sup>签到处</sup><br><img src="https://s1.ax1x.com/2023/09/11/pPgPc8O.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/10/pPcX3EF.png" alt=""><br><sup>巨大的……可以叫海报吧？</sup><br><img src="https://s1.ax1x.com/2023/09/10/pPcX8N4.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgPfrd.png" alt=""><br><sup>结合了Google风格颜色和中国风外形的背景墙</sup><br>估计是因为审核相对较严， <s>（怎么把我放进来了）</s> 会场上人并不多，目测最多也就四五百吧<br><img src="https://s1.ax1x.com/2023/09/11/pPgP5VI.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgPIat.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgPoIP.png" alt=""><br>还有苦逼的打工人，到了这还得加班<br><img src="https://s1.ax1x.com/2023/09/11/pPgPhqA.png" alt=""></p><h2 id="签到">签到</h2><p>签到之后领到了小礼品，一个袋子和几个徽章<br><img src="https://s1.ax1x.com/2023/09/10/pPcXuj0.png" alt=""><br>这设计风格很Google<br><img src="https://s1.ax1x.com/2023/09/10/pPcXQBT.png" alt=""><br>免费报名的活动还管饭，虽然标配但还是要说一声良心<br><img src="https://s1.ax1x.com/2023/09/10/pPcXncq.png" alt=""></p><h2 id="Keynote">Keynote</h2><p><img src="https://s1.ax1x.com/2023/09/11/pPgP7Pf.png" alt=""><br>主旨演讲开始前半小时就放开了所有的区域，可以进入会场了<br><img src="https://s1.ax1x.com/2023/09/11/pPg1Zex.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg1mTK.png" alt=""><br><sup>Google大中华区的总裁陈俊挺，但说中文听起来怪怪的</sup><br>吉祥物大黄，很符合我对外企在华本地化的想象（<br><img src="https://s1.ax1x.com/2023/09/11/pPg1ew6.png" alt=""><br>然后就是各路title听起来很牛逼的Google大佬<br><img src="https://s1.ax1x.com/2023/09/11/pPg1uFO.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg1Mfe.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg1lSH.png" alt=""><br><sup>↑这位是Google Web ML的部门主管，记住他，后面还有</sup><br><a href="https://ioconnectchina.googlecnapps.cn/speakers/profile/a-jason-mayes-ai02/">https://ioconnectchina.googlecnapps.cn/speakers/profile/a-jason-mayes-ai02/</a><br>以及谷歌的一些技术和计划<br><img src="https://s1.ax1x.com/2023/09/11/pPg136A.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg11ld.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg18OI.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPg1Jmt.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgPwr9.png" alt=""><br><sup>这张并不是Keynote，而是后面演讲时拍的</sup><br>主旨演讲的内容也可以在<a href="https://www.bilibili.com/video/BV1uN411p72G">这里</a>看到</p><h2 id="餐食">餐食</h2><p>午餐可以凭餐券从中餐、西餐和纯素食中选一份，我这份是西餐，对于我这个不挑剔的人来说味道也还挺好的<br><img src="https://s1.ax1x.com/2023/09/11/pPgPs56.png" alt=""><br>全天都有供应零食咖啡饮料，还有一些冰柜里放着文创雪糕<br><img src="https://s1.ax1x.com/2023/09/11/pPg1tTf.png" alt=""></p><h2 id="展示区">展示区</h2><p>在<a href="https://i-h5.btech.cc/google2308/index.html">地图上</a>可以看到展示区的内容<br>里面也有不少的内容，比如Google一些工具的特性前瞻和一些团队与Google合作做出来的一些有意思的项目<br>比如这个，给乐高加上硬件接口的项目组，就用乐高积木做了个简单的循迹小车<br><img src="https://s1.ax1x.com/2023/09/11/pPgP0bR.png" alt=""><br>还有一个叫<a href="https://github.com/google/project-gameface">Gameface</a>的项目，用摄像头识别人脸动作代替传统的昂贵而适用性反而更差的专用控制设备，帮助有肢体残疾和渐冻症等的残疾人玩游戏（在主旨演讲中也有提到）<br>至于反作弊，现场问了下确实是有被反作弊识别为外挂的情况，但我觉得做一个虚拟的控制设备就可以解决<br><img src="https://s1.ax1x.com/2023/09/11/pPgiZZR.png" alt=""><br><sup>来视察工作的陈总</sup></p><h3 id="AI">AI</h3><p>从现场展示区的布局和主旨演讲中的占比可以看得出来，重头还是AI<br><img src="https://s1.ax1x.com/2023/09/11/pPgPDV1.png" alt=""><br>但是展示区这里的demo和技术似乎……也没有什么能让我眼前一亮的东西<br>比如这个跑在浏览器里的Mediapipe，但TensorFlow.js也出现好久了，这些基础的CV也不是很重的运算任务，虽然帧数确实跑得很高<br><img src="https://s1.ax1x.com/2023/09/11/pPg1atS.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgDoTg.png" alt=""><br><sup><s>看我自己设计的T恤</s></sup><br>把palm 2集成进了Android studio，问了一下有支持别的语言的计划但并没有移植到别的IDE的计划<br>以及，工作人员说</p><blockquote><p>Unfortunately, this is only available in the US.</p></blockquote><p><img src="https://s1.ax1x.com/2023/09/11/pPg1Uk8.png" alt=""><br>你这些关键词里有几个不太对劲啊.jpg<br><img src="https://s1.ax1x.com/2023/09/11/pPg1dfg.png" alt=""><br>还有一些flutter啊，passkey啊还有chrome devtools的自动填充什么的这些也没啥意思，拍照都没拍，也没去深入了解</p><h2 id="合影">合影</h2><p>B站看到了<a href="https://space.bilibili.com/130763764">三太子敖丙</a>的直播<br><img src="https://s1.ax1x.com/2023/09/09/pP6rdAJ.png" alt=""><br>跑过去蹭了个合照<br><img src="https://s1.ax1x.com/2023/09/11/pPgD5m8.png" alt=""><br>后来又看到<a href="https://space.bilibili.com/19319172">差评君</a>也在直播<br><img src="https://s1.ax1x.com/2023/09/09/pP6r0hR.png" alt=""><br>还没去找，刚巧他就往我这走过来了，不过看这阵仗，旁边还围了几个人，也没好意思去打扰<br><img src="https://s1.ax1x.com/2023/09/11/pPgP6PK.png" alt=""></p><h2 id="one-more-thing">one more thing</h2><p>听完主旨演讲往展示区走的路上，看到有好多人围着一位在交流，想这肯定是个大佬，就拍了张照<br><img src="ttps://s1.ax1x.com/2023/09/11/pPg1Y0P.png" alt=""><br>没想到他看到之后招了招手，我就凑过去跟他聊了两句，顺便合了个影<br><img src="https://s1.ax1x.com/2023/09/11/pPgDhOf.png" alt=""><br>后来在TensorFlow.js那里见到他了，他甚至认识我了，主动跟我打了个招呼<br>我看到他展台摆的也是Mediapipe（他对面的是Mediapipe但是注重的是这个模型，而他主要讲解的是在浏览器里运行这个模型），刚巧之前我也<a href="/2022/10/08/smile-aim/">用到过这个项目</a>，就只能尴尬地临时想了个问题，问有没有计划使模型泛用性更强，比如让它可以识别猫狗这些动物<br>可能是我英语不好没表达清楚，也可能是会场太吵了导致他听错了，他似乎以为我问的是如何训练针对其他动物的模型，于是跟我讲了好久you need losts of data和actually you can do this with mediapipe’s code and cats’ dataset<br>看他兴致勃勃地，我也不好意思打断他，听他讲完之后表示谢意就跑了<br>然后，突然发现他的挂牌挂绳是代表主旨演讲嘉宾的红色，瞥到一眼腰间别着的工牌上的名字，发现是Jason Mayes<br>对，前面提到那个Google Web ML的主管<br>是的……Google Web机器学习的主管，教我机器学习……</p><h1>回家</h1><p>再看两眼中国馆<br><img src="https://s1.ax1x.com/2023/09/11/pPgPWKH.png" alt=""><br><img src="https://s1.ax1x.com/2023/09/11/pPgPg2D.png" alt=""><br>跑路（<br><img src="https://s1.ax1x.com/2023/09/11/pPgP2xe.png" alt=""></p><h1>结尾</h1><p>开始我觉得通过我的报名表只是审查中的疏漏，没有发现我这个19岁的“C-level高管”<br>但参与活动后我越来越觉得这更是刻意而为之<br>我看到了在二楼一张小桌旁围坐着开会的Google员工，他们的神情平等地投入而又专注、眼中闪烁着相同的创意和想象力；主管级的技术大牛愿意为一个几乎没有机器学习基础的准大一新生细致地讲解训练一个能够识别猫的骨骼的模型的步骤<br>而我在他们的眼里，会不会就是一个未来可期的年轻创业者呢？<br>虽然现在我不是，但我希望我在四年内会是</p>]]></content>
    
    
    <summary type="html">我就像那只混进狼群的哈士奇</summary>
    
    
    
    <category term="流水账" scheme="https://stydxm.com/categories/%E6%B5%81%E6%B0%B4%E8%B4%A6/"/>
    
    
  </entry>
  
  <entry>
    <title>Intel SoMa</title>
    <link href="https://stydxm.com/2023/03/18/intel-soma/"/>
    <id>https://stydxm.com/2023/03/18/intel-soma/</id>
    <published>2023-03-18T04:04:49.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>等了好久，终于拿到这几颗soma b3了</p><span id="more"></span><p><img src="https://s1.ax1x.com/2023/03/18/ppJp078.jpg" alt=""><br>我手上的这三颗编号是1097 1739 3246<br>还有我之前搞到的两颗soma（编号476 2948）<br><img src="https://s1.ax1x.com/2023/03/18/ppJKzUH.jpg" alt=""><br>虽然点不亮用不上，这每核15线程<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>的架构只是拿来收藏也挺有意思的<br>（不过按照结城的说法，成千上万颗+Intel FPGA这两个要求或许就注定民间无法点亮soma了）</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://www.bilibili.com/read/cv7525799">原文</a>，<a href="https://web.archive.org/web/20230302144219/https://www.bilibili.com/read/cv7525799">存档1</a>，<a href="https://archive.md/4Iktl">存档2</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;等了好久，终于拿到这几颗soma b3了&lt;/p&gt;</summary>
    
    
    
    <category term="数码" scheme="https://stydxm.com/categories/%E6%95%B0%E7%A0%81/"/>
    
    
  </entry>
  
  <entry>
    <title>高考前的第一百天</title>
    <link href="https://stydxm.com/2023/02/27/100-days-before-gaokao/"/>
    <id>https://stydxm.com/2023/02/27/100-days-before-gaokao/</id>
    <published>2023-02-27T14:13:55.000Z</published>
    <updated>2025-11-25T21:31:43.286Z</updated>
    
    <content type="html"><![CDATA[<p>今天是2023.2.27，如果我们没有数错的话离6.7的高考还有刚好100天时间<br>三年初中三年高中过去的这么快，体验过首考之后的我也知道一百天只会更快</p><span id="more"></span><p>首考不甚理想，离我的目标杭电计算机还有大概两百五十分差距。当然语数两门课考到这么高是不可能的，只能寄希望于另四门课有些提升吧<br>毕竟绝大多数人的高考只有一次，一念之间，一道数学选择题就是五分，哪怕是多了或者少了一分也有可能会极大地改变人生轨迹<br>不敢期望高考能有什么超常发挥，每门课都能发挥出应有的水平就是很大的幸运了<br><img src="https://s1.ax1x.com/2023/02/27/ppClxv4.jpg" alt=""><br>（这是晚上在操场上拍的星空）</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;今天是2023.2.27，如果我们没有数错的话离6.7的高考还有刚好100天时间&lt;br&gt;
三年初中三年高中过去的这么快，体验过首考之后的我也知道一百天只会更快&lt;/p&gt;</summary>
    
    
    
    <category term="流水账" scheme="https://stydxm.com/categories/%E6%B5%81%E6%B0%B4%E8%B4%A6/"/>
    
    
  </entry>
  
  <entry>
    <title>开启阿里云的.cn域名隐私保护这么难？</title>
    <link href="https://stydxm.com/2022/12/31/enable-aliyun-privacy-protect/"/>
    <id>https://stydxm.com/2022/12/31/enable-aliyun-privacy-protect/</id>
    <published>2022-12-31T11:57:17.000Z</published>
    <updated>2025-11-25T21:31:43.286Z</updated>
    
    <content type="html"><![CDATA[<h1>收到通知</h1><p>前两天收到阿里云的一份邮件<img src="https://s1.ax1x.com/2022/12/31/pSCFNtg.png" alt=""><br>指向一份公告<a href="https://help.aliyun.com/noticelist/articleid/1061803879.html">1月1日国家顶级域名（.CN、.中国）隐私保护服务收费的公告</a><br><img src="https://s1.ax1x.com/2022/12/31/pSCFUhQ.png" alt=""><br>39一年，虽然有点贵，但为了避免实名上网还是得买 <s>（早知道这还收费就不买cn后缀了）</s></p><h1>寻找产品</h1><p>我还以为很简单，域名隐私保护服务嘛，搜一下就是了。结果找半天，文档里也没有，产品里也没有，站内也搜不到，只能找到一篇2018年的暂停公告<br><img src="https://s1.ax1x.com/2022/12/31/pSCF0cn.png" alt=""></p><h1>给客服打电话</h1><p>客服告诉我在域名控制台-管理-安全设置里可以开启，结果一看并没有，还有一条“温馨提示”，内容就是2018的那个公告<br><img src="https://s1.ax1x.com/2022/12/31/pSCFhcR.png" alt=""></p><h1>再给客服打电话</h1><p>跟客服说了前个客服告诉我的和我看到的情况，结果他略带疑惑地轻声说了句“是已经暂停了呀”，我告诉他我收到的邮件之后他让我稍等（看来客服还没了解这个情况……），然后说要申请，通过短信给我发了<a href="https://survey.aliyun.com/apps/zhiliao/JktcWw9V7">这个链接</a><br><img src="https://s1.ax1x.com/2022/12/31/pSCkkCQ.png" alt=""></p><h1>填表单</h1><p>居然这玩意还是填表单申请，人工手动开通的……<br><img src="https://s1.ax1x.com/2022/12/31/pSCkeuq.png" alt=""><br>1.4不知道能不能完成呢<br><img src="https://s1.ax1x.com/2022/12/31/pSCkmD0.png" alt=""></p><p><strong>2023.1.4 update</strong></p><h1>付钱</h1><p>1.4打开阿里云看到了账单<br><img src="https://s1.ax1x.com/2023/01/04/pSkpoq0.jpg" alt=""><br>居然提示消息还有模板字符串没替换的bug？<br><img src="https://s1.ax1x.com/2023/01/04/pSk9PIO.jpg" alt=""></p><p><strong>2023.1.8 update</strong></p><h1>***，退钱！</h1><p>本来以为完事了，结果收到一封邮件<br><img src="https://s1.ax1x.com/2023/01/08/pSVq9jx.png" alt=""><br>到后台一看，猛然发现有效期就4天<br><img src="https://s1.ax1x.com/2023/01/08/pSVbv4J.png" alt=""><br>说好的39一年呢？？？怎么只有四天？？？<br><img src="https://s1.ax1x.com/2023/01/08/pSVqm8A.png" alt=""><br>客服表示“反馈专员核实”，那就只能先等着了</p><p><strong>2023.2.27 update</strong></p><h1>好像完事了</h1><p>似乎早就给开了，不过没有提醒，我也不知道是什么时候修正的问题</p>]]></content>
    
    
    <summary type="html">2022的最后一天，我竟在折腾这个……</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="域名" scheme="https://stydxm.com/tags/%E5%9F%9F%E5%90%8D/"/>
    
  </entry>
  
  <entry>
    <title>传输巨量数据：卡车拉硬盘，还真不是个梗</title>
    <link href="https://stydxm.com/2022/11/19/migrate-massive-data/"/>
    <id>https://stydxm.com/2022/11/19/migrate-massive-data/</id>
    <published>2022-11-19T05:56:34.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<h1>前言</h1><blockquote><p>Never underestimate the bandwidth of a station wagon full of tapes hurtling down the highway. – <a href="https://en.wikipedia.org/wiki/Andrew_S._Tanenbaum">Andrew S. Tanenbaum</a><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p></blockquote><p>前几天看到一张表情包，大意是可以用卡车转移数据到aws<br>本来以为是玩梗，没想到去搜了一下之后发现aws真的有这样的服务，叫做<a href="https://aws.amazon.com/cn/snowmobile/">AWS Snowmobile</a></p><p><img src="https://s1.ax1x.com/2022/11/19/zKSXUU.jpg" alt=""></p><p>查资料的时候发现已经有人给直接移动储存介质的传输方式取了个名字——Sneakernet，中文叫球鞋网络或者跑腿网络<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>，于是就想写写关于大量数据传输的内容<br>在云计算时代，如果企业要从传统的机房迁移到云服务商，势必要传输大量的数据；当数据达到一定量的时候，从企业自己服务器的硬盘传到云服务商的储存中去也是很困难的事，云服务器为了方便客户迁移，也给出了各种迁移方案<br>查资料的时候也发现aws已经推出了很多sneakernet的服务，所以下面提到云服务商的时候主要以aws为例 <s>我看到snowmobile才想要写这篇的</s></p><h1>网络</h1><p>通过网络传输数据是最常规、最简便的方法，对于绝大多数个人和企业的非业务数据，直接通过网络传输数据方便快捷，必然是首选<br>但是对于数据规模较大的情况，即使不考虑大量数据上传的网络带宽费用，即使以千兆网络的理论速率，每周也只能上传约75T的数据<br>这样的速率对于数据量稍大的情况来说——比如企业的数据库、科研数据等——意味着需要付出很长的时间和不小的流量开支，而且耗时太长</p><h1>运输储存介质</h1><p>当数据量大到网络传输数据的耗时不可接受时，就需要使用sneakernet传输数据<br>以aws为例，他们已经有很多<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>专门用于数据上云的服务了，其中的<a href="https://aws.amazon.com/cn/snow/">Snow系列</a>就是sneakernet的应用</p><h2 id="直接邮寄储存介质">直接邮寄储存介质</h2><blockquote><p>Many years ago, professor Andy Tanenbaum wrote the following:</p><blockquote><p>Never underestimate the bandwidth of a station wagon full of tapes hurtling down the highway.</p></blockquote><p>Since station wagons and tapes are both on the verge of obsolescence, others have updated this nugget of wisdom to reference DVDs and Boeing 747s.<sup class="footnote-ref"><a href="#fn4" id="fnref4">[4]</a></sup></p></blockquote><h3 id="AWS-Import-Export">AWS Import/Export</h3><p>早在2009年3月，阿里云和azure都还不存在的时候，aws就<a href="https://aws.amazon.com/blogs/aws/send-us-that-data/">推出了</a>这个服务<br>客户可以把小于50磅、8U<sup class="footnote-ref"><a href="#fn4" id="fnref4:1">[4:1]</a></sup>的储存设备寄给亚马逊，亚马逊会把里面的文件导入S3<br>不过目前这个服务已经停止了，博客中留的<a href="http://aws.amazon.com/importexport">产品页面</a>现在会被重定向到snowball，大概是因为已经有其他更成熟、更细分的服务可以上云了吧<br><s>当年<a href="http://awsimportexport.s3.amazonaws.com/aws-import-export-calculator.html">用来计算耗时的计算器</a>居然现在还能用</s><br>虽然由于物理接口速度的限制（当时的USB 2.0和eSATA都还不到千兆），每周只能传输大概40-50TB的数据，现在看起来速度并不快，但对比当时的网速这并不慢，不妨是一种成功的尝试</p><h3 id="其他">其他</h3><p>清华也运过有700TB实验数据的磁带<sup class="footnote-ref"><a href="#fn5" id="fnref5">[5]</a></sup></p><h2 id="将数据装载在专用设备中邮寄">将数据装载在专用设备中邮寄</h2><h3 id="AWS-Import-Export-Snowball">AWS Import/Export Snowball</h3><blockquote><p>We launched the first-generation AWS Snowball service way back in 2009. As I wrote at the time, “Hard drives are getting bigger more rapidly than internet connections are getting faster.” I believe that remains the case today. In fact, the rapid rise in Big Data applications, the emergence of global sensor networks, and the “keep it all just in case we can extract more value later” mindset have made the situation even more dire.<sup class="footnote-ref"><a href="#fn6" id="fnref6">[6]</a></sup></p></blockquote><p>2015年10月亚马逊<a href="https://aws.amazon.com/blogs/aws/aws-importexport-snowball-transfer-1-petabyte-per-week-using-amazon-owned-storage-appliances/">推出了</a>Snowball，速度提升到了每周1PB<sup class="footnote-ref"><a href="#fn6" id="fnref6:1">[6:1]</a></sup>，而且有了实体的设备</p><p><img src="https://s1.ax1x.com/2022/11/19/zKJ2qI.png" alt=""></p><p>这个snowball设备就是个箱子，有电源、网口和一块屏幕，我猜测内部是类似普通开发板的架构和一些硬盘，最多可以储存50TB数据<sup class="footnote-ref"><a href="#fn6" id="fnref6:2">[6:2]</a></sup><br>它的使用方法变成了客户在内网里向snowball写入数据，然后寄给亚马逊，亚马逊会进行导入<br>做出这样改变的原因我猜测是这样标准化的设备在亚马逊的数据中心里可以自动化操作，而以前五花八门的设备显然做不到；而且manifest由程序自动生成，方便使用的同时也减少了错误<br>aws另外发布过<a href="https://aws.amazon.com/blogs/aws/aws-snowball-edge-more-storage-local-endpoints-lambda-functions/">Snowball Edge</a>、<a href="https://aws.amazon.com/blogs/aws/introducing-aws-snowcone-small-lightweight-edge-storage-and-processing/">Snowcone</a>这些设备，只是介质和容量有所不同，其他也都大同小异<br><img src="https://s1.ax1x.com/2022/11/19/zKg9p9.png" alt=""><br><img src="https://s1.ax1x.com/2022/11/19/zKgkm6.png" alt=""></p><h3 id="Azure-Data-Box">Azure Data Box</h3><p>azure也有类似服务，叫<a href="https://azure.microsoft.com/zh-cn/products/databox/data/">Azure Data Box</a>，有40TB的Data Box Disk、100TB的Data Box、1PB的Data Box Heavy三种，这里讲讲“标准款”的Data Box</p><p><img src="https://s1.ax1x.com/2022/11/19/zKBrJ1.png" alt=""></p><p>它有一个电口两个光口，共三个万兆接口</p><h3 id="Transfer-Appliance">Transfer Appliance</h3><p>GCP也有这样的设备<a href="https://cloud.google.com/transfer-appliance">Transfer Appliance</a><br>Google只说了有40TB的TA40和300TB的TA300两种型号，但甚至连一张照片都没有发<br><s>属实保密程度高</s></p><h1>上门收货</h1><h2 id="AWS-Snowmobile">AWS Snowmobile</h2><p>这就是我当时看到的那个卡车拉硬盘的服务，aws更是把卡车开到了<a href="https://www.youtube.com/watch?v=8vQmTZTq7nw">发布会</a>上<br><img src="https://s1.ax1x.com/2022/11/19/zKEF4P.png" alt=""><br>确切地说，snowmobile并不包含卡车，而是一个集装箱<br>它可以储存100PB数据，有共计1T的网络带宽，可以在几周内传输EB级的数据<br><s>钱给到位，亚马逊甚至可以给你安排发电机、安排车辆护送、安排安保人员哦</s></p><h1>离谱方法</h1><blockquote><p>这里的仅供图一乐 <s>应该没人真的会这么用吧</s></p></blockquote><h2 id="IPoAC">IPoAC</h2><p>全称IP over Avian Carriers，以鸟类为载体的网际协议，来自<a href="https://www.rfc-editor.org/rfc/rfc1149">rfc 1149</a>，这是一个愚人节rfc</p><p><img src="https://s1.ax1x.com/2022/11/26/ztNHfg.png" alt=""></p><p><a href="https://zh.wikipedia.org/wiki/%E4%BB%A5%E9%B8%9F%E7%B1%BB%E4%B8%BA%E8%BD%BD%E4%BD%93%E7%9A%84%E7%BD%91%E9%99%85%E5%8D%8F%E8%AE%AE#/media/File:Homing_pigeon.jpg">图源</a></p><p>并且还真的有人实践过:2001年四月，挪威卑尔根的一个用户组发了九个“包”</p><p>rfc1149甚至还有修订版，即加上了QoS的<a href="https://www.rfc-editor.org/rfc/rfc2549">rfc2549</a>和支持IPv6的<a href="https://www.rfc-editor.org/rfc/rfc6214">rfc6214</a></p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>Tanenbaum, Andrew S. (1989). <a href="https://archive.org/details/computernetworks02tane/page/57">Computer Networks</a>. New Jersey: Prentice-Hall. p. 57. ISBN 0-13-166836-6. <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://en.wikipedia.org/wiki/Sneakernet">Sneakernet</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://aws.amazon.com/cn/products/migration-and-transfer/">Migration &amp; Transfer on AWS</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li><li id="fn4" class="footnote-item"><p><a href="https://aws.amazon.com/blogs/aws/send-us-that-data/">AWS Import/Export: Ship Us That Disk!</a> <a href="#fnref4" class="footnote-backref">↩︎</a> <a href="#fnref4:1" class="footnote-backref">↩︎</a></p></li><li id="fn5" class="footnote-item"><p><a href="https://tuna.moe/event/2022/lto-practice/">金枪鱼之夜：空运磁带的 PB 级实验数据传输</a> <a href="#fnref5" class="footnote-backref">↩︎</a></p></li><li id="fn6" class="footnote-item"><p><a href="https://aws.amazon.com/blogs/aws/aws-importexport-snowball-transfer-1-petabyte-per-week-using-amazon-owned-storage-appliances/">AWS Import/Export Snowball – Transfer 1 Petabyte Per Week Using Amazon-Owned Storage Appliances</a> <a href="#fnref6" class="footnote-backref">↩︎</a> <a href="#fnref6:1" class="footnote-backref">↩︎</a> <a href="#fnref6:2" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">前几天看到一张表情包，大意是可以用卡车转移数据到aws&lt;br&gt;本来以为是玩梗，没想到去搜了一下之后发现aws真的有这样的服务</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
  </entry>
  
  <entry>
    <title>整活：玩游戏时，在你笑的时候使用AI自动瞄准</title>
    <link href="https://stydxm.com/2022/10/08/smile-aim/"/>
    <id>https://stydxm.com/2022/10/08/smile-aim/</id>
    <published>2022-10-08T12:56:46.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>前两天同学聊天的时候发了个<a href="https://www.bilibili.com/video/BV1Kd4y1i7Nf">视频</a>，还提了个很有趣的思路——在笑的时候自动瞄准<br>一看视频挺有意思的，实现应该也不难，试了一下不到一小时就做完了</p><span id="more"></span><p><img src="https://s1.ax1x.com/2022/10/08/xGowDA.png" alt=""><br><img src="https://s1.ax1x.com/2022/10/08/xGHqxS.png" alt=""></p><h1>思路</h1><ul><li>读取摄像头输入，检测表情</li><li>检测到笑了之后，开始捕捉屏幕画面</li><li>在屏幕画面中识别人形，并读取坐标</li><li>如果能识别到人，就将鼠标移到头的位置</li></ul><h1>实现</h1><h2 id="技术栈">技术栈</h2><h3 id="识别表情">识别表情</h3><p>看视频里的实现方法<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>简单而且效果不差，懒得再去找更好的方案了，就直接拿来用吧</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 调用电脑摄像头进行实时人脸+眼睛+微笑识别，可直接复制粘贴运行</span></span><br><span class="line"><span class="comment"># bilibili视频教程:同济子豪兄</span></span><br><span class="line"><span class="comment"># 2019-5-16</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+<span class="string">&#x27;haarcascade_frontalface_default.xml&#x27;</span>)</span><br><span class="line"></span><br><span class="line">eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+<span class="string">&#x27;haarcascade_eye.xml&#x27;</span>)</span><br><span class="line"></span><br><span class="line">smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades+<span class="string">&#x27;haarcascade_smile.xml&#x27;</span>)</span><br><span class="line"><span class="comment"># 调用摄像头</span></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(<span class="literal">True</span>):</span><br><span class="line">    <span class="comment"># 获取摄像头拍摄到的画面</span></span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    faces = face_cascade.detectMultiScale(frame, <span class="number">1.3</span>, <span class="number">2</span>)</span><br><span class="line">    img = frame</span><br><span class="line">    <span class="keyword">for</span> (x,y,w,h) <span class="keyword">in</span> faces:</span><br><span class="line">    <span class="comment"># 画出人脸框，蓝色，画笔宽度微</span></span><br><span class="line">        img = cv2.rectangle(img,(x,y),(x+w,y+h),(<span class="number">255</span>,<span class="number">0</span>,<span class="number">0</span>),<span class="number">2</span>)</span><br><span class="line">    <span class="comment"># 框选出人脸区域，在人脸区域而不是全图中进行人眼检测，节省计算资源</span></span><br><span class="line">        face_area = img[y:y+h, x:x+w]</span><br><span class="line">        </span><br><span class="line">        <span class="comment">## 人眼检测</span></span><br><span class="line">        <span class="comment"># 用人眼级联分类器引擎在人脸区域进行人眼识别，返回的eyes为眼睛坐标列表</span></span><br><span class="line">        eyes = eye_cascade.detectMultiScale(face_area,<span class="number">1.3</span>,<span class="number">10</span>)</span><br><span class="line">        <span class="keyword">for</span> (ex,ey,ew,eh) <span class="keyword">in</span> eyes:</span><br><span class="line">            <span class="comment">#画出人眼框，绿色，画笔宽度为1</span></span><br><span class="line">            cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(<span class="number">0</span>,<span class="number">255</span>,<span class="number">0</span>),<span class="number">1</span>)</span><br><span class="line">        </span><br><span class="line">        <span class="comment">## 微笑检测</span></span><br><span class="line">        <span class="comment"># 用微笑级联分类器引擎在人脸区域进行人眼识别，返回的eyes为眼睛坐标列表</span></span><br><span class="line">        smiles = smile_cascade.detectMultiScale(face_area,scaleFactor= <span class="number">1.16</span>,minNeighbors=<span class="number">65</span>,minSize=(<span class="number">25</span>, <span class="number">25</span>),flags=cv2.CASCADE_SCALE_IMAGE)</span><br><span class="line">        <span class="keyword">for</span> (ex,ey,ew,eh) <span class="keyword">in</span> smiles:</span><br><span class="line">            <span class="comment">#画出微笑框，红色（BGR色彩体系），画笔宽度为1</span></span><br><span class="line">            cv2.rectangle(face_area,(ex,ey),(ex+ew,ey+eh),(<span class="number">0</span>,<span class="number">0</span>,<span class="number">255</span>),<span class="number">1</span>)</span><br><span class="line">            cv2.putText(img,<span class="string">&#x27;Smile&#x27;</span>,(x,y-<span class="number">7</span>), <span class="number">3</span>, <span class="number">1.2</span>, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>), <span class="number">2</span>, cv2.LINE_AA)</span><br><span class="line">        </span><br><span class="line"><span class="comment"># 实时展示效果画面</span></span><br><span class="line">    cv2.imshow(<span class="string">&#x27;frame2&#x27;</span>,img)</span><br><span class="line">    <span class="comment"># 每5毫秒监听一次键盘动作</span></span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">5</span>) &amp; <span class="number">0xFF</span> == <span class="built_in">ord</span>(<span class="string">&#x27;q&#x27;</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 最后，关闭所有窗口</span></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><h3 id="识别人体和骨骼绑定">识别人体和骨骼绑定</h3><p>暑假里搞了几天<a href="https://mediapipe.dev/">MediaPipe</a>，不敢说有多精通，至少是搞明白怎么用了，所以毫不犹豫地选了<a href="https://google.github.io/mediapipe/solutions/pose">MediaPipe Pose</a></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> mediapipe <span class="keyword">as</span> mp</span><br><span class="line">mp_drawing = mp.solutions.drawing_utils</span><br><span class="line">mp_drawing_styles = mp.solutions.drawing_styles</span><br><span class="line">mp_pose = mp.solutions.pose</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line"><span class="keyword">with</span> mp_pose.Pose(</span><br><span class="line">    min_detection_confidence=<span class="number">0.5</span>,</span><br><span class="line">    min_tracking_confidence=<span class="number">0.5</span>) <span class="keyword">as</span> pose:</span><br><span class="line">  <span class="keyword">while</span> cap.isOpened():</span><br><span class="line">    success, image = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> success:</span><br><span class="line">      <span class="built_in">print</span>(<span class="string">&quot;Ignoring empty camera frame.&quot;</span>)</span><br><span class="line">      <span class="comment"># If loading a video, use &#x27;break&#x27; instead of &#x27;continue&#x27;.</span></span><br><span class="line">      <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># To improve performance, optionally mark the image as not writeable to</span></span><br><span class="line">    <span class="comment"># pass by reference.</span></span><br><span class="line">    image.flags.writeable = <span class="literal">False</span></span><br><span class="line">    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)</span><br><span class="line">    results = pose.process(image)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Draw the pose annotation on the image.</span></span><br><span class="line">    image.flags.writeable = <span class="literal">True</span></span><br><span class="line">    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)</span><br><span class="line">    mp_drawing.draw_landmarks(</span><br><span class="line">        image,</span><br><span class="line">        results.pose_landmarks,</span><br><span class="line">        mp_pose.POSE_CONNECTIONS,</span><br><span class="line">        landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style())</span><br><span class="line">    <span class="comment"># Flip the image horizontally for a selfie-view display.</span></span><br><span class="line">    cv2.imshow(<span class="string">&#x27;MediaPipe Pose&#x27;</span>, cv2.flip(image, <span class="number">1</span>))</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">5</span>) &amp; <span class="number">0xFF</span> == <span class="number">27</span>:</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">cap.release()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="捕获屏幕">捕获屏幕</h3><p>随便Google了一下，找了个叫<a href="https://github.com/BoboTiG/python-mss">mss</a>的库</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> mss <span class="keyword">import</span> mss</span><br><span class="line">sct = mss()</span><br><span class="line">sct_cap = sct.grab(&#123;<span class="string">&quot;top&quot;</span>: <span class="number">0</span>, <span class="string">&quot;left&quot;</span>: <span class="number">0</span>, <span class="string">&quot;width&quot;</span>: <span class="number">1920</span>, <span class="string">&quot;height&quot;</span>: <span class="number">1080</span>&#125;)</span><br></pre></td></tr></table></figure><h3 id="移动鼠标">移动鼠标</h3><p>这个也没去找，视频里的代码一眼看到了<a href="https://github.com/asweigart/pyautogui">PyAutoGUI</a></p><figure class="highlight python"><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="keyword">import</span> pyautogui</span><br><span class="line">pyautogui.moveTo(x, y)</span><br></pre></td></tr></table></figure><h2 id="代码">代码</h2><p>注释写的比较详细，基本每步都有注释</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> mediapipe <span class="keyword">as</span> mp</span><br><span class="line"><span class="keyword">import</span> pyautogui</span><br><span class="line"><span class="keyword">from</span> mss <span class="keyword">import</span> mss</span><br><span class="line"></span><br><span class="line">face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + <span class="string">&#x27;haarcascade_frontalface_default.xml&#x27;</span>)</span><br><span class="line">eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + <span class="string">&#x27;haarcascade_eye.xml&#x27;</span>)</span><br><span class="line">smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + <span class="string">&#x27;haarcascade_smile.xml&#x27;</span>)</span><br><span class="line"><span class="comment"># 调用摄像头</span></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># 获取屏幕</span></span><br><span class="line">sct = mss()</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    <span class="comment"># 获取摄像头拍摄到的画面</span></span><br><span class="line">    ret, img = cap.read()</span><br><span class="line">    <span class="comment"># 获取屏幕图像</span></span><br><span class="line">    sct_cap = np.array(sct.grab(&#123;<span class="string">&quot;top&quot;</span>: <span class="number">0</span>, <span class="string">&quot;left&quot;</span>: <span class="number">0</span>, <span class="string">&quot;width&quot;</span>: <span class="number">1920</span>, <span class="string">&quot;height&quot;</span>: <span class="number">1080</span>&#125;))</span><br><span class="line">    faces = face_cascade.detectMultiScale(img, <span class="number">1.3</span>, <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">for</span> (x, y, w, h) <span class="keyword">in</span> faces:</span><br><span class="line">        <span class="comment"># 画出蓝色人脸框</span></span><br><span class="line">        img = cv2.rectangle(img, (x, y), (x + w, y + h), (<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">        <span class="comment"># 框选出人脸区域，在人脸区域而不是全图中进行人眼检测，节省计算资源</span></span><br><span class="line">        face_area = img[y:y + h, x:x + w]</span><br><span class="line">        <span class="comment"># 用人眼级联分类器引擎在人脸区域进行人眼识别，返回的eyes为眼睛坐标列表</span></span><br><span class="line">        eyes = eye_cascade.detectMultiScale(face_area, <span class="number">1.3</span>, <span class="number">10</span>)</span><br><span class="line">        <span class="keyword">for</span> (ex, ey, ew, eh) <span class="keyword">in</span> eyes:</span><br><span class="line">            <span class="comment"># 画出人眼框，绿色，画笔宽度为1</span></span><br><span class="line">            cv2.rectangle(face_area, (ex, ey), (ex + ew, ey + eh), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">1</span>)</span><br><span class="line">        <span class="comment"># 用微笑级联分类器引擎在人脸区域进行人眼识别，返回的eyes为眼睛坐标列表</span></span><br><span class="line">        smiles = smile_cascade.detectMultiScale(face_area, scaleFactor=<span class="number">1.16</span>, minNeighbors=<span class="number">65</span>, minSize=(<span class="number">25</span>, <span class="number">25</span>),</span><br><span class="line">                                                flags=cv2.CASCADE_SCALE_IMAGE)</span><br><span class="line">        <span class="keyword">for</span> (ex, ey, ew, eh) <span class="keyword">in</span> smiles:</span><br><span class="line">            <span class="comment"># 画出微笑框，红色（BGR色彩体系），画笔宽度为1</span></span><br><span class="line">            cv2.rectangle(face_area, (ex, ey), (ex + ew, ey + eh), (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>), <span class="number">1</span>)</span><br><span class="line">            cv2.putText(img, <span class="string">&#x27;Smile&#x27;</span>, (x, y - <span class="number">7</span>), <span class="number">3</span>, <span class="number">1.2</span>, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>), <span class="number">2</span>, cv2.LINE_AA)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 当摄像头画面中检测到微笑时检测屏幕图像</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">len</span>(smiles) &gt; <span class="number">0</span>:</span><br><span class="line">            <span class="comment"># 检测人体并获取骨骼</span></span><br><span class="line">            pose = mp.solutions.pose.Pose(</span><br><span class="line">                min_detection_confidence=<span class="number">0.5</span>,</span><br><span class="line">                min_tracking_confidence=<span class="number">0.5</span>)</span><br><span class="line">            sct_cap.flags.writeable = <span class="literal">False</span></span><br><span class="line">            sct_cap = cv2.cvtColor(sct_cap, cv2.COLOR_BGR2RGB)</span><br><span class="line">            results = pose.process(sct_cap)</span><br><span class="line">            sct_cap.flags.writeable = <span class="literal">True</span></span><br><span class="line">            sct_cap = cv2.cvtColor(sct_cap, cv2.COLOR_RGB2BGR)</span><br><span class="line">            <span class="comment"># 绘制骨骼</span></span><br><span class="line">            mp.solutions.drawing_utils.draw_landmarks(</span><br><span class="line">                sct_cap,</span><br><span class="line">                results.pose_landmarks,</span><br><span class="line">                mp.solutions.pose.POSE_CONNECTIONS,</span><br><span class="line">                landmark_drawing_spec=mp.solutions.drawing_styles.get_default_pose_landmarks_style())</span><br><span class="line">            <span class="comment"># 当检测到人体时</span></span><br><span class="line">            <span class="keyword">if</span> results.pose_landmarks:</span><br><span class="line">                <span class="comment"># 获取0号地标</span></span><br><span class="line">                <span class="comment"># 地标编号参考：https://mediapipe.dev/images/mobile/pose_tracking_full_body_landmarks.png</span></span><br><span class="line">                landmark = results.pose_landmarks.landmark[<span class="number">0</span>]</span><br><span class="line">                x = sct_cap.shape[<span class="number">1</span>] * landmark.x</span><br><span class="line">                y = sct_cap.shape[<span class="number">0</span>] * landmark.y</span><br><span class="line">                <span class="comment"># 打印坐标</span></span><br><span class="line">                <span class="built_in">print</span>(x, y)</span><br><span class="line">                cv2.circle(sct_cap, (<span class="built_in">int</span>(x), <span class="built_in">int</span>(y)), <span class="number">10</span>, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>), -<span class="number">1</span>)</span><br><span class="line">                <span class="comment"># 将鼠标移到对应坐标</span></span><br><span class="line">                pyautogui.moveTo(x, y)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 实时展示效果画面</span></span><br><span class="line">    cv2.imshow(<span class="string">&#x27;frame2&#x27;</span>, img)</span><br><span class="line">    cv2.imshow(<span class="string">&#x27;screen&#x27;</span>, sct_cap)</span><br><span class="line">    <span class="comment"># 每5毫秒监听一次键盘动作，按Q时退出</span></span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">5</span>) &amp; <span class="number">0xFF</span> == <span class="built_in">ord</span>(<span class="string">&#x27;q&#x27;</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭所有窗口</span></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1>效果</h1><p><img src="https://s1.ax1x.com/2022/10/15/xwOkFK.png" alt=""></p><blockquote><p>用的人脸照片是<a href="https://www.pexels.com/zh-cn/photo/2379004/">Pexels上随便找的一张</a></p></blockquote><p>在游戏里没敢试，因为怕被反作弊识别然后号没了<br>识别表情、识别人体都没啥问题，也能正确地把鼠标移到屏幕里人的头上</p><h2 id="表情">表情</h2><p>找了各种肤色、情绪的笑脸照片，都能正确识别，唯一的问题是图片放到摄像头之后要过几秒钟才能识别出微笑，不过也没什么太大问题了</p><h2 id="人体姿态">人体姿态</h2><p>为了模拟真实情况 <s>谁真去用啊</s> ，不仅试了常规照片，玩过的几个游戏（GTA5，BF5，RDR2等）截图也试了一下，人物靠得近一点就没什么没问题了（太小的识别不到）<br><s>Steve和Alex都识别不了，差评</s></p><h1>展望</h1><p>检测微笑的卷积神经网络性能很好，至少在我的机器上完全观察不到卡顿；但mediapipe的pose模型检测1080P的画面时，在我的5800H上目测最多只有十几帧<br>mediapipe的模型是跑在CPU上的，我电脑上还有一块3070 mobile，所以我的想法是使用GPU加速，虽然文档里有GPU加速相关的内容<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>，但是写的是面对Linux的，可惜大多数游戏在Linux上无法运行或体验很不好<br>并且，mediapipe的模型是TF lite的，而TF lite主要面向移动设备，并没有计划开发windows平台的GPU加速<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>，所以未来如果要解决这个问题，我的想法是使用性能更好的模型或者有较完善GPU加速支持的模型</p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p><a href="https://github.com/TommyZihao/zihaoopencv/blob/master/%E7%AC%AC%E5%8D%81%E7%AB%A0%EF%BC%9A%E7%89%A9%E4%BD%93%E6%A3%80%E6%B5%8B%E4%B8%8E%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB%E3%80%90%E5%AD%90%E8%B1%AA%E5%85%84opencv-python%E6%95%99%E7%A8%8B%E3%80%91.md">第十章：物体检测与人脸识别【子豪兄opencv-python教程】</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li><li id="fn2" class="footnote-item"><p><a href="https://google.github.io/mediapipe/getting_started/gpu_support.html">GPU support</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p></li><li id="fn3" class="footnote-item"><p><a href="https://github.com/tensorflow/tensorflow/issues/40325#issuecomment-642143623">tensorflow contributor的comment</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;p&gt;前两天同学聊天的时候发了个&lt;a href=&quot;https://www.bilibili.com/video/BV1Kd4y1i7Nf&quot;&gt;视频&lt;/a&gt;，还提了个很有趣的思路——在笑的时候自动瞄准&lt;br&gt;
一看视频挺有意思的，实现应该也不难，试了一下不到一小时就做完了&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="AI" scheme="https://stydxm.com/tags/AI/"/>
    
  </entry>
  
  <entry>
    <title>给Hexo添加脚注支持</title>
    <link href="https://stydxm.com/2022/09/23/hexo-footnote/"/>
    <id>https://stydxm.com/2022/09/23/hexo-footnote/</id>
    <published>2022-09-23T16:32:22.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>hexo有现成的不错的方案<a href="https://github.com/markdown-it/markdown-it-footnote"><code>markdown-it-footnote</code></a>可用，所以也不用怎么折腾，但是有几个坑</p><span id="more"></span><p>markdown-it-footnote的<a href="https://www.npmjs.com/package/markdown-it-footnote">README</a>写着</p><blockquote><h2 id="Install">Install</h2><p>node.js, browser:</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">npm install markdown-it-footnote --save</span><br><span class="line">bower install markdown-it-footnote --save</span><br></pre></td></tr></table></figure></blockquote><p>但并不是装上这个库就能用脚注了 <s>（踩坑了）</s></p><p>首先<a href="https://github.com/markdown-it/markdown-it-footnote"><code>markdown-it-footnote</code></a>是<a href="hhttps://markdown-it.github.io/">Markdown it</a>的一个插件，所以先把hexo默认的<a href="https://github.com/hexojs/hexo-renderer-marked"><code>hexo-renderer-marked</code></a>渲染引擎替换成<a href="https://github.com/hexojs/hexo-renderer-markdown-it"><code>hexo-renderer-markdown-it</code></a></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">npm un hexo-renderer-marked --save</span><br><span class="line">npm i hexo-renderer-markdown-it --save</span><br></pre></td></tr></table></figure><p>这一步挺常规的，然后就是插件需要手动启用 <s>没仔细看readme</s><br>在配置文件<code>_config.yml</code>里加入<code>markdown</code>这个字段，并启用脚注插件</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">markdown:</span></span><br><span class="line">  <span class="attr">plugins:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">markdown-it-footnote</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;hexo有现成的不错的方案&lt;a href=&quot;https://github.com/markdown-it/markdown-it-footnote&quot;&gt;&lt;code&gt;markdown-it-footnote&lt;/code&gt;&lt;/a&gt;可用，所以也不用怎么折腾，但是有几个坑&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="博客" scheme="https://stydxm.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>从iPhone14看手机创新的匮乏</title>
    <link href="https://stydxm.com/2022/09/10/iphone14/"/>
    <id>https://stydxm.com/2022/09/10/iphone14/</id>
    <published>2022-09-10T12:08:44.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<p>刚发布的iPhone14系列看起来跟上一代几乎没有变化——除了刘海变成了长的已经不像药丸的药丸<br>因为这个药丸，iOS就做了“灵动岛”，在UI设计方面做了点创新<br><s>然而还是掩盖不了屏幕一个巨大挖孔的事实，甚至还强行把两个孔连起来了</s></p><span id="more"></span><p>于是，就有了罗老师（网传）发的这段话<br><img src="https://s1.ax1x.com/2022/09/11/vO8qyV.jpg" alt=""><br>诚然，这个UI设计确实做得很优秀，但也反映了手机创新的匮乏——一年的代差，区别居然只是UI上的动画。再往前看，iPhone X之后也没有什么没大的进步，只是收窄刘海和加摄像头。然后顺着这个想法来理一理近年来手机领域的 <strong>《创新》</strong></p><h1>先说结论</h1><p>我认为这十多年来，自从智能手机正面屏幕、侧面按键、反面摄像头的格局定下来之后，手机形态上唯三有点创新而且能被主流市场认可的就是全面屏、折叠屏和屏下指纹了</p><blockquote><p>当然并不是说没有进步，处理器的算力不断提升，摄像头越堆越多，全面屏出现后屏占比的百分之几的提升，内存、储存甚至一路提到18+1t，现在主流游戏本大多还是16+512</p></blockquote><h1>创新的设计难成主流</h1><p>手机的设计并非真的一成不变，不同手机间的差异化也不止在纸面的数据上体现<br>手机销量最高的几个品牌故步自封，精力一直花在卷参数和做营销上；索尼、LG、谷歌、微软这些不差钱也不把手机当主业的厂商反而能做出一些让人眼前一亮的新设计，但不能</p><h2 id="升降摄像头——昙花一现的妥协方案">升降摄像头——昙花一现的妥协方案</h2><p>升降摄像头里最常见的是机械升降一个摄像头模组，太多了就不举例了，那个年代的新机一眼望去全都是这样的，其中有个特例是<a href="https://www.oppo.com/en/smartphones/series-reno/reno/">OPPO Reno</a>的扇形摄像头模组<br><img src="https://s1.ax1x.com/2022/09/11/vOWNlt.png" alt=""><br>升降摄像头还有一种方案是<s>略显复古的</s>滑盖形态，保证完整屏幕的同时减少了机身内的机械结构（手动的所以不需要电机了），减少了厚度和重量，腾出更多空间<br>典例：<a href="https://www.hihonor.com/ca/blog/honor-magic-2-first-looks-of-the-upcoming-honor-phone/">荣耀magic2</a>、<a href="https://www.oppo.com/en/smartphones/series-find-x/find-x/">OPPO find x</a><br><img src="https://s1.ax1x.com/2022/09/11/vOfP1I.png" alt=""></p><h2 id="正反双屏——牺牲成本的妥协方案">正反双屏——牺牲成本的妥协方案</h2><p>像nubia x, <a href="https://www.vivo.com/vivo/nexdualdisplay/">vivo NEX 双屏版</a>这样的正反面双屏在我看来这是为了正面完整屏幕砍掉前置摄像头后，对于自拍的解决方案<br>少了前置摄像头，加了一块屏幕，耗电和成本上升，而且仍然是一个妥协的方案，除非<s>手机烫手</s>翻来翻去看<br><img src="https://s1.ax1x.com/2022/09/11/vOhmqK.png" alt=""></p><h2 id="正面多屏——难成主流的效率设计">正面多屏——难成主流的效率设计</h2><p>像<a href="https://www.lg.com/global/lgwing">LG Wing</a>、LG的副屏手机壳、<a href="https://www.microsoft.com/en-us/surface/devices/surface-duo#overview">Surface Duo</a>这样的单面多屏的形态在多任务处理上有着显著的优势，但多出来的结构会导致续航差、机身厚重这些问题，而且也没有手机大厂尝试这样的设计，也一直没能得到主流消费市场的认可<br><img src="https://s1.ax1x.com/2022/09/11/vO4yfH.png" alt=""></p><h2 id="未量产的方案">未量产的方案</h2><p>还有诸如<a href="https://www.theverge.com/2022/7/12/23205814/lg-rollable-phone-video-leak">LG Rollable</a>卷轴屏、<a href="https://www.mi.com/mixalpha">MIX Alpha</a>的环绕屏等的方案，最终都没能实现量产开售，所以说目前谈论它们还为时尚早，只能说让我们一起期望手机厂商能做出真正 <strong>有意思</strong> 的设计吧</p><h1>厂商们不再热衷于真正的创新</h1><p>从我开始关注数码圈开始，这几年来手机届所谓的“革新“实则却是营销，内存从3G、4G一路来到了18G，摄像头像素从1200w到了108m，无数所谓”黑科技“实则是根本没用或科技以换皮为本的概念被创造出来<br>手机市场早就从增量变为存量，手机的差异越来越少，外观也就是在背板和摄像头排布上做文章，翻了好几倍的纸面参数在实际体验中究竟带来了多大的提升呢？恐怕不大<br>灵动岛是一次有意思的尝试，是对挖孔屏交互方式的一种探索，但效果显然不如直接做成无孔的。这是创新吗？当然是；这创新有意义吗？不如研究研究怎么解决屏幕上的孔，而不是花时间研究怎么把孔做好看<br>苹果这么做的“成效”也显而易见。苹果在产品设计上的变化本就自带话题属性（从<a href="https://trends.google.com/trends/explore?q=dynamic%20island">google trends</a>上看得出来），从刘海到摄像头突起，从取消3.5mm耳机孔到为了所谓环保而不再赠送充电头，这些变化都少不了用户的批评，很多主流手机厂商还是照样跟进。苹果这么做顺便还做了免费宣传——但凡关注数码科技领域，谁又不知道灵动岛呢？<br><img src="https://s1.ax1x.com/2022/09/23/xAPzY4.png" alt=""></p><h1>用户需要真正的创新</h1><p>更高的参数固然好，结果带来的是软件优化越来越差，对性能要求越来越高（比如某国际游戏大厂的两款手机3A大作），用户为了其实大可不必的性能消耗而不得不购买更好的硬件，但真的是用户想看到的吗？<br>至少对我来说，与其花费人力物力研发被软件浪费掉的性能，我更想买到体验上有提升的设计、有意义的创新。我不是说更好的性能更高的参数没有意义，而是说应该把这些研发成本投入到更有意义的东西的研发上去</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;刚发布的iPhone14系列看起来跟上一代几乎没有变化——除了刘海变成了长的已经不像药丸的药丸&lt;br&gt;
因为这个药丸，iOS就做了“灵动岛”，在UI设计方面做了点创新&lt;br&gt;
&lt;s&gt;然而还是掩盖不了屏幕一个巨大挖孔的事实，甚至还强行把两个孔连起来了&lt;/s&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="数码" scheme="https://stydxm.com/categories/%E6%95%B0%E7%A0%81/"/>
    
    <category term="随想" scheme="https://stydxm.com/categories/%E9%9A%8F%E6%83%B3/"/>
    
    
    <category term="手机" scheme="https://stydxm.com/tags/%E6%89%8B%E6%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>LG wing：未能展开的未来</title>
    <link href="https://stydxm.com/2022/08/05/lg-wing/"/>
    <id>https://stydxm.com/2022/08/05/lg-wing/</id>
    <published>2022-08-05T06:01:35.000Z</published>
    <updated>2025-11-25T21:31:43.287Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>图片较多且都未经压缩，可能加载会有点慢</p></blockquote><p>2020年9月，发布会结束后，LG wing的<a href="https://www.lg.com/global/lgwing">图片</a>着实让我惊讶，但在当时它和我没什么关系，售价1049美元且没有国行，而且LG手机一贯的垃圾佬特质 <s>过一年就能海鲜市场半价捡</s> 让我根本没有买它的想法</p><p>不想不到一年时间，卖了26年手机的LG宣布由于亏损而放弃了手机业务，也让这个形态独特的手机成了LG最后一款公开发售的手机 <s>当然海鲜市场价格又低了</s></p><p>发布后还不到两年，即使是成色完美的机器在小黄鱼上已经不到1.5k了，自己又想换手机了，就花1.7k买了个成色完美、<code>号称原装未拆机</code>的LG wing。至于为啥这么贵，因为当主力机用，而且用的电信卡，买的是8+256的att版的</p><span id="more"></span><p>（LG wing有多个版本，对大陆地区的网络支持不同，二手价格也不一样。韩版的不支持三大运营商，也就是只能用WiFi上网；T版，也就是T-mobile版，只能支持移动联通的流量和电话、短信功能；V版，也就是Verizon的，支持三家的流量，但是要手动切换电信的4G和2G，据说移动联通5G也可以；att版，也就是AT&amp;T版，支持三网5G和2G <sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> ，但是我测试发现似乎不太行，应该是不能完全支持电信5G的频段，所以有人说行有人说不行，我也不太在意能不能用5G，就没再折腾）</p><h1>外观和手感</h1><p>手机到手给人的第一印象当然是外观和手感</p><p>手机外观我也不太在意，正面几乎全是屏，后面是玻璃后盖+三摄，觉得很漂亮了，看渲染图好像蓝色版本的更漂亮一点，不过反正戴个壳都一样</p><p><img src="https://s1.ax1x.com/2022/08/05/vnewp4.jpg" alt=""></p><p>手感的话前后的曲面握起来还是比较舒服的，厚有点厚，超过1cm了，但可以捏着边框所以影响也不大</p><p>机器确实比较重，260g的质量已经接近主流折叠屏（左右翻折的那种打开较大的）的手机了，这是在买之前就了解清楚了的。到手之后还是比想象中重一点，反正对我这样三四斤的单反+镜头一拿一整天的人来说也没什么，用了两三天也没有手酸手臂痛 <s>现在拿别的手机已经感觉轻飘飘了</s></p><h1>屏幕</h1><p>这个手机最特殊的就是那块可以旋转的主屏了，电脑一直用着多屏，手机只有一块屏幕居然不太习惯，现在终于可以一边看QQ一边看B站了</p><p><img src="https://s1.ax1x.com/2022/08/05/vne01J.jpg" alt=""></p><p>这块6.8寸的主屏是1080P60的，很多人为既没有2K也没有高刷而感到遗憾，不知道为什么我对这个并没啥感觉，反而是被曲面屏的素质惊艳到了，可惜相机并不能拍出来</p><p>而且这块屏亮度也很高，在强烈的太阳光下清晰可读，晚上也没觉得OLED有多瞎眼 <s>不过万一晚上没拿住掉下来能砸死倒是真的</s></p><p><img src="https://s1.ax1x.com/2022/08/05/vneahF.jpg" alt=""><br><img src="https://s1.ax1x.com/2022/08/05/vneUtU.jpg" alt=""></p><p>这个屏幕尺寸也让机身长度来到了16.95cm，我的手掌根到中指尖刚好也差不多，再加上机身超过半斤，单手操作再也不可能了</p><p>副屏的下巴有点大，但其实真用起来也没啥影响</p><p>有待验证的一点是两块屏幕似乎有一定的色差，不过我只见过一次，然后就没观察到过这个现象，可能是由于调节亮度或者色温的时候系统没有同步，改成自动亮度之后就好了</p><p>屏幕问题的话可能不是屏幕本身的问题（什么绕口令），而是是屏幕的转轴，当然我也不清楚问题到底是设计问题导致本身就这样，还是因为不耐用转多了导致的，还是因为贩子拆机后装回去的水平不行导致的</p><p>转轴似乎有一些突起，导致转轴上下两侧不平，而且主屏和机身中间的缝比较大，如果不是端坐着的话可能因为重力作用屏幕离机身有一段距离，点屏幕的时候会按回去，轻微碰撞发出声音，在用输入法的时候尤其明显，关了声音和振动还有哒哒哒的声音 <s>机械软键盘了属于是</s></p><h1>相机</h1><p>相机没什么好说的，真要点拍什么我会拿相机，随手拍点记录生活的肯定够了</p><p>直男也用不着前摄，本来还用来人脸识别，换升降摄像头的手机之后连解锁都不用了，就彻底用不着前摄了，估计也就实名认证的时候用一下吧</p><p>只是我不太理解为啥要两个超广角而不是把其中一个换成长焦</p><p>刚好今天天上的积云挺漂亮的，就顺手拍两张<br><img src="https://s1.ax1x.com/2022/08/05/vnCxXT.jpg" alt="没开到最高像素的主摄"><br><img src="https://s1.ax1x.com/2022/08/05/vneNkT.jpg" alt="广角"></p><p>要是有空还能想起来的话出去再拍点</p><p>展开主屏之后的稳定器模式很有创意，可惜硬件上用的是一颗1300w的成像质量很差的超广角摄像头而不是真正的云台，即使在光照充足的条件下，果冻效应和满屏噪点也让画面变得非常不可用，使不错的软件设计无法发挥出来</p><p><img src="https://s1.ax1x.com/2022/08/05/vnC2tA.png" alt=""></p><p><strong>2022.8.11update</strong> 出去玩顺手拍了点，应该能看出来哪些是主摄哪些是广角</p><p><img src="https://s1.ax1x.com/2022/08/12/vG7BE4.jpg" alt=""><br><img src="https://s1.ax1x.com/2022/08/12/vG7DUJ.jpg" alt=""><br><img src="https://s1.ax1x.com/2022/08/12/vG7yCR.jpg" alt=""><br><img src="https://s1.ax1x.com/2022/08/12/vG7r59.jpg" alt=""><br><img src="https://s1.ax1x.com/2022/08/12/vG7wbF.jpg" alt=""></p><h1>其他性能</h1><p>这台机器为了旋转的主屏妥协了很多，比如上千美元的售价居然只用了765G，比如如此厚重的机身只有4000mAh的电池</p><p>但是作为一台2020年发布的旗舰机，除了处理器的遗憾之外该有的也都有了，玻璃后盖、无打孔的OLED曲面屏、屏幕指纹、无线充电、三摄和<s>方向错了的</s>4800w像素的主摄，甚至这个形态都能做到IP56的防尘防水</p><p>765G处理器性能不如855 865，在两块屏开不同应用的时候有的时候确实会卡。还好我已经多年不玩手游了，玩游戏都是用电脑，目前的性能还够用</p><p>4000mAh的电池加上两块屏确实不太耐用，亮屏时间也就四五个小时，对目前的我来说没什么问题，或许一年之后上大学就该考虑换了</p><h1>最后</h1><p>买之前看过几篇评测文章和几个视频，都是把它当备用机甚至玩具来玩，但我还是决定还是作为主力机用，至少目前来说我还没有后悔</p><p>作为一台满是机械结构的手机，机身厚重我能接受，但是有点担心摄像头模组和转轴的寿命问题，尤其是这独特的转轴结构全球仅此一款，还是来自一个已经不做手机的厂商；玻璃后盖和两块屏容易在意外中受损，用的时候还是要小心一点</p><p>还有一点跟机器本身没啥关系，海鲜市场的水挺深的，我这台卖家说全原未拆机，实际成色完美得不像二手，一点磕碰划痕都没有，但是边框上似乎有补过漆，所以存疑，查了下没有被硬解过，就没再深究了；挑的时候甚至还看到声称是全新一手仅拆封的，我就更不敢买了</p><p>再用一段时间之后如果有新的体会就再来更新吧</p><p><img src="https://s1.ax1x.com/2022/08/05/vn9e8x.png" alt=""><br><img src="https://s1.ax1x.com/2022/08/05/vnpWEd.png" alt=""></p><hr class="footnotes-sep"><section class="footnotes"><ol class="footnotes-list"><li id="fn1" class="footnote-item"><p>需要进行一些<a href="https://www.coolapk.com/feed/28658563?shareKey=OGJlNTlhNDMxMzY5NjJlNDA4ZmQ">修改</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p></li></ol></section>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;图片较多且都未经压缩，可能加载会有点慢&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2020年9月，发布会结束后，LG wing的&lt;a href=&quot;https://www.lg.com/global/lgwing&quot;&gt;图片&lt;/a&gt;着实让我惊讶，但在当时它和我没什么关系，售价1049美元且没有国行，而且LG手机一贯的垃圾佬特质 &lt;s&gt;过一年就能海鲜市场半价捡&lt;/s&gt; 让我根本没有买它的想法&lt;/p&gt;
&lt;p&gt;不想不到一年时间，卖了26年手机的LG宣布由于亏损而放弃了手机业务，也让这个形态独特的手机成了LG最后一款公开发售的手机 &lt;s&gt;当然海鲜市场价格又低了&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;发布后还不到两年，即使是成色完美的机器在小黄鱼上已经不到1.5k了，自己又想换手机了，就花1.7k买了个成色完美、&lt;code&gt;号称原装未拆机&lt;/code&gt;的LG wing。至于为啥这么贵，因为当主力机用，而且用的电信卡，买的是8+256的att版的&lt;/p&gt;</summary>
    
    
    
    <category term="数码" scheme="https://stydxm.com/categories/%E6%95%B0%E7%A0%81/"/>
    
    
    <category term="手机" scheme="https://stydxm.com/tags/%E6%89%8B%E6%9C%BA/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://stydxm.com/2022/07/25/hello-world/"/>
    <id>https://stydxm.com/2022/07/25/hello-world/</id>
    <published>2022-07-25T12:27:11.000Z</published>
    <updated>2022-07-26T11:31:20.000Z</updated>
    
    <content type="html"><![CDATA[<p>这段时间研究一些静态页面托管服务，于是也想着给自己搭一个博客。</p><span id="more"></span><p>有了想法之后，花五分钟买了个域名<a href="//stydxm.com">stydxm.com</a>，花五分钟搭了个<a href="https://hexo.io">hexo</a>，再花十分钟把博客放到了<a href="https://pages.dev">Cloudflare Pages</a>上去，然后……花了好久好久挑了个主题<a href="https://github.com/ppoffice/hexo-theme-icarus">Icarus</a>和改域名的配置。总共花了不到一个小时把博客搭起来了，接下来就是把以前在各种地方写的一些东西搬到这里来（这也是有的文章时间比这篇还早的原因）<br>关于博客页面的更新也会写在这篇文章里</p><h2 id="update">update</h2><p>2022.7.26 把这篇文章的封面换成了必应的每日图片，API是<a href="https://bing.ioliu.cn/v1">ioliu</a>的</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;这段时间研究一些静态页面托管服务，于是也想着给自己搭一个博客。&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="博客" scheme="https://stydxm.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
  <entry>
    <title>1.18：无周目老服务器的存档之困</title>
    <link href="https://stydxm.com/2022/07/07/minecraft-server-terrain-update/"/>
    <id>https://stydxm.com/2022/07/07/minecraft-server-terrain-update/</id>
    <published>2022-07-07T13:10:28.000Z</published>
    <updated>2022-08-21T16:49:34.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>本文是我做的<a href="https://www.bilibili.com/video/BV1xU4y1m7ZC">这期视频</a>的文稿，删除服务器相关的内容之后放到博客上来了</p></blockquote><span id="more"></span><p>北京时间2020.10.4，在Minecraft Live 2020上，MOJANG宣布了下一次更新的主题：洞穴与山崖。<br><img src="https://s1.ax1x.com/2022/08/22/vy7DoQ.png" alt=""><br>2020.11.4，1.17的第一个快照20w45a发布；2021.6.8，1.17的第一个正式版发布；<br><img src="https://s1.ax1x.com/2022/08/22/vy7dL8.jpg" alt=""><br>2021.7.13，1.18的第一个快照1.18-experimental snapshot 1发布；2021.11.30,1.18的第一个正式版发布。<br><img src="https://s1.ax1x.com/2022/08/22/vy7yJs.png" alt=""><br>洞穴与山崖更新，地形生成改变之后，更高的高度上限，更漂亮的山峰、洞穴，美西螈、荧光鱿鱼，毫无疑问会带来全新的体验。<br><img src="https://s1.ax1x.com/2022/08/22/vy70eS.png" alt=""><br>但是，对于一些开服已经很久的老服务器来说，1.18虽然是时代的潮流，服务器的升级却变得越来越困难。从低版本一点点升级的过程带来了沉重的历史包袱。毫无疑问，这些服务器必然会在将来的某一天更新到1.18，但是这其中有些事情，可能没那么容易。<br><img src="https://s1.ax1x.com/2022/08/22/vy7sij.png" alt=""><br>最简单的方案当然是直接升级，已经探索过的区块下方会被拓展，新的地形要到更远的地方才能见到。这种方案对于存档是伤害最小也最方便的，但是效果却不佳：明明更新了，但新的内容要到几万格之外才能找到，相当于更新了寂寞。<br>类似的想法还有部分升级，比如把没什么问题的空岛、大厅升级，而生存不升级。对空岛来说，新地形没什么用，因为根本看不到，而对于生存区来说还是更新了寂寞。<br><img src="https://s1.ax1x.com/2022/08/22/vy76Wn.png" alt=""><br>另一个选择是开新区，现有的生存停留在1.16，新开一个1.18的生存区。这样确实是既保留了存档，又避开了老存档升级这个棘手的问题。但如果这样的话，会消耗大量服务器资源，而且如果开了新区，同是生存区，背包得要互通吧，那把新区的东西带到老区来怎么办呢？本来生存区人就不多，开了新区之后现有的生存区占用了大量资源，玩的人却更少了，也把生存区的玩家割裂开了<br><img src="https://s1.ax1x.com/2022/08/22/vy7czq.png" alt=""><br>最后我觉得最好的方案就是删部分区块，这个其实早就有过设想，因为很多被探索过但没有改变过的区块并没有意义，但占了不少的储存空间，我们早就想找到一个办法删除这些区块，但似乎并没有非常精准的方案，最多也就是人工看一下，这样工作量极大而且靠人看很有可能会遗漏一些地方导致误删区块，因此可行性并不高<br>总之，更高的版本改变了地形生成机制，使我们这样一个坚持不换周目不删档的服务器升级过程遇到了很大的麻烦。我看到的其他服务器解决方案大多是删档换周目，这样的方式虽然最简单易行，还能从根本上解决问题，但与我们的理念（注：该服务器的理念是无周目）背道而驰，因此需要理念相似、愿意合作的服务器共同探索出解决方案。</p>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;本文是我做的&lt;a href=&quot;https://www.bilibili.com/video/BV1xU4y1m7ZC&quot;&gt;这期视频&lt;/a&gt;的文稿，删除服务器相关的内容之后放到博客上来了&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="随想" scheme="https://stydxm.com/categories/%E9%9A%8F%E6%83%B3/"/>
    
    
    <category term="Minecraft" scheme="https://stydxm.com/tags/Minecraft/"/>
    
  </entry>
  
  <entry>
    <title>GIF之后，再无动态图片</title>
    <link href="https://stydxm.com/2022/03/26/dynamic-images/"/>
    <id>https://stydxm.com/2022/03/26/dynamic-images/</id>
    <published>2022-03-26T05:15:00.000Z</published>
    <updated>2022-07-26T06:28:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>24号看到GIF发明者去世的消息[2]，觉得GIF早已完成了它的历史使命。</p><p>但今天打开QQ空间，看到GIF的梗图，才意识到GIF仍然在被大量使用。</p><span id="more"></span><p>然后仔细一想，虽然GIF的分辨率低、帧率低、只有256色，虽然现在的web开发早已没有满屏闪烁的gif，没有雪碧图，虽然像谷歌苹果这样的巨头和各个开源组织都在不遗余力地推行更快或者更高压缩率的图形编码格式，比如webp和svg……但是，到现在除了gif，我们仍然没有第二种足够通行并且较轻量的动图格式，或许在将来较长时间里，也不会有。我们要么继续用gif，要么就只能用需要播放器的视频了。</p><p><strong>2022.7.26 update</strong></p><blockquote><p>这篇文章原本发在B站动态和QQ空间里，今天搬到博客上的时候做了一点小修改，顺便查了一下除gif外现有的动态图片格式，然后发现了犯了个错误——Google的<a href="https://developers.google.com/speed/webp">WebP</a>其实是支持动图的，只是webp的动图有多少，恐怕不好说。</p></blockquote><p>支持动图的格式：</p><h2 id="APNG-Animated-Portable-Network-Graphics"><a href="https://wiki.mozilla.org/APNG_Specification">APNG</a> - Animated Portable Network Graphics</h2><blockquote><p>APNG is an extension of the PNG format, adding support for animated images. It is intended to be a replacement for simple animated images that have traditionally used the GIF format, while adding support for 24-bit images and 8-bit transparency. APNG is a simpler alternative to MNG, providing a spec suitable for the most common usage of animated images on the Internet.</p></blockquote><p>拓展自PNG，支持24bit位深度和8bit的透明图像</p><p>兼容PNG，第一帧为普通PNG，所以也可以作为普通PNG来解析</p><h2 id="WebP"><a href="https://developers.google.com/speed/webp">WebP</a></h2><blockquote><p>The 14-bit dynamics for image size limit the maximum size of a WebP lossless image to 16384✕16384 pixels.</p></blockquote><p><a href="https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification?hl=en">官网上</a>说支持最高14bit，16K分辨率的动态图片</p><h2 id="AVIF-AV1-Image-File-Format"><a href="https://aomediacodec.github.io/av1-avif/">AVIF</a> - AV1 Image File Format</h2><p>基于AV1的图片格式，支持动图</p><p>支持多种色彩空间，多种压缩率，多种位深度</p><h2 id="MNG-Multiple-image-Network-Graphics"><a href="http://www.libpng.org/pub/mng/">MNG</a> - Multiple-image Network Graphics</h2><p>和APNG一样也拓展自PNG，而且得到了PNG的官方承认，但似乎目前兼容性还不如APNG</p><p>[1]: 封面图片：<a href="https://www.washingtonpost.com/obituaries/2022/03/24/gif-creator-stephen-wilhite-dead/">Computer programmer Stephen Wilhite accepts his Webby Lifetime Achievement Award in 2013. (Webby Awards/AP)</a><br>[2]: <a href="https://www.washingtonpost.com/obituaries/2022/03/24/gif-creator-stephen-wilhite-dead/">Stephen Wilhite, computer programmer who created the GIF, dies at 74</a><br>[3]: <a href="https://www.w3.org/Graphics/GIF/spec-gif87.txt">Graphics Interchange Format</a></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;24号看到GIF发明者去世的消息[2]，觉得GIF早已完成了它的历史使命。&lt;/p&gt;
&lt;p&gt;但今天打开QQ空间，看到GIF的梗图，才意识到GIF仍然在被大量使用。&lt;/p&gt;</summary>
    
    
    
    <category term="技术" scheme="https://stydxm.com/categories/%E6%8A%80%E6%9C%AF/"/>
    
    <category term="随想" scheme="https://stydxm.com/categories/%E9%9A%8F%E6%83%B3/"/>
    
    
  </entry>
  
</feed>
