#agent #tether #lidar #system #clustering #consolidator #lidar2-d

已撤回 tether-lidar2d-consolidation-rs

Tether Lidar2D Consolidator Agent,Rust版本

0.3.1 2023年11月7日

#6 in #tether

MIT 协议

68KB
1.5K SLoC

Tether Lidar2D Consolidator,用Rust编写

这基本上是将原始NodeJS Agent(此处称为“原始Agent”✌️)直接移植到Rust 🦀。

实现了原始Agent的所有基本功能,但应用程序的结构并不试图复制原始版本。

两个主要目标是

  • 学习在真实世界应用中使用Rust,遵循一个已知且基本工程挑战已被“解决”的现有代码库
  • 产生一个更快速、更轻量级的Lidar Consolidator Agent版本,可以在资源较少的平台(如Raspberry Pi)上部署,而不对系统造成太大的压力

开发依赖

Paho Eclipse MQTT有一些(非Rust)依赖。在Mac上,你可能需要以下内容

brew install openssh cmake

和在Linux上

sudo apt install libssl-dev build-essential cmake

基本用法

如果使用cargo,你可以简单地使用

cargo run

或者,例如,以发布版(推荐)和某些命令行参数运行

cargo run --release -- --loglevel debug

或者,运行编译的二进制文件(假设你已经运行了cargo build --release),通常位于./target/release/tether-lidar2d-consolidation-rs

命令行配置

你可以通过在你的执行命令后附加--help来查看所有可用的命令行参数,例如cargo run -- --help

选项故意做得几乎与原始Agent中使用的选项相同,只有一些值得注意的例外

  • 这里我们使用了一个标志--perspectiveTransform.includeOutside而不是设置一个布尔值ignoreOutside。这更好地反映了默认用法,并在使用标志时更有意义(如果不显式附加,默认为false
  • 对于Tether,你可以提供--tether.host--tether.username--tether.password,如果这些与默认值不同
  • 没有与autoBroadcastConfig相关的选项,因为不需要这种行为;我们每次启动和请求时都保存和(重新)发布配置,基本上每次都改变

图书馆注意事项

MQTT 客户端

最初我们尝试使用 mqtt-rs,因为它看起来相对简单易用,但将来可能会考虑使用 mqttrs,它可能“更好”。

目前我们选择了 paho-mqtt,因为它似乎得到了很好的支持,并提供了一些实际场景(尤其是异步)的示例。

MessagePack 编码/解码

rmp_serde 适用于 JSON 和 MsgPack 序列化/反序列化。我们可能没有充分利用零拷贝操作,但这需要更多时间来弄清楚。

最初我们尝试了 msgpack-simple,它警告说“性能不如静态解决方案”,但作为一个起点使用起来要容易得多。

集群

我们尝试了库 kddbscan,尽管这可能会更“准确”,但它似乎运行得太慢。无论如何,这和 OG Agent 使用的 DBSCAN 算法非常不同。

然后我们选择了更朴实的(但据称性能更好)的 petal-clustering。这反过来又需要一种叫做 ndarray 的东西,它看起来非常相似(并且可能基于)Python 的 numpy

目前,我们使用 OG Agent 中的 DBSCAN 方法,但将来可能值得测试这个库中其他支持的模式,即 HDbscan,它可能更快(参见 这篇论文)。

另一种可能性可能是库 linfa-clustering

JSON 序列化/反序列化

我们使用库 serdeserde_json 的组合,这使得以各种方式处理 JSON 变得容易,包括与 Rust 类型/结构体对应的有类型强类型,这正是我们在加载/保存 Config 时的需求。

透视变换

我们正在尝试从 ROI 到标准化“正方形”输出四边形的“四边形到四边形投影”,类似于 OG Agent 中的 perspective-transform

到目前为止

最终,我们使用了 ndarray(已安装以支持集群计算)和 nalgebra 的组合。

日志记录

我们使用 logenv-logger。默认日志级别设置为 INFO,但可以被覆盖,例如通过环境变量前缀,例如。

RUST_LOG=debug cargo run

命令行配置

我们使用 clap,它仅进行命令行参数解析(不使用文件、环境变量等)。

more-config 这样的工具可能很有用,因为它包含了与 NodeJS 的 rc 包类似的功能。

实现说明

  • 与 OG 代理一样,LIDAR 设备的序列字符串是从主题中提取的,具体是从 agentIdOrGroup 部分,位于 lidar2d/{agentIdOrGroup}/scans
  • 样本从数组数组中复制;每个“样本”是一个包含 [angle, distance] 元素的数组,有时还有 [angle, distance, quality](尽管后者尚未处理)。样本被转换为点,所有转换后的点的列表(向量)被“插入”(即替换,如果键已存在)到表示所有 LIDAR 设备(以序列字符串为键)的哈希表中。
    • 可能存在一些数据分配/复制操作;需要尽可能减少这些操作
    • 在此过程中有一些 .unwrap() 的实例;在某些情况下应更仔细地处理错误

待办事项

  • 添加(可选)tether-tracking-smooth 功能,内置
  • 存在区域应在实际空间中定义(更合理的校准),而不是标准化跟踪空间
  • 存在区域应可定义为除矩形之外的形状
  • 平滑还应删除应合并的重复现有点(不要留下“过时”的点)
  • 确保在 Raspberry Pi 上编译,然后交叉编译(例如从 Mac)
  • 保留消息,用于配置发布 - 移除“请求配置”主题的需求
  • 调试日志级别应抑制 MQTT 日志消息(太冗长)
  • 允许所有关键设置(聚类,平滑)在命令中实时更新
  • 如果在超时后没有从已知设备接收到数据,则打印警告/错误(以及可选的恐慌?)
  • 如果没有找到 Lidar 设备配置文件,则创建一个
  • 维护“状态”(配置数据),并在启动时发布,这应该允许可视化器开始显示扫描数据
  • 使用 rmp-serde 替代 msgpack-simple 进行 MessagePack
  • 从磁盘读取/写入配置数据
  • 转换传入的扫描点:旋转和位置/偏移
  • 对传入样本应用扫描掩码阈值
  • 应用最大聚类大小过滤
  • 处理 ROI、变换(扭曲)
  • 允许通过“saveLidarConfig”保存传入的配置(设备,ROI)
  • 允许按请求创建 AutoMaskSampler
  • 应使用多个传感器进行测试
  • 跟踪输出应应用 ignoreOutside、ignoreOutsideMargin 逻辑
  • 如果收到未知/未配置的设备,则将其添加到配置中,并使用一些默认值
  • 从命令行参数/默认值加载/覆盖一些设置(例如 Tether、聚类)
  • 自动生成代理 UUID
  • 使用“真实”的 Tether Agent crate - 尽管目前尚未设置为异步
  • 将潜在阻塞的过程(例如配置写入磁盘)移入单独的线程或非阻塞函数
  • 退出时正确关闭客户端,以便队列也得到正确销毁
  • 目前,如果“扫描样本”是形式为 (f64,f64) 的元组,即 (角度,距离),则如果包含质量,系统将引发恐慌。这意味着我们可能需要一个没有固定长度的数组,或者简单地完全删除质量“字段”
  • 清除和/或设置新的 AutoMask Samplers 时可能存在内存泄漏

有用的资源

依赖项

~28–42MB
~737K SLoC