<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=Detour</id>
	<title>Detour - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=Detour"/>
	<link rel="alternate" type="text/html" href="https://wiki.lessokaji.com/index.php?title=Detour&amp;action=history"/>
	<updated>2026-04-15T17:42:06Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>https://wiki.lessokaji.com/index.php?title=Detour&amp;diff=989&amp;oldid=prev</id>
		<title>Maintenance script：​add</title>
		<link rel="alternate" type="text/html" href="https://wiki.lessokaji.com/index.php?title=Detour&amp;diff=989&amp;oldid=prev"/>
		<updated>2026-04-11T08:32:19Z</updated>

		<summary type="html">&lt;p&gt;add&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= DetourLite独立插件开发 =&lt;br /&gt;
&lt;br /&gt;
== 1. 前言 ==&lt;br /&gt;
Detour仓库里的 `Standalone` 目录给出了一个可直接复用的扩展方式：把扩展代码单独编译成 `*Plugin.dll`，部署到 DetourLite 的 `plugins` 目录，在不改动 `DetourCore` / `DetourDance` 主工程的前提下扩展算法核。&lt;br /&gt;
&lt;br /&gt;
这种 Standalone Plugin 适合以下场景：&lt;br /&gt;
&lt;br /&gt;
* 增加自定义 HTTP API。&lt;br /&gt;
* 对接 ROS2、原生 C/C++ 动态库、UDP/TCP 传感器。&lt;br /&gt;
* 新增自定义 2D / 3D 传感器组件类型。&lt;br /&gt;
* 在启动时执行一次性初始化逻辑。&lt;br /&gt;
&lt;br /&gt;
仓库中的参考工程：&lt;br /&gt;
&lt;br /&gt;
* `Standalone/DemoPlugin`&lt;br /&gt;
* `Standalone/StandaloneSensors`&lt;br /&gt;
* `Standalone/DllImportSensor`&lt;br /&gt;
&lt;br /&gt;
== 2. 加载机制 ==&lt;br /&gt;
DetourLite 的插件加载逻辑在 `DetourLite/DetourLiteMain.cs` 中，规则很直接：&lt;br /&gt;
&lt;br /&gt;
* 启动时扫描当前目录下 `plugins` 文件夹以及所有子目录。&lt;br /&gt;
* 所有文件名匹配 `*Plugin.dll` 的程序集都会被 `Assembly.LoadFrom(...)` 加载。&lt;br /&gt;
* 如果程序集里导出了一个名为 `Plugin` 的类，并且该类里存在 `static void Main()`，则该方法会在 `DetourLib.Init()` 之后被调用。&lt;br /&gt;
&lt;br /&gt;
因此有两个层次的能力：&lt;br /&gt;
&lt;br /&gt;
* 只要 DLL 名称以 `Plugin.dll` 结尾，程序集就会被加载。&lt;br /&gt;
* 如果还提供 `Plugin.Main()`，就可以执行启动初始化逻辑。&lt;br /&gt;
&lt;br /&gt;
这意味着：&lt;br /&gt;
&lt;br /&gt;
* '''纯组件型插件''' 可以只提供导出的组件类，不一定非要写 `Plugin.Main()`。&lt;br /&gt;
* '''启动型插件''' 则应提供 `Plugin.Main()`，在里面注册 HTTP 路由、启动线程、初始化第三方库等。&lt;br /&gt;
&lt;br /&gt;
== 3. 组件是如何注册进 Detour 的 ==&lt;br /&gt;
DetourCore 使用 `DetourCore/Misc/JSONLoader.cs` 从当前 AppDomain 中所有已加载程序集的导出类型里查找组件类型。&lt;br /&gt;
&lt;br /&gt;
所以自定义组件要满足下面几个条件：&lt;br /&gt;
&lt;br /&gt;
* 类型必须是 `public`。&lt;br /&gt;
* 类型必须继承 `LayoutDefinition.Component`，通常继承 `Lidar.Lidar2D` 或 `DetourDance.Lidar3D`。&lt;br /&gt;
* 类型必须有无参构造函数。&lt;br /&gt;
* 类型所在程序集必须在 Detour 读取 `detour.json` 之前已经被加载。&lt;br /&gt;
&lt;br /&gt;
组件类型通常用属性声明：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
[LayoutDefinition.ComponentType(Name = &amp;quot;ROS2d lidar&amp;quot;)]&lt;br /&gt;
public class ROS2Lidar2D : Lidar.Lidar2D&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
公开字段可配合以下属性暴露到参数界面和状态输出：&lt;br /&gt;
&lt;br /&gt;
* `FieldMember`&lt;br /&gt;
* `StatusMember`&lt;br /&gt;
* `MethodMember`&lt;br /&gt;
&lt;br /&gt;
== 4. 你可以做的两类插件 ==&lt;br /&gt;
=== 4.1 启动型插件 ===&lt;br /&gt;
这类插件只关心 DetourLite 启动后执行逻辑，不一定要新增配置里的组件类型。最常见用途是挂额外 API。&lt;br /&gt;
&lt;br /&gt;
`Standalone/DemoPlugin/loader.cs` 就是这种模式。它在 `Plugin.Main()` 里注册了：&lt;br /&gt;
&lt;br /&gt;
* `/eraseCloud`&lt;br /&gt;
* `/get2dlm`&lt;br /&gt;
* `/mycall`&lt;br /&gt;
* `/getPCD`&lt;br /&gt;
&lt;br /&gt;
最小示例：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using DetourCore;&lt;br /&gt;
&lt;br /&gt;
namespace MyStandalonePlugin&lt;br /&gt;
{&lt;br /&gt;
    public class Plugin&lt;br /&gt;
    {&lt;br /&gt;
        public static void Main()&lt;br /&gt;
        {&lt;br /&gt;
            PicoHttpServer.AddGetHandler(&amp;quot;/helloPlugin&amp;quot;, () =&amp;gt;&lt;br /&gt;
            {&lt;br /&gt;
                return &amp;quot;hello from standalone plugin&amp;quot;;&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4.2 组件型插件 ===&lt;br /&gt;
这类插件把新传感器或新部件编译进独立 DLL，让 DetourLite 在读取 `detour.json` 时识别出来。&lt;br /&gt;
&lt;br /&gt;
仓库里的对应示例：&lt;br /&gt;
&lt;br /&gt;
* `Standalone/DemoPlugin/ros2lidar2d.cs`&lt;br /&gt;
* `Standalone/StandaloneSensors/Mid360Lidar.cs`&lt;br /&gt;
* `Standalone/StandaloneSensors/LeishenC16Lidar.cs`&lt;br /&gt;
* `Standalone/DllImportSensor/CppLidar.cs`&lt;br /&gt;
&lt;br /&gt;
== 5. 2D 传感器插件写法 ==&lt;br /&gt;
2D 传感器通常继承 `DetourCore.CartDefinition.Lidar.Lidar2D`，重点是重写两个方法：&lt;br /&gt;
&lt;br /&gt;
* `InitReadLidar()`&lt;br /&gt;
* `ReadLidar()`&lt;br /&gt;
&lt;br /&gt;
最小骨架：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.Threading;&lt;br /&gt;
using DetourCore.CartDefinition;&lt;br /&gt;
&lt;br /&gt;
[LayoutDefinition.ComponentType(Name = &amp;quot;My UDP Lidar&amp;quot;)]&lt;br /&gt;
public class MyUdpLidar : Lidar.Lidar2D&lt;br /&gt;
{&lt;br /&gt;
    private readonly object sync = new();&lt;br /&gt;
    private Lidar.LidarFrame latest = new();&lt;br /&gt;
    private int scanC = 0;&lt;br /&gt;
&lt;br /&gt;
    public override void InitReadLidar()&lt;br /&gt;
    {&lt;br /&gt;
        new Thread(() =&amp;gt;&lt;br /&gt;
        {&lt;br /&gt;
            while (true)&lt;br /&gt;
            {&lt;br /&gt;
                latest = new Lidar.LidarFrame()&lt;br /&gt;
                {&lt;br /&gt;
                    counter = ++scanC,&lt;br /&gt;
                    raw = new[]&lt;br /&gt;
                    {&lt;br /&gt;
                        new Lidar.Lidar2D.RawLidar&lt;br /&gt;
                        {&lt;br /&gt;
                            th = 0,&lt;br /&gt;
                            d = 1000,&lt;br /&gt;
                            intensity = 1&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
                lock (sync)&lt;br /&gt;
                    Monitor.PulseAll(sync);&lt;br /&gt;
            }&lt;br /&gt;
        }).Start();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public override Lidar.LidarFrame ReadLidar()&lt;br /&gt;
    {&lt;br /&gt;
        lock (sync)&lt;br /&gt;
            Monitor.Wait(sync);&lt;br /&gt;
        return latest;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2D 输出约定：&lt;br /&gt;
&lt;br /&gt;
* `th`：角度，单位是 '''度'''。&lt;br /&gt;
* `d`：距离，单位是 '''毫米'''。&lt;br /&gt;
* `intensity`：反射强度，`float`。&lt;br /&gt;
* `counter`：单调递增帧号。&lt;br /&gt;
&lt;br /&gt;
Detour 的 2D 预处理、去拖尾、车体滤波、反光板提取、激光里程计等逻辑都在基类中完成，所以插件作者只需要稳定地产生原始帧。&lt;br /&gt;
&lt;br /&gt;
== 6. 3D 传感器插件写法 ==&lt;br /&gt;
3D 传感器通常继承 `DetourDance.Lidar3D`，同样重写：&lt;br /&gt;
&lt;br /&gt;
* `InitReadLidar()`&lt;br /&gt;
* `ReadLidar()`&lt;br /&gt;
&lt;br /&gt;
3D 返回值是 `LidarOutput3D`，关键字段如下：&lt;br /&gt;
&lt;br /&gt;
* `azimuth`：水平角，单位度。&lt;br /&gt;
* `altitude`：俯仰角，单位度。&lt;br /&gt;
* `d`：距离，单位毫米。&lt;br /&gt;
* `intensity`：反射强度。&lt;br /&gt;
* `progression`：该点在当前扫描周期里的进度，推荐归一化到 `0..1`。&lt;br /&gt;
* `tick`：单调递增扫描序号。&lt;br /&gt;
&lt;br /&gt;
如果你的数据源来自原生库，可以参考 `Standalone/DllImportSensor/CppLidar.cs`；如果来自 ROS2，可以参考 `Standalone/DemoPlugin/ros2lidar2d.cs`；如果来自 UDP 包流，可以参考 `Standalone/StandaloneSensors/*`。&lt;br /&gt;
&lt;br /&gt;
== 7. 开发环境与引用 DLL ==&lt;br /&gt;
Detour 独立插件不是通过 NuGet 分发 SDK，而是直接引用发布到 MDCS 站点的参考 DLL。&lt;br /&gt;
&lt;br /&gt;
运行包下载入口：&lt;br /&gt;
&lt;br /&gt;
* [https://dl.lessokaji.com MDCS 下载站]&lt;br /&gt;
* [https://dl.lessokaji.com/prebaked 预配置下载集合]&lt;br /&gt;
&lt;br /&gt;
插件开发常用引用：&lt;br /&gt;
&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/Detour/RefDetourCore.dll RefDetourCore.dll]&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/Detour/RefDetourDance.dll RefDetourDance.dll]&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/RefFundamentalLib.dll RefFundamentalLib.dll]&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/LessokajiWeaverUtilities.dll LessokajiWeaverUtilities.dll]&lt;br /&gt;
&lt;br /&gt;
可选辅助引用：&lt;br /&gt;
&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/RefLessokajiProtect.dll RefLessokajiProtect.dll]&lt;br /&gt;
* [https://mdcs.lessokaji.com/dependencies/LessokajiWeaver.Fody.dll LessokajiWeaver.Fody.dll]&lt;br /&gt;
&lt;br /&gt;
为方便拉取这些 DLL，提供一个下载脚本：&lt;br /&gt;
&lt;br /&gt;
* [https://mdcs.lessokaji.com/res/get-detour-plugin-sdk.ps1 get-detour-plugin-sdk.ps1]&lt;br /&gt;
&lt;br /&gt;
典型用法：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;powershell&amp;quot;&amp;gt;&lt;br /&gt;
Invoke-WebRequest `&lt;br /&gt;
  -Uri &amp;quot;https://mdcs.lessokaji.com/res/get-detour-plugin-sdk.ps1&amp;quot; `&lt;br /&gt;
  -OutFile &amp;quot;.\get-detour-plugin-sdk.ps1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
powershell -ExecutionPolicy Bypass `&lt;br /&gt;
  -File .\get-detour-plugin-sdk.ps1 `&lt;br /&gt;
  -Destination .\tools&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
执行后会得到：&lt;br /&gt;
&lt;br /&gt;
* `tools\RefDetourCore.dll`&lt;br /&gt;
* `tools\RefDetourDance.dll`&lt;br /&gt;
* `tools\deps\RefFundamentalLib.dll`&lt;br /&gt;
* `tools\deps\LessokajiWeaverUtilities.dll`&lt;br /&gt;
&lt;br /&gt;
== 8. csproj 参考写法 ==&lt;br /&gt;
`Standalone/DemoPlugin/DemoPlugin.csproj` 给出了一个可直接工作的最小模式。&lt;br /&gt;
&lt;br /&gt;
一个简化版示例如下：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;Project Sdk=&amp;quot;Microsoft.NET.Sdk&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;PropertyGroup&amp;gt;&lt;br /&gt;
    &amp;lt;OutputType&amp;gt;Library&amp;lt;/OutputType&amp;gt;&lt;br /&gt;
    &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;&lt;br /&gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;&lt;br /&gt;
  &amp;lt;/PropertyGroup&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;ItemGroup&amp;gt;&lt;br /&gt;
    &amp;lt;Reference Include=&amp;quot;FundamentalLib&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;HintPath&amp;gt;..\tools\deps\RefFundamentalLib.dll&amp;lt;/HintPath&amp;gt;&lt;br /&gt;
    &amp;lt;/Reference&amp;gt;&lt;br /&gt;
    &amp;lt;Reference Include=&amp;quot;DetourCore&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;HintPath&amp;gt;..\tools\RefDetourCore.dll&amp;lt;/HintPath&amp;gt;&lt;br /&gt;
    &amp;lt;/Reference&amp;gt;&lt;br /&gt;
    &amp;lt;Reference Include=&amp;quot;DetourDance&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;HintPath&amp;gt;..\tools\RefDetourDance.dll&amp;lt;/HintPath&amp;gt;&lt;br /&gt;
    &amp;lt;/Reference&amp;gt;&lt;br /&gt;
    &amp;lt;Reference Include=&amp;quot;LessokajiWeaverUtilities&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;HintPath&amp;gt;..\tools\deps\LessokajiWeaverUtilities.dll&amp;lt;/HintPath&amp;gt;&lt;br /&gt;
    &amp;lt;/Reference&amp;gt;&lt;br /&gt;
  &amp;lt;/ItemGroup&amp;gt;&lt;br /&gt;
&amp;lt;/Project&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果希望本地构建后直接落到运行目录，可以像 `Standalone/DemoPlugin` 那样把 `OutputPath` 设到 DetourLite 的 `plugins` 目录，这对调试最方便。&lt;br /&gt;
&lt;br /&gt;
== 9. 构建与部署 ==&lt;br /&gt;
建议流程：&lt;br /&gt;
&lt;br /&gt;
# 在独立工程中编译插件 DLL。&lt;br /&gt;
# 确认文件名以 `Plugin.dll` 结尾，例如 `StandaloneSensorsPlugin.dll`。&lt;br /&gt;
# 把 DLL 复制到 DetourLite 运行目录下的 `plugins` 子目录。&lt;br /&gt;
# 如果插件依赖额外原生库，也一起放到 DetourLite 可搜索到的位置。&lt;br /&gt;
# 启动 DetourLite，检查控制台是否打印：&lt;br /&gt;
#* 找到并加载了 `*Plugin.dll`&lt;br /&gt;
#* 找到了 `Plugin.Main()`&lt;br /&gt;
#* 或至少程序集被成功加载&lt;br /&gt;
&lt;br /&gt;
本仓库当前已验证可编译的示例：&lt;br /&gt;
&lt;br /&gt;
* `Standalone/DemoPlugin`&lt;br /&gt;
* `Standalone/StandaloneSensors`&lt;br /&gt;
* `Standalone/DllImportSensor`&lt;br /&gt;
&lt;br /&gt;
== 10. 当前限制 ==&lt;br /&gt;
这一点非常重要：&lt;br /&gt;
&lt;br /&gt;
* '''DetourLite''' 当前会自动扫描 `plugins\*Plugin.dll`。&lt;br /&gt;
* '''Windows 版 Detour.exe''' 当前仓库里 '''没有''' 对等的自动插件加载器。&lt;br /&gt;
&lt;br /&gt;
这会带来两个直接影响：&lt;br /&gt;
&lt;br /&gt;
* 如果插件只是给 DetourLite 增加 HTTP API，这没有问题。&lt;br /&gt;
* 如果插件新增了自定义组件类型，而你又想在 Windows `Detour.exe` 里直接通过 UI 新建、编辑、加载这种组件，那么客户端进程也必须先把同一个程序集加载进来，否则 UI 的类型列表和 JSON 反序列化都看不到该类型。&lt;br /&gt;
&lt;br /&gt;
换句话说，'''当前代码最稳妥的使用方式''' 是：&lt;br /&gt;
&lt;br /&gt;
* 纯扩展 API：只部署到 DetourLite。&lt;br /&gt;
* 自定义组件：优先用于 Lite 端独立运行场景；如果还要配合 Windows UI 使用，则需要让客户端也加载同一个插件程序集。&lt;br /&gt;
&lt;br /&gt;
== 11. 推荐开发方法 ==&lt;br /&gt;
结合仓库里的 `Standalone` 示例，推荐按下面的顺序开发：&lt;br /&gt;
&lt;br /&gt;
# 先做一个只包含 `Plugin.Main()` 的最小插件，确认 DLL 能被 DetourLite 发现并执行。&lt;br /&gt;
# 再加一个最小 HTTP 路由，确认运行时初始化逻辑没问题。&lt;br /&gt;
# 如果要接传感器，再新建一个继承 `Lidar2D` 或 `Lidar3D` 的类型，先返回最小假数据。&lt;br /&gt;
# 假数据跑通后，再接入 ROS2 / UDP / 原生 DLL 等真实数据源。&lt;br /&gt;
# 最后再补 `FieldMember` / `StatusMember`，把参数和状态整理到可维护的程度。&lt;br /&gt;
&lt;br /&gt;
这样排查最简单，能快速区分是：&lt;br /&gt;
&lt;br /&gt;
* 加载失败，&lt;br /&gt;
* 初始化失败，&lt;br /&gt;
* 组件识别失败，&lt;br /&gt;
* 还是数据源本身有问题。&lt;br /&gt;
&lt;br /&gt;
== 12. 结语 ==&lt;br /&gt;
Standalone Plugin 的核心价值是：'''把项目级扩展放在独立 DLL 里维护，而不是不断改 Detour 主工程'''。对于项目交付、客户定制、特定传感器桥接、快速试验都更合适。&lt;br /&gt;
&lt;br /&gt;
如果只是想扩 API，写启动型插件即可；如果要接自定义雷达，就按 `Lidar2D` / `Lidar3D` 的约定返回原始帧，让 Detour 继续负责后面的预处理和 SLAM 计算。&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>