<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki2.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E6%96%B0%E7%9A%84%E9%9B%B7%E8%BE%BE</id>
	<title>如何适配新的雷达 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki2.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E6%96%B0%E7%9A%84%E9%9B%B7%E8%BE%BE"/>
	<link rel="alternate" type="text/html" href="https://wiki2.lessokaji.com/index.php?title=%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E6%96%B0%E7%9A%84%E9%9B%B7%E8%BE%BE&amp;action=history"/>
	<updated>2026-05-16T15:01:13Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>https://wiki2.lessokaji.com/index.php?title=%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E6%96%B0%E7%9A%84%E9%9B%B7%E8%BE%BE&amp;diff=997&amp;oldid=prev</id>
		<title>Artheru：​Initial bilingual draft (auto-published)</title>
		<link rel="alternate" type="text/html" href="https://wiki2.lessokaji.com/index.php?title=%E5%A6%82%E4%BD%95%E9%80%82%E9%85%8D%E6%96%B0%E7%9A%84%E9%9B%B7%E8%BE%BE&amp;diff=997&amp;oldid=prev"/>
		<updated>2026-05-16T11:15:16Z</updated>

		<summary type="html">&lt;p&gt;Initial bilingual draft (auto-published)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;languages/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 概述 / Overview ==&lt;br /&gt;
本页是 ''激光雷达插件适配'' 的总入口。MDCS 把雷达 / 相机视为一类 ''传感器插件''，通过 Medulla2 的 `IOObject` 体系热插拔。开发新雷达适配的目标是写一个 .NET DLL，包含名为 `MainIOObject` 的类，继承自下列基类之一：&lt;br /&gt;
&lt;br /&gt;
This is the entry page for adapting a new lidar to MDCS. Medulla2 treats lidars (and cameras) as ''sensor plugins'': a single .NET DLL whose entry class is named `MainIOObject` and inherits from one of the bases below.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! 传感器 / Sensor !! 基类 / Base class !! 文件 / File !! 详细教程 / Detailed guide&lt;br /&gt;
|-&lt;br /&gt;
| 2D 激光雷达 / 2D lidar || `Lidar2DIOObject` || `D:\src\M2\OfficialPlugins\LidarController\Lidar2DIOObject.cs` || [[Special:MyLanguage/2D激光雷达适配|2D激光雷达适配]]&lt;br /&gt;
|-&lt;br /&gt;
| 3D 激光雷达 / 3D lidar || `Lidar3DIOObject` || `D:\src\M2\OfficialPlugins\LidarController\Lidar3DIOObject.cs` || [[Special:MyLanguage/3D激光雷达适配|3D激光雷达适配]]&lt;br /&gt;
|-&lt;br /&gt;
| 相机（2D/3D）/ Camera || `IOObject`（约定 `MainIOObject` 类名）/ `IOObject` (convention: class name `MainIOObject`) || `D:\src\M2\MedullaCore\Types\IO.cs:47` || [[Special:MyLanguage/3D相机适配|3D相机适配]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 适配工作量速查 / Adaptation effort cheat-sheet ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! 难度 / Difficulty !! 雷达类型 / Lidar type !! 典型工作 / Typical work !! 工时 / Effort&lt;br /&gt;
|-&lt;br /&gt;
| ⭐ Easy || 厂商提供 .NET SDK 的 2D / 3D 雷达 / 2D/3D with vendor .NET SDK || 包一层适配，把 SDK 回调写到 cachedLidar / Wrap SDK callbacks into `cachedLidar` || 0.5–1 d&lt;br /&gt;
|-&lt;br /&gt;
| ⭐⭐ Medium || 协议公开但需自己解析报文（TCP / UDP）/ Open protocol, parse frames || 帧解析 + 字节序 + 点云转换 / Frame parsing + endianness + point conversion || 1–3 d&lt;br /&gt;
|-&lt;br /&gt;
| ⭐⭐⭐ Hard || 协议私有 / 半私有，需逆向 / 现场抓包 / Closed protocol, reverse-engineer || + 协议分析 + 异常恢复 / Plus protocol analysis &amp;amp; failure recovery || 3–10 d&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 适配前先做的事 / Before you start ==&lt;br /&gt;
1. 拿到雷达的硬件说明书与通信手册（角分辨率、扫描频率、协议、数据格式）。&amp;lt;br /&amp;gt;Get the hardware datasheet and protocol manual (angular resolution, scan rate, protocol, frame layout).&lt;br /&gt;
2. 用厂商工具确认雷达 ''数据流通'' — 设备通电、连上电脑、能看到点云。&amp;lt;br /&amp;gt;Use the vendor tool to verify the device powers up and streams.&lt;br /&gt;
3. 决定使用的通讯方式（TCP / UDP / 串口 / USB CAN）。&amp;lt;br /&amp;gt;Pick the transport (TCP / UDP / serial / USB CAN).&lt;br /&gt;
4. 用 [[Special:MyLanguage/MDCS-plugin-helper|MDCS-plugin-helper]] 创建工程骨架：&amp;lt;br /&amp;gt;Scaffold the project with MDCS-plugin-helper:&lt;br /&gt;
   &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
   cd MDCS-plugin-helper&lt;br /&gt;
   python generate.py&lt;br /&gt;
   # 选择 lidar sensor plugin / Select &amp;quot;lidar sensor plugin&amp;quot;&lt;br /&gt;
   # 输入工程名，例如 / Project name e.g.: WLR716Lidar&lt;br /&gt;
   &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 关键概念 / Key concepts ==&lt;br /&gt;
=== 数据模型 / Data model ===&lt;br /&gt;
雷达插件每扫完一帧，把点云填入基类的 `cachedLidar` 字段（`LidarPoint2D[]` 或 `LidarPoint3D[]`），然后调用 `output()`。`output()` 把当前帧序列化到 `DObject`（共享内存）；Detour 在另一进程订阅这个 DObject 拿数据。&lt;br /&gt;
&lt;br /&gt;
A lidar plugin fills its frame into `cachedLidar` (`LidarPoint2D[]` for 2D, `LidarPoint3D[]` for 3D) once per full scan, then calls `output()`. `output()` serialises to a named `DObject` (shared memory); Detour subscribes from another process and reads from there.&lt;br /&gt;
&lt;br /&gt;
2D 点 / 2D point:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class LidarPoint2D&lt;br /&gt;
{&lt;br /&gt;
    public float th;          // 角度 / angle (rad or degrees per plugin)&lt;br /&gt;
    public float d;            // 距离 / distance (mm)&lt;br /&gt;
    public float intensity;    // 归一化反射率 / normalised reflectivity&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3D 点 / 3D point:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class LidarPoint3D&lt;br /&gt;
{&lt;br /&gt;
    public float d;            // 距离 / distance&lt;br /&gt;
    public float azimuth;      // 方位角 / azimuth&lt;br /&gt;
    public float altitude;     // 俯仰角 / altitude&lt;br /&gt;
    public float intensity;&lt;br /&gt;
    public float progression;  // 用于多回波合并 / multi-echo&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 生命周期 / Lifecycle ===&lt;br /&gt;
&lt;br /&gt;
  io load plugins/WLR716Lidar.dll  →  Reflection 找到 MainIOObject 并实例化&lt;br /&gt;
                                  →  插件 Start(...) 启动通信线程&lt;br /&gt;
                                  →  线程内循环: 读包 → 解析 → fill cachedLidar → output()&lt;br /&gt;
                                  →  Medulla / Detour 在 DObject 上等到帧后继续工作&lt;br /&gt;
&lt;br /&gt;
启动命令（`startup.iocmd`）：&lt;br /&gt;
The Medulla startup script (`startup.iocmd`) wires it up:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
lidar = io load plugins/WLR716Lidar.dll&lt;br /&gt;
lidar Start 192.168.0.2 2110&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 关键字段 / Key fields ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! 字段 / Field !! 类型 !! 含义 / Meaning&lt;br /&gt;
|-&lt;br /&gt;
| `cachedLidar` || `LidarPoint2D[]` / `3D[]` || 最新一整帧点云 / latest full-scan cloud&lt;br /&gt;
|-&lt;br /&gt;
| `frame` || `long` || 帧计数器，单调递增 / monotonically increasing frame counter&lt;br /&gt;
|-&lt;br /&gt;
| `scanC` || `int` || 雷达自上电起的帧号 / device's own frame count since power-on&lt;br /&gt;
|-&lt;br /&gt;
| `interval` || `double` || 上一帧到当前帧的耗时（ms）/ elapsed ms since previous frame&lt;br /&gt;
|-&lt;br /&gt;
| `tick` || `long` || 输出时刻的系统 tick / system tick at `output()`&lt;br /&gt;
|-&lt;br /&gt;
| `angleBias` || `float` || 角度偏置；用于硬件倒装或安装角误差 / angle offset (mount correction)&lt;br /&gt;
|-&lt;br /&gt;
| `mirror` || `bool` || 倒装标志，true 时 `th := -th` / mirror flag — negates `th`&lt;br /&gt;
|-&lt;br /&gt;
| `ReflexRange` || `float` || 反射率归一化分母 / reflectivity normaliser&lt;br /&gt;
|-&lt;br /&gt;
| `angleStart/End` || `float` || 输出帧的角度过滤区间 / output FOV filter&lt;br /&gt;
|-&lt;br /&gt;
| `minDist/maxDist` || `float` || 距离过滤区间 / distance filter&lt;br /&gt;
|-&lt;br /&gt;
| `pointsN` || `int` || 当前帧点数（output 前更新）/ point count&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 插件发现 / Plugin discovery ===&lt;br /&gt;
Medulla 加载 DLL 时通过反射查找 ''类名为 `MainIOObject`'' 的类型，并要求其有无参构造函数。无属性标记。&lt;br /&gt;
At load time Medulla looks for the type named `MainIOObject` via reflection; it must have a parameterless constructor. There is no `[Plugin]` attribute.&lt;br /&gt;
&lt;br /&gt;
== 最小骨架 / Minimal skeleton ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System;&lt;br /&gt;
using System.Collections.Generic;&lt;br /&gt;
using System.Net.Sockets;&lt;br /&gt;
using System.Threading;&lt;br /&gt;
using LidarController;&lt;br /&gt;
&lt;br /&gt;
namespace WLR716Lidar&lt;br /&gt;
{&lt;br /&gt;
    public class MainIOObject : Lidar2DIOObject&lt;br /&gt;
    {&lt;br /&gt;
        private string _ip;&lt;br /&gt;
        private int _port = 2110;&lt;br /&gt;
&lt;br /&gt;
        public MainIOObject()&lt;br /&gt;
        {&lt;br /&gt;
            // 在出厂正装情况下，雷达正前方对应的扫描起 / 止角度（看产品手册）&lt;br /&gt;
            // For factory-default mounting, the scan start/end relative to lidar forward.&lt;br /&gt;
            ScanStartAngle = -135;&lt;br /&gt;
            ScanEndAngle   = 135;&lt;br /&gt;
            ScanAngleSgn   = 1;        // 逆时针 / counter-clockwise&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        public void Start(string ip, int port = 2110)&lt;br /&gt;
        {&lt;br /&gt;
            _ip = ip; _port = port;&lt;br /&gt;
            new Thread(() =&amp;gt;&lt;br /&gt;
            {&lt;br /&gt;
                while (true)&lt;br /&gt;
                {&lt;br /&gt;
                    try { Loop(); }&lt;br /&gt;
                    catch (Exception ex)&lt;br /&gt;
                    {&lt;br /&gt;
                        Console.WriteLine($&amp;quot;{ex.Message}&amp;quot;);&lt;br /&gt;
                        Thread.Sleep(1000);&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }).Start();&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        private void Loop()&lt;br /&gt;
        {&lt;br /&gt;
            using var tcp = new TcpClient(_ip, _port);&lt;br /&gt;
            using var ns  = tcp.GetStream();&lt;br /&gt;
&lt;br /&gt;
            var thetaList   = new List&amp;lt;float&amp;gt;();&lt;br /&gt;
            var distList    = new List&amp;lt;int&amp;gt;();&lt;br /&gt;
            var intensList  = new List&amp;lt;float&amp;gt;();&lt;br /&gt;
            var ticStart    = DateTime.Now;&lt;br /&gt;
&lt;br /&gt;
            while (true)&lt;br /&gt;
            {&lt;br /&gt;
                // ... read frame header + payload (vendor-specific) ...&lt;br /&gt;
                // when one full scan accumulated:&lt;br /&gt;
                var cloud = new List&amp;lt;LidarPoint2D&amp;gt;(thetaList.Count);&lt;br /&gt;
                for (int i = 0; i &amp;lt; thetaList.Count; i++)&lt;br /&gt;
                    cloud.Add(new LidarPoint2D {&lt;br /&gt;
                        th = (mirror ? -thetaList[i] : thetaList[i]) + angleBias,&lt;br /&gt;
                        d  = distList[i],&lt;br /&gt;
                        intensity = intensList[i] / ReflexRange&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                cachedLidar = cloud.ToArray();&lt;br /&gt;
                frame    += 1;&lt;br /&gt;
                scanC    += 1;&lt;br /&gt;
                interval  = (DateTime.Now - ticStart).TotalMilliseconds;&lt;br /&gt;
                ticStart  = DateTime.Now;&lt;br /&gt;
                tick      = DateTime.Now.Ticks;&lt;br /&gt;
                output();&lt;br /&gt;
&lt;br /&gt;
                thetaList.Clear(); distList.Clear(); intensList.Clear();&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;
详细的协议解析、安装校准与 3D 雷达细节见对应的子页面：&lt;br /&gt;
For per-protocol parsing, install-time calibration and 3D-specific details, see the dedicated pages:&lt;br /&gt;
&lt;br /&gt;
* [[Special:MyLanguage/2D激光雷达适配|2D激光雷达适配]]&lt;br /&gt;
* [[Special:MyLanguage/3D激光雷达适配|3D激光雷达适配]]&lt;br /&gt;
* [[Special:MyLanguage/3D相机适配|3D相机适配]]&lt;br /&gt;
&lt;br /&gt;
== 校准与对齐 / Calibration &amp;amp; alignment ==&lt;br /&gt;
雷达适配完成后必做：&lt;br /&gt;
After the plugin works, always:&lt;br /&gt;
&lt;br /&gt;
# '''外参标定 / Extrinsic calibration''': 把雷达坐标系对齐到车体坐标系。教程见 [[Special:MyLanguage/标定激光雷达外参|标定激光雷达外参]]。&lt;br /&gt;
# '''水平度复核 / Horizontality check''': 用水平仪 ± IMU 反馈确认雷达扫描平面与地面平行（高位叉车场景尤其重要）。&lt;br /&gt;
# '''时间戳一致性 / Timestamp coherence''': 多传感器 SLAM 场景中，`tick` 与系统 NTP 必须一致；&amp;gt; 50 ms 偏差会引起 SLAM 漂移。&lt;br /&gt;
# '''强度归一化 / Intensity normalisation''': 通过 `ReflexRange` 调整，让反光板的强度明显高于普通墙面（在 Detour 可视化中表现为偏红）。&lt;br /&gt;
&lt;br /&gt;
== Detour 端怎么收 / Detour-side consumption ==&lt;br /&gt;
Detour 在 `D:\src\Detour\DetourCore\CartDefinition\Lidar.cs:302-311` 通过 `DObject(name)` 订阅雷达数据，等待 `Wait()` 后反序列化 `LidarOutput`，再喂给 SLAM。所以插件作者完全不需要直接调用 Detour API — 把帧塞进 `cachedLidar` + 调 `output()` 即可。&lt;br /&gt;
&lt;br /&gt;
Detour subscribes via `DObject(name)` and deserialises `LidarOutput` once `Wait()` returns. Plugin authors don't call Detour APIs directly — `cachedLidar` + `output()` is the whole contract.&lt;br /&gt;
&lt;br /&gt;
外部里程计 / IMU 数据是另一条通路：通过 `TightCoupler.PostExternalFeed(...)`（见 `D:\src\Detour\DetourCore\Algorithms\TightCoupler.ExternalCoupler.cs`）。雷达插件本身不走这条。&lt;br /&gt;
&lt;br /&gt;
External odometry / IMU enters Detour via a separate path (`TightCoupler.PostExternalFeed`). That is not the lidar plugin's job.&lt;br /&gt;
&lt;br /&gt;
== 失败模式与对策 / Common failures ==&lt;br /&gt;
* '''雷达连不上 / Can't connect''': 检查 IP / 防火墙，看 `Start()` 抛出的异常被外层 catch 后 sleep 重连。&lt;br /&gt;
* '''解析帧长不对 / Wrong frame length''': 字节序错误，确认大端 / 小端；用 vendor 工具抓 1 帧对比。&lt;br /&gt;
* '''点云 0° 不在正前方 / 0° not aligned to forward''': 用 `angleBias` 或 `ScanStartAngle/EndAngle` 修正。&lt;br /&gt;
* '''反射率值不合理 / Reflectivity off''': 调整 `ReflexRange`；不同雷达把强度 / 反光率 / 回波 三个量混用，要看手册区分。&lt;br /&gt;
* '''帧率不稳定 / Jittery frame rate''': 多线程同步问题。把读包改为完整 `while(want_len) Read`（同 `TestHe3051\Class1.cs`）。&lt;br /&gt;
&lt;br /&gt;
== 工程示例 / Working example ==&lt;br /&gt;
完整可运行示例：`D:\src\cookbook\MDCS-plugin-helper\TestHe3051\Class1.cs`（145 行，TCP 流，He3051 雷达）。&lt;br /&gt;
&lt;br /&gt;
Complete working example: `D:\src\cookbook\MDCS-plugin-helper\TestHe3051\Class1.cs` (145 lines, TCP, He3051 lidar).&lt;br /&gt;
&lt;br /&gt;
== 相关页面 / See also ==&lt;br /&gt;
* [[Special:MyLanguage/2D激光雷达适配|2D激光雷达适配]]&lt;br /&gt;
* [[Special:MyLanguage/3D激光雷达适配|3D激光雷达适配]]&lt;br /&gt;
* [[Special:MyLanguage/3D相机适配|3D相机适配]]&lt;br /&gt;
* [[Special:MyLanguage/Medulla-API|Medulla-API]]&lt;br /&gt;
* [[Special:MyLanguage/标定激光雷达外参|标定激光雷达外参]]&lt;br /&gt;
* [[Special:MyLanguage/使用手册 - 双雷达标定手册|双雷达标定手册]]&lt;br /&gt;
* [[Special:MyLanguage/激光雷达选型测试报告|激光雷达选型测试报告]]&lt;br /&gt;
&lt;br /&gt;
[[Category:二次开发相关说明]]&lt;br /&gt;
[[Category:开发手册]]&lt;/div&gt;</summary>
		<author><name>Artheru</name></author>
	</entry>
</feed>