相当复杂的传感器网络

电器工程 微控制器 传感器 无线的 沟通 xbee
2022-01-22 13:44:58

我最近在做一个项目,这是第一个涉及到足以使传感器网络复杂化的项目。最后,我认为沟通是整体性能的瓶颈,我想知道更有经验的人会如何解决这个问题。这是一个很长的阅读,但我认为它很有趣,所以请坚持下去。问题是设计一种能够在障碍物路线上航行并将乒乓球投向棕色盒子目标的自主飞艇。开始:

传感器

  • 4D Systems uCAM-TTL 相机模块 - UART 接口
  • HMC6352 数字罗盘 - I2C 接口
  • Maxbotix Sonar ez4 - 1 针模拟接口

执行器

  • 2 个 L293D 电机驱动器(连接到简单的爱好电机) - 这些用于双向驱动 6 个电机。他们需要 PWM 输入来改变速度。现在我们的 3 个电机总是在做同样的事情(控制向上/向下运动的那些),所以它们只需要来自我们的控制器的 2 个 PWM 输出来控制所有 3 个电机。控制横向运动的其他 3 个电机都需要单独控制(用于全方位运动),因此我们的控制器需要另外 6 个 PWM 输出。
  • 伺服电机 - PWM接口

控制器

出于稍后会清楚的原因,我们最终使用了 2x ATmega328P。我们使用 Arduino Uno 对它们进行编程(我们无法访问 ISP)但我们制造了一个定制的 PCB,因此我们不必使用 arduino 板,因为这只会给我们的飞艇增加不必要的重量。至于我们为什么选择 ATmega328P,我对 arduino 环境非常熟悉,我认为这让代码开发变得更快更容易。

通讯与处理

  • 2x Xbee 基础版
  • 2x ATmega328P
  • 使用 openCV 运行 C++ 的台式计算机

从相机模块可以看出,我们的大部分项目都依赖于计算机视觉。飞艇只能承载这么大的重量,我们对在微控制器上实现计算机视觉感到不舒服。所以我们最终做的是使用 XBee 将图像数据传递回台式计算机。所以在服务器端,我们接收图像数据并使用 openCV 处理图像并从中找出一些东西。现在服务器端还需要知道高度信息(来自声纳)和指南针信息。

第一个问题是我们无法让相机由微控制器控制,原因有几个。主要问题是 uP 的内部存储器无法处理存储整个帧。通过巧妙的编码可能有解决这个问题的方法,但出于这个问题的目的,让我们假设这是不可能的。所以为了解决这个问题,我们让服务器端通过 XBee 收发器发送相机命令,并且 XBee 接收器(在飞艇上)的输出连接到相机的输入。

下一个问题是单个 ATmega328P 上没有足够的 PWM 来控制所有电机,因为 I2C 接口使用了一个 PWM 引脚(该死的……)。这就是为什么我们决定使用第二个。无论如何,该代码实际上非常适合并行处理,因为高度控制完全独立于横向移动控制(因此 2 微可能比连接到 PWM 控制器的更好)。因此,U1 负责 2 个 PWM 输出(向上/向下)并读取声纳。U2 负责读取罗盘、控制 6 个 PWM 输出(横向电机)以及读取声纳。U2 还负责通过 XBee 接收来自服务器的命令。

这导致了我们的第一个沟通问题。XBee DOUT 线连接到微控制器和相机。当然,现在我们设计了一个协议,以便我们的微命令将忽略相机命令,而相机命令将忽略微命令,这很好。然而,当忽略我们的微指令时,相机会在其输出线上发回 NAK 数据。由于该命令是针对 micro 的,我们需要以某种方式关闭到 XBee 的相机输出。为了解决这个问题,我们制作了微控制器 2 个 FET,它们位于相机和 XBee(这是第一个 FET)之间以及 U2 和 XBee(这是第二个 FET)之间。因此,当摄像机试图将信息发送回服务器时,第一个 FET 处于“开启”状态,而第二个 FET 处于“关闭”状态。

因此,为了让您了解它是如何工作的,这里有几个例子:

  1. 服务器请求一张图片 - PIC_REQUEST 通过 XBee 并到达 U2 和相机。U2 忽略它,相机发回图像数据。
  2. 服务器刚刚处理完一张图片并正在发送电机数据以告诉飞艇右转 - MOTOR_ANGLE(70) 通过 XBee 到达 U2 和摄像头。U2 识别为微命令,因此关闭了相机的 FET(但也许相机已经用 NAK 响应了?谁知道……)。U2 然后通过改变电机 PWM 输出来响应命令。然后它会重新打开相机的 FET(这是默认设置,因为图像数据是最重要的)。
  3. 服务器意识到我们已经到了障碍路线中的一个点,我们的默认悬停高度现在需要为 90 英寸而不是 50 英寸。SET_HEIGHT 通过 XBee,发生与示例 2 相同的事情。U2 识别 SET_HEIGHT 命令并在 U1 上触发中断。U1 现在从它的高度控制回路中出来并等待从 U2 接收串行数据。没错,更多的串行数据。此时 U2 的 FET 开启(相机的 FET 关闭),因此服务器接收到 U2 也发送给 U1 的高度。那是出于验证目的。现在 U1 为 height2HoverAt 重置其内部变量。U2 现在关闭它的 FET 并重新打开相机 FET。

我肯定遗漏了大量信息,但我认为这足以理解一些复杂性。最后,我们的问题只是同步一切。有时缓冲区中会留下数据,但只有 3 个字节(我们所有的命令都是 6 个字节序列)。有时我们会失去与相机的连接,不得不重新同步它。

所以我的问题是:你们建议哪些技术可以使所有这些组件之间的通信更可靠/健壮/更简单/更好?

例如,我知道有人会在板载 XBee 输出和摄像头之间添加一个延迟电路,以便微型在响应带有 NAK 的微型命令之前有机会关闭摄像头的通话线路。还有其他类似的想法吗?

谢谢,我相信这将需要很多编辑,所以请继续关注。


编辑1:对我们来说,通过其中一个微控制器拼接相机的 UART 数据似乎是不可能的。相机数据有两个选项,原始位图或 JPEG。对于原始位图,相机会尽可能快地向您发送数据。ATmega328P 只有 128 字节的串行缓冲区(技术上这是可配置的,但我不确定如何配置),我们认为我们无法足够快地将其从缓冲区中取出并通过 XBee。这留下了 JPEG 方法,它发送每个包并等待控制器确认它(小握手协议)。这可以达到的最快速度是 115200 波特。现在由于某种原因,我们可以通过 XBee 可靠地传输大量数据的最快速度是 57600 波特(即使在我们进行了节点/网络配对以允许自动重新发送功能之后)。在我们的网络中添加额外的停止(相机到微型到 XBee,而不仅仅是相机到 XBee)只是简单地减慢了传输图像所需的时间。我们需要一定的图像刷新率才能使我们的电机控制算法正常工作。

3个回答

我知道您想选择一个您熟悉的开发环境,这样您就可以开始运行,但我认为硬件/软件的权衡可能会让您陷入困境,因为您坚持使用 Arduino,而不是选择拥有所有功能的部分您需要的硬件外围设备,而是用中断驱动的 C 语言编写所有内容。

我同意@Matt Jenkins 的建议,并希望对此进行扩展。

我会选择带有 2 个 UART 的 uC。一个连接到 Xbee,一个连接到相机。uC 接受来自服务器的命令以启动摄像头读取,并且可以编写例程以逐字节地将数据从摄像头 UART 通道传输到 XBee UART 通道 - 因此没有缓冲区(或最多只有一个非常小的缓冲区)一)需要。我会尝试通过选择一个也满足您所有 PWM 需求的部件(8 个 PWM 通道?)来一起消除其他 uC,如果您想坚持使用 2 个不同的 uC 来处理它们各自的轴,那么也许不同的通信接口会更好,因为你所有的其他 UART 都会被占用。

其他人还建议迁移到嵌入式 linux 平台来运行所有东西(包括 openCV),我认为这也是值得探索的东西。不过,我以前去过那里,一个为期 4 个月的学校项目,你只需要尽快完成它,不能因为分析而陷入瘫痪 - 我希望它对你来说没问题!


编辑#1 回复@JGord 的评论:

我做了一个使用 ATmega164p 实现 UART 转发的项目。它有 2 个 UART。这是该项目的逻辑分析仪捕获(Saleae USB 逻辑分析仪)的图像,显​​示了 UART 转发: 分析仪捕获

最上面一行是源数据(在这种情况下是您的相机),最下面一行是转发到的 UART 通道(在您的情况下是 XBee)。为此编写的例程处理了 UART 接收中断。现在,您是否相信在进行此 UART 转发时,您可以愉快地配置您的 PWM 通道并处理您的 I2C 例程?让我解释一下。

每个 UART 外设(无论如何都是我的 AVR)由几个移位寄存器、一个数据寄存器和一个控制/状态寄存器组成。如果出现以下任一情况,该硬件将自行执行操作(假设您已经初始化了波特率等),而无需您进行任何干预:

  1. 一个字节进来或
  2. 一个字节被放置在其数据寄存器中并标记为输出

这里重要的是移位寄存器和数据寄存器。假设 UART0 有一个字节进入,我们想将该流量转发到 UART1 的输出。当一个新字节被移入 UART0 的输入移位寄存器时,它被传送到 UART0 数据寄存器并触发 UART0 接收中断。如果您已经为其编写了 ISR,则可以将 UART0 数据寄存器中的字节移至 UART1 数据寄存器,然后设置 UART1 的控制寄存器以开始传输。它的作用是告诉 UART1 外设将您刚刚放入其数据寄存器的任何内容,放入其输出移位寄存器,然后开始将其移出。从这里,您可以从您的 ISR 中返回并返回到您的 uC 在中断之前正在执行的任何任务。现在UART0,在清除其移位寄存器之后,如果在 ISR 期间还没有这样做,则清除其数据寄存器可以开始移入新数据,并且 UART1 正在移出您刚刚放入其中的字节 - 所有这些都发生在当您的 uC 正在执行其他任务时,无需您的干预。整个 ISR 需要几微秒的时间来执行,因为我们只在一些内存中移动 1 个字节,这留下了足够的时间去做其他事情,直到 UART0 上的下一个字节进入(这需要 100 微秒)。并且 UART1 正在移出您刚刚放入其中的字节 - 所有这些都在您的 uC 停止执行其他任务时自行发生而无需您的干预。整个 ISR 需要几微秒的时间来执行,因为我们只在一些内存中移动 1 个字节,这留下了足够的时间去做其他事情,直到 UART0 上的下一个字节进入(这需要 100 微秒)。并且 UART1 正在移出您刚刚放入其中的字节 - 所有这些都在您的 uC 停止执行其他任务时自行发生而无需您的干预。整个 ISR 需要几微秒的时间来执行,因为我们只在一些内存中移动 1 个字节,这留下了足够的时间去做其他事情,直到 UART0 上的下一个字节进入(这需要 100 微秒)。

这就是拥有硬件外围设备的美妙之处——您只需写入一些内存映射寄存器,它就会从那里处理其余的事情,并通过像我上面刚刚解释的那样的中断发出信号来引起您的注意。每次在 UART0 上输入一个新字节时,都会发生此过程。

请注意逻辑捕获中只有 1 个字节的延迟,因为如果您想这样想,我们只会“缓冲”1 个字节。我不确定你是如何得出你的O(2N)估计的——我假设你已经将 Arduino 串行库函数放置在一个等待数据的阻塞循环中。如果我们考虑到必须在 uC 上处理“读取相机”命令的开销,中断驱动方法更像是O(N+c)包含c单字节延迟和“读取相机”指令的地方。鉴于您要发送大量数据(图像数据对吗?),这将非常小。

有关 UART 外围设备(以及 uC 上的每个外围设备)的所有这些细节都在数据表中进行了详尽的解释,并且都可以在 C 语言中访问。我不知道 Arduino 环境是否为您提供了如此低的访问权限,以至于您可以开始访问寄存器 - 就是这样 - 如果没有,你会受到它们的实施的限制。如果你用 C 语言编写它,你就可以控制一切(如果用汇编语言编写更是如此),你可以真正推动微控制器发挥其真正潜力。

为什么不能通过 µC 传输相机数据?我的意思不是缓冲图片,而是通过 µC 中继 UART 数据,以便它可以决定哪些应该发回,哪些不应该发回?

如果您有一个带有两个 UART 的 µC,则最简单,但可以在软件中进行仿真。

我想到了另一种选择,但这对于您的项目来说可能有点笨重和太重:-

  • 为什么不使用连接到小型 USB 集线器的无线 USB 服务器,通过 USB->RS232 适配器为系统的不同部分提供多个控制通道?

是的,笨重,但如果你把它们拆掉,并且可能在可能的情况下使用 USB 而不是 RS232,你可能会逃脱它......