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