树莓派和 Arduino 机器人入门手册(二)

随笔4个月前发布 易卯
53 0 0

原文:Beginning Robotics with Raspberry Pi and Arduino Using Python and OpenCV

协议:CC BY-NC-SA 4.0

五、树莓派和 Arduino

在第四章中,我们使用 Raspberry Pi 上的 GPIO 引脚与 LED 和超声波传感器进行交互。很多时候,这就足够做你想做的事了。然而,我也讨论了 Raspberry Pi GPIO 的一些缺点,以及可能需要扩展 Pi 的功能来克服这些缺点。

在这一章中,我们将向我们的机器人介绍一个微控制器。微控制器是一种设备,通常是芯片形式,设计为通过输入和输出引脚直接与其他组件一起工作。每个引脚都连接到微控制器的电路,并有特定的用途。

因为引脚直接连接到微控制器敏感的内部工作部件,所以通常需要额外的电路来保证安全工作。许多制造商提供评估板,允许开发人员快速构建原型和概念验证器件。

一种这样的板实际上是由开发者而不是芯片制造商开发的,并提供给公众。由于其易用性、丰富的文档和出色的社区支持,这个设备很快成为了爱好者社区的最爱。当然,我说的是 Arduino。

我们涵盖了很多关于 Arduino 的信息:如何安装软件,编写程序(称为草图),以及将这些程序加载到 Arduino 板上。我们还将介绍如何让您的 Raspberry Pi 和 Arduino 板相互通信。这为你的机器人增加了指数级的能力。

但是在我们进入 Arduino 之前,让我们回顾一下 Raspberry Pi 的一些缺点。

树莓派 的 GPIO 回顾

具体来说,我们来谈谈缺乏足够的模拟和脉宽调制(PWM)引脚。

实时或接近实时的处理

实时处理是系统与 GPIO 和外部设备直接交互的能力。这对于 CNC 应用或其他需要即时响应的应用至关重要。在机器人学术语中,它对于要求对刺激做出即时反应的闭环系统是必要的。

一个很好的例子是移动机器人的边缘检测器。你希望机器人在自己驶下悬崖或桌子边缘之前停止移动。花费时间处理操作系统的许多抽象层以到达决定停止的逻辑,然后通过许多层将信号发送到电机控制器可能证明是灾难性的。而且,如果操作系统延迟操作或挂起,机器人将很高兴地直线下降到它的灭亡,永远不会知道。相反,你希望你的机器人立即停止。

尽管 Linux 有利于接近实时的处理,但这些是特殊的操作系统,我们使用的 Raspbian 安装不在其中。

模拟输入

我们已经看到数字输入在 Pi 上工作。事实上,当一个数字引脚打开然后关闭(变高,然后变低)时,我们使用超声波测距仪来检测范围。通过一点数学运算,我们能够将信号转换成有用的数据。那是一个数字信号;它只是检测一个引脚何时具有高电压,然后检测同一引脚何时具有低电压。

模拟信号有很多种类型;不仅仅是高或低、白或黑、开或关,还有一系列的值——或者用黑/白类比的灰色阴影。当你使用一个传感器来测量物体的强度或水平时,这是非常有用的。使用光敏电阻的光传感器就是一个例子。随着光强度的变化,传感器上的电阻也随之变化,电压也随之变化。一种称为模数转换器(ADC)的设备将模拟信号转换为程序可以使用的数字值。

Raspberry Pi 有一个模拟引脚。这不是很有用,尤其是当它与电路板可能使用的另一个功能相关联时——本例中是串行通信。如果我们将该引脚专用于模拟输入,我们将无法使用该串行通道。即使我们不打算使用那个特定的串行通道,单个模拟输入的用途也非常有限。

模拟输出

模拟输出本质上类似于模拟输入。在我们之前做的 LED 练习中,我们使用数字信号来打开和关闭 LED。模拟允许我们改变 LED 的亮度或强度。然而,数字系统,如计算机或微处理器,不能产生真正的模拟信号。

它调整数字信号的频率和持续时间。数字信号的持续时间称为脉冲。调整一个脉冲在给定时间段内的活动频率以及该脉冲的长度,称为脉宽调制(PWM)。当我们测量来自超声波测距仪的信号时,我们实际上是在测量从设备返回的脉冲。

Raspberry Pi 有四个 PWM 引脚可用。然而,这四个引脚只连接到两个 PWM 过程。这意味着我们只有两个 PWM 通道可用。同样,这并不像我们希望的那样有用。有了实时处理器,我们可以用软件模拟 PWM。然而,如前所述,Raspberry Pi 不是一个实时系统。因此,如果我们想要两个以上的 PWM 通道,我们需要找到另一种解决方案。

Arduino 来救援了

幸运的是,有一类设备是专门设计来实时管理各种类型的输入和输出的。这些是微处理器。有许多类型的微处理器。一些更常见和易于使用的是 AVR ATTiny 和 ATMega 处理器。

然而,这些是芯片,除非你习惯于使用它们,否则它们很难接近和使用。为了使这些设备更容易使用,制造商创造了所谓的开发板。这些板将芯片上的引脚连接到接头,以便于原型制作。他们还增加了使用引脚所需的电子器件,如稳压器、上拉电阻、滤波器电容、二极管等。因此,最后,你所要做的就是将你的特定电子设备连接到设备上,并制作出你的产品原型。

几年前,意大利的一群工程师聚在一起,做了一件前所未有的事情。他们围绕 AVR ATMega 芯片开发了自己的开发板,将设计向公众开放(开放硬件),然后向爱好者和学生推销。他们称这块板为 Arduino。图 5-1 显示了一个典型的 Arduino Uno。我敢肯定,它的预期结果是成为爱好者和制造者社区事实上的标准。

树莓派和 Arduino 机器人入门手册(二)

图 5-1

Arduino Uno

我们将使用 Arduino Uno 搭配我们的树莓派。为什么?首先,它是一个实时处理器。Arduino 直接与引脚和连接的外设通信。不存在操作系统或程序层抽象导致的延迟。第二,它提供了更多的引脚。其中包括我们添加的六个模拟引脚和六个基于硬件的 PWM 引脚。它是“基于硬件”的,因为电路板是实时的,我们可以在任何引脚(顺便说一下,有 20 个)上模拟 PWM 信号(通过软件)。

这只是 Arduino Uno。有一个更大版本的 Arduino 板,称为 Mega。Mega 有 54 个数字引脚和 16 个模拟引脚。这总共是 70 针的 IO 品质。

Arduino 是开放的硬件,这意味着任何人都可以构建这些设计。因此,你会发现许多不同制造商以不同价格推出的许多不同版本。这是一个物有所值的典型例子。如果你是刚刚入门,我建议多花一点钱买一个更可靠的主板。稍后,随着您对故障诊断有了更好的理解和更高的容忍度,您可以尝试更便宜的主板。

使用 Arduino

Arduino 非常容易编程和使用。许多人被与电子和编程硬件一起工作的前景吓住了。但是,这么多人通过 Arduino 开始他们的机器人和物联网事业是有原因的。将设备连接到 Arduino 非常容易,尤其是使用名为 shields 的附件。

对 Arduino 进行编程也非常容易。Arduino 为电路板编程提供了一个接口,简称 Arduino。或者更准确的说,是 Arduino IDE(集成开发环境)。Arduino IDE 使用一种 C 编程风格,也称为 Arduino。如您所见,硬件、软件和开发环境在概念上是一样的。当你谈论 Arduino 编程时,软件和硬件之间没有区别,因为软件的唯一目的是与硬件交互。

在本章中,您需要安装 Arduino IDE,并将 Arduino 连接到您的计算机。假设安装说明和练习在您的 Raspberry Pi 上运行,但是老实说,在另一台机器上安装也同样容易。因此,如果你更喜欢在 Pi 之外的东西上工作,或者你只是不喜欢远程操作,你可以在你的 PC 或笔记本电脑上做所有的练习。

安装 Arduino IDE

在我们将 Arduino 连接到我们的 Raspberry Pi 之前,我们需要安装软件和驱动程序。还好,这个超级容易。安装 Arduino IDE 的同时也会安装使用 Pi 所需的所有驱动程序。

安装 Arduino IDE

  1. 打开终端窗口。
  2. 类型sudo apt-get install Arduino
  3. 对任何提示都回答是。
  4. 去喝一杯。这可能需要惊人的长时间。

安装过程完成后,它会将 Arduino IDE 添加到您的编程菜单中。

连接 Arduino

当我最初概述这本书的这一部分时,我的意图是提供多种方式将 Arduino 连接到 Raspberry Pi。然而,使用除 USB 端口之外的任何东西都会引入另一层复杂性和 Linux 细节,这超出了本文的介绍范围。它实际上是告诉 Pi 您正在激活 UART 引脚,然后禁用许多使用该通道的本机功能。这是一个不必要的过程,特别是因为有四个 USB 端口准备好了,如果你需要更多,你可以随时添加一个 USB 集线器。因此,我们将使用 USB 连接,这样我们就可以专注于 Arduino 的介绍,因为它与 Pi 相关。

要连接 Arduino,我们只需将 Raspberry Pi 的 USB 电缆连接到 Arduino,如图 5-2 所示。根据主板制造商的不同,您可能需要不同的 USB 电缆。因为我用的是原装的 Uno,所以我用的是 USB A-to-B 线。有的人用 USB 迷你线,有的人用 USB micro。

树莓派和 Arduino 机器人入门手册(二)

图 5-2

USB A to B cable connected to the Arduino Uno

就是这样。由于 Arduino 板由您的 Pi 通过 USB 电缆供电,因此您不需要添加外部电源。您正准备开始使用您的 Arduino。接下来,我们要用无处不在的 blink 程序测试你的 Arduino。但首先,我们来看看界面。

编程 Arduino

正如我之前说过的,Arduino 的编程非常简单。然而,由于我们刚刚花了很多时间学习 Python,所以理解一些差异非常重要。

我们将从界面和一些使用它的技巧开始。然后我们将编写一个小程序来说明这种语言的结构和语法。所有这些都是为下一节做准备,在下一节中,我们将更深入地了解 Arduino 编程语言。

Arduino IDE

当你第一次打开 Arduino IDE 时,你会看到一个非常简单的界面(见图 5-3 )。开发人员在开发 Arduino 时采用了编程语言和 IDE 的接口。如果你过去做过编码,这个界面会显得缺乏特色。这既是有目的的,也有点误导。

树莓派和 Arduino 机器人入门手册(二)

图 5-3

Arduino IDE

尽管界面简单,IDE 却异常健壮。最重要的是,它提供了交叉编译,使您在 Linux、Windows 或 Apple 机器上编写的代码能够在更简单的 AVC 处理器上工作。

让我们浏览一下 Arduino IDE 中的一些关键特性和操作。

图标和菜单

由于 Arduino 与众不同,界面顶部工具栏中的图标可能与你所习惯的不同。查看图 5-4 并从左至右移动,图标为编译、上传、新草图、打开、保存,最右边是串行监视器。

树莓派和 Arduino 机器人入门手册(二)

图 5-4

Arduino IDE toolbar

前两个图标非常重要。

Compile 告诉 IDE 处理您的代码,并准备好加载到 Arduino 板上。它运行你的代码,并试图建立最终的机器级程序。此时,它会识别您可能输入的任何错误。Arduino 不提供任何调试功能,所以你很大程度上依赖于编译功能。

上传编译草图,然后上传到板上。因为上传功能首先运行编译器,所以您可以获得与编译功能相同的编译活动,但是,在过程结束时,它会尝试将编译后的代码加载到您的主板上。由于 AVR 处理器一次只能存储和运行一个程序,所以每次你上传到 Arduino 板上,你就覆盖了那里当前的内容。这并不总是可取的。有时你会间歇性地编译代码来检查语法并确保它是正确的。你不会总是想把这些中间步骤加载到板上。

然而,最后,你需要上传你的草图来看看会发生什么。编译草图可以确保你有工作代码。代码是否在做你想让它做的事情是另一回事。上传了你才知道这个。

创建新草图

可以通过单击工具栏中的“新建草图”图标或单击菜单中的“文件”“➤”“新建”来创建新草图。创建新草图总是会打开一个新的 IDE 实例。无论您在之前的窗口中做了什么,它都还在。

第一次打开 Arduino IDE 时,您会看到一个新草图的框架。这也是你后来创建一个的时候看到的。每个 Arduino 草图都包含这些元素。新的草图操作总是用这个框架预先填充 IDE。当我们写第一个草图时,你会看到这些元素是什么。

保存草图

在编译或运行草图之前,您需要保存它。你可以随时保存草图,只是必须在编译或上传之前完成。要保存草图,请单击保存图标或从菜单中选择文件➤保存。

首次保存草图时,系统会自动为其创建一个项目文件夹。这是保存代码文件(扩展名为.ino)的地方。为项目创建的任何其他文件也保存在该文件夹中。当您处理更大、更复杂的程序时,或者当您开始在 IDE 中将草图分成不同的选项卡时,这一点很重要。

打开现有草图

默认情况下,当您打开 IDE 时,您正在处理的最后一个草图会自动打开。当您在同一个项目上工作一段时间时,这很方便。

如果需要打开另一个草图,可以单击菜单栏中的打开草图图标,或者选择文件➤打开。或者,您也可以选择文件➤打开最近。这将列出您最近打开的几个草图。选择其中一个文件将在 IDE 的新实例中打开它。

板和端口选择

正确编译和加载草图的关键是选择合适的电路板和端口。从工具菜单中选择板和端口。

选择主板会告诉编译器您使用的是哪个版本的 Arduino。随着你的 Arduino、机器人和/或物联网经验的增长,你可能会使用不同的主板。Arduino IDE 最大的优点之一就是它的灵活性。您会发现自己正在使用熟悉而舒适的环境为不同制造商生产的大量不同的电路板编程。这有助于 Arduino 成为 maker 社区事实上的标准。

要为您的机器人选择电路板和端口,请确保您的 Arduino 通过 USB 连接,并且 Arduino IDE 已安装并打开。

  1. 从菜单中选择工具➤板。
  2. 从可用主板列表中选择 Arduino/Genuino Uno。
  3. 从菜单中选择工具➤港。列表中应该有一个类似于 TTYAMC0 上的 Arduino/Genuino Uno 的条目。
  4. 选择此条目。

此时,Arduino IDE 应该准备好编译草图并将其加载到您的主板上。我们将写我们的第一个草图来测试它。

举例作弊

当您安装 Arduino IDE 时,您也安装了一组示例草图(参见图 5-5 )。这些是学习 Arduino 编码的极好参考。在你学习的过程中,浏览这些草图,寻找与你试图完成的功能相似的功能。

树莓派和 Arduino 机器人入门手册(二)

图 5-5

List of example code included with the base install

要查看或打开示例列表,请单击“文件”“➤示例”。随着您添加更多的库(如用于传感器和其他设备的库),您将添加到此示例列表中。所以当你扩展你自己的能力,以及你的机器人的能力时,一定要重温这些例子。

使用选项卡和多个文件

当我在前面讨论保存草图时,为单个文件创建一个项目文件夹似乎有点奇怪。这是因为一个项目可以包含多个文件。您可以为一个项目创建多个 Arduino 文件,或者您可能想要将包含的文件与项目放在一起。图 5-6 显示了三个标签打开的 Arduino IDE。

树莓派和 Arduino 机器人入门手册(二)

图 5-6

Arduino IDE with multiple tabs

当项目文件夹中有多个代码文件时,当您打开该项目中的文件时,每个文件在 Arduino IDE 中都显示为一个选项卡。这允许您在工作时轻松地在文件之间导航。

当使用标签和多个文件时,有一些事情需要记住。通过 IDE 创建并保存为 INO 文件的标签中的代码被附加到主 INO 文件的末尾。这意味着您在这些选项卡中创建的任何功能都可以在这些选项卡中使用。但是,除非将文件包含在代码中,否则不在 IDE 中创建的文件的选项卡(如包含文件的选项卡)不可用。这既方便又令人沮丧,因为您需要跟踪特定函数来自哪个文件。

当我们回顾 Arduino 中的编码时,我会在这一章的后面更多地涉及到包含文件。

图 5-7 显示了选项卡管理菜单。

树莓派和 Arduino 机器人入门手册(二)

图 5-7

Tab management menu

您可以创建新的选项卡来帮助组织您的代码。创建新选项卡时,该选项卡将作为新文件保存在项目文件中。

  1. 打开 Arduino IDE 并创建一个新文件。
  2. 保存文件以创建新的项目文件。
  3. 单击 IDE 标签栏中的箭头。
  4. 单击新建选项卡。
  5. 在打开的对话框中,输入选项卡的名称。请记住,这是项目文件夹中新文件的名称。
  6. 保存文件。所有未保存的选项卡也会被保存。

保存标签后,Arduino 会自动创建一个新文件来存储标签中的代码。

草图

Arduino 的程序被称为草图。这个想法就是你只是简单地勾画出代码——就像你在餐馆餐巾上勾画出一个想法一样。老实说,有时候确实会有这种感觉。

你在用一种叫做编程的语言写 Arduino 草图。它是 C 编程语言的精简版本,旨在使编码更容易。Arduino 实际上使用了为 Arduino 板设计的程序的修改版本。它本质上是 AVR 处理器能够运行的精简指令集。

像 Python 一样,当您添加功能和复杂性时,您可以添加库。在 C 语言中,我们使用include指令。它的作用与 Python 中的import命令相同。稍后,当我们在两个电路板之间进行交流时,我们会看到这一点。

你好 Arduino

为了理解编程 Arduino 和 Python 之间的区别,我们将编写一个简单的程序。与 GPIO 章节一样,第一个程序是 Hello World 的硬件版本——一个闪烁的 LED。加载程序后,您将了解更多关于编程、其结构以及如何使用它的知识。

在 GPIO 章节中,我们构建了一个带 LED 的小电路。然而,Arduino 有一个内置在电路板上的 LED 供我们使用,所以我们还不需要打开试验板。LED 与 UNO 上的 13 号插脚相连;其他版本可能会有所不同。

  1. 从编程菜单中打开 Arduino IDE。

  2. 验证板已连接并被检测到。

  3. 在 Arduino IDE 菜单上,转到工具并将鼠标悬停在电路板上。你应该看到 Arduino Uno 被选中。

  4. 现在悬停在串行端口上。它应该显示类似于/dev/ttyUSB0的内容。如果您的 Pi 分配了不同的端口,您的端口可能会有所不同。重点是那里有东西,而且检查过了。

  5. 通过单击菜单外的某个位置关闭工具菜单。

  6. 输入以下代码:

    int ledPin = 13;
    
    void setup() {
            pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
            digitalWrite(ledPin, HIGH);
            delay(1000);
            digitalWrite(ledPin, LOW);
            delay(1000);
    }
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  7. 将文件另存为blink_test

  8. 单击复选框图标以编译草图。

  9. 如果出现任何错误,请确保您输入的代码是正确的。请记住,与 Python 不同,您必须以分号结束每一行。和 Python 一样,大小写很重要。

  10. 当一切编译正确时,单击箭头图标(指向右侧)。这将草图上传到 Arduino。

等待几秒钟,让它上传。之后,您应该会看到连接到引脚 13 的 LED 闪烁。

恭喜你,你刚刚完成了你的第一个 Arduino 程序。另外,你是从你的 Pi 做到的。

素描剖析

我们刚刚写的草图不是最复杂的,但它确实说明了 Arduino 草图的基本结构。

int ledPin = 13;

  • 1
  • 2

我们首先创建一个名为ledPin的整数变量,并给它指定数字 13。给变量起一个有意义的名字是一个好习惯,即使程序很短,只有一个变量。

void setup() {

  • 1
  • 2

然后我们创建了一个名为setup()的函数。这个函数和loop()函数存在于每个 Arduino 草图中。setup功能是你放置预备代码的地方,比如打开串口,或者,正如我们在这个草图中所做的,定义我们如何使用管脚。setup功能在程序开始时运行一次。

pinMode(ledPin, OUTPUT);

  • 1
  • 2

setup函数中,我们有一个单独的命令。pinMode函数告诉编译器如何使用一个管脚。在这种情况下,我们声明ledPin(值为 13)为输出引脚。这告诉编译器我们正在发送信号,并且我们不期望通过这个管脚接收信号。

然后,在开始我们的loop函数之前,我们用右括号关闭setup函数。

void loop() {

  • 1
  • 2

loop功能是 Arduino 草图唯一需要的元素。顾名思义,loop是连续重复运行,直到板卡断电,或者板卡复位。它相当于 Python 中的while true:命令。loop函数中的任何代码都会以处理器所能处理的最快速度重复。

digitalWrite(ledPin, HIGH);

  • 1
  • 2

loop函数中,我们有闪烁 LED 的代码。我们首先使用digitalWrite功能将引脚设置为高电平状态。同样,我们传递它ledPin和我们想要设置的状态——在本例中是高。

delay(1000);

  • 1
  • 2

下一行在执行下一个命令之前增加了 1,000 毫秒或 1 秒的延迟。

digitalWrite(ledPin, LOW);

  • 1
  • 2

一秒钟延迟后,我们使用与设置高电平相同的命令digitalWrite将引脚设置为低电平状态。然而,这一次,我们通过它的常数低。

delay(1000);

  • 1
  • 2

同样,我们引入了一秒钟的延迟。因为这是loop功能中的最后一个命令,所以延迟后我们返回到loop功能的开始。直到我们拔掉 Arduino 或上传另一张草图。

Arduino 语言简介

如前所述,Arduino 编程语言是从编程语言派生出来的。反过来,编程植根于 C 语言。如果你熟悉 C 语言的编码,Arduino 很容易使用。Arduino 中的许多功能、语法和快捷键与 c 语言中的一样好用。

其余的,你很快就能理解。请记住,Arduino 不是 Python,当您使用它时,它的行为会有很大不同。

例如,Arduino 比 Python 更不关心空白和格式,Python 使用缩进来表示代码块。在 C 语言中,代码块是用花括号{}定义的。也就是说,你不能完全忽略空白。在行首多一个空格会导致无穷无尽的挫败感。

另一个让初学者和经验丰富的程序员都感到沮丧的关键区别是行终止。在 Python 中,您只需移动到下一行,不需要终止符。然而,在 Arduino 和 C 中,行以分号结束。如果编译器期望的地方没有分号,就会出现错误。这是初学者最常犯的错误。如果您的代码无法编译,首先要寻找的是丢失的分号。

Python 和 Arduino 的一个共同点是区分大小写。记住大小写很重要。intPinintpin不一样。如果您的代码没有正确编译或没有如预期的那样运行,这是第二件要寻找的事情。

包括其他文件

与 Python 非常相似,有时您需要包含其他文件或库。当您向 Arduino 添加传感器、电机或其他设备,并且需要将设备库添加到您的代码中时,这是最有可能的。

Arduino 使用 C 和 C++方法,通过#include指令从外部文件添加代码。下面一行包括标准伺服库:

#include <Servo.h>

  • 1
  • 2

像所有指令一样,include的语法略有不同。注意,这一行的末尾没有分号。分号会导致错误,并且代码不会编译。另外,include关键字前面有一个#(散列)。

变量和数据类型

像 Python 一样,Arduino 拥有所有常见的数据类型,尽管它们的行为可能略有不同。Python 和 Arduino 最大的区别之一是,你必须在使用变量之前声明它。一个很好的例子是for循环。在 Python 中,您可以这样做:

for i in range (0, 3):

  • 1
  • 2

在 C 和 Arduino 中,循环如下:

for (int i = 0; i < 3; i ++) {... }

  • 1
  • 2

这些说法大相径庭。我将在本章后面解释for循环语法。

这里要注意的关键是,在 Python 中,i变量是在没有类型的情况下创建的,当第一个值 0 赋给它时,它就变成了一个整数。在 Arduino 中,在给变量赋值之前,你必须告诉编译器这个变量是什么;否则,您会收到类似以下内容的错误:

Error: variable i not defined in this scope

  • 1
  • 2

声明变量的规则与 Python 相同,最佳实践也相同。

  • 变量只能包含字母、数字和下划线。
  • 它们区分大小写;variableVariable不一样。那以后会咬你一口的。
  • 不要使用 Python 关键字。
  • 用尽可能少的字符使变量名有意义。
  • 使用小写的 L 和大写的 O 时要小心,它们看起来很像 1 和 0,会导致混淆。我不是说不要使用它们;确保你清楚自己在做什么。强烈建议不要将它们用作单字符变量名。
字符和字符串

琴弦有三种味道;字符、作为字符数组的字符串和作为对象的字符串。每一个都以截然不同的方式处理。

字符(char)是存储为 ASCII 数值的单个字母数字字符。记住,计算机是以 1 和 0 工作的,所有的东西最终都会被分解成以 1 和 0 存储的数字。ASCII 码是代表单个字母数字字符的数值。例如,字母a实际上是 ASCII 码 97。即使不可见的字符也有 ASCII 表示。回车的 ASCII 码是 13。您经常会看到使用与char函数相同的符号来编写这些函数,例如char(13)

一个字符串可以用两种不同的方式处理。处理从 C 继承的字符串的本机方法是字符数组。您可以像这样声明这种类型的字符串:

string someWord[7];

  • 1
  • 2

或者

string someWord[] = "Arduino";

  • 1
  • 2

这将创建一个由 10 个字符组成的字符串,存储为一个数组。我们很快会学到更多关于数组的知识,但它们大致相当于 Python 列表。要访问这种类型的字符串中的字符,可以使用它在数组中的位置。someWord[0]示例返回字符 a。

字符串对象

尽管有时您可能希望以我刚才解释的方式操作字符和字符串,但 Arduino 提供了一种更方便的处理字符串的方式:String对象。注意大写的 s。

String对象提供了许多处理文本和将其他值转换成字符串的内置方法。使用简单的数组操作可以很容易地重新创建这些函数。String对象只是让它变得更容易;然而,如果你不打算做大量的字符串操作,这可能是多余的。

对字符串操作有用的函数的例子有trim()toUpperCase()toLowerCase()

有几种方法可以创建一个String对象。因为它是一个对象,所以您必须创建一个String对象的实例。对象的实例化方式通常与声明任何其他变量的方式相同。事实上,由于所有的数据类型本质上都是对象,所以完全相同。例如,这就是如何初始化一个名为myStringString对象的实例:

String myString;

  • 1
  • 2

或者

String myString = "Arduino";

  • 1
  • 2
民数记

像 Python 一样,有几种可用的数字格式。最常见的是整数(int)和小数(浮点)。您偶尔会使用布尔类型和一些其他类型。

整数表示–32,768 到 32,767 之间的 16 位数字。无符号整数可以保存 0 到 65,535 之间的正值。长整数(long)是一个从–2,147,483,648 到 2,147,483,647 的 32 位数字。所以根据你需要的数量,你有几个选择。

小数或非整数存储为浮点类型。浮点是从–3.4028235E+38 到 3.4028235 e+38 的 32 位数字。像 Python 一样,Arduino 中的浮点不是原生的,只是近似的。但是它们在 Arduino 中比在 Python 中更精确。

以下代码说明了如何在 Arduino 中创建数字变量:

int myNumber;
int myNumber = 10;
long myLongInt;
long myLongInt = 123456;
float myFloat;
float myFloat = 10.1;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

一定要注意每行末尾的分号。除了代码块以外,每一行代码都必须以分号结束。

数组

如前所述,数组本质上与 Python 中的列表相同。它们用括号([ ])表示。在数组中寻址一个值的工作方式与在 Python 中完全一样。Arduino 数组也是从零开始的,这意味着数组中的第一个值位于位置 0。

下面的示例创建一个数组,遍历这些数组,然后将一些值输出到串行端口。

  1. 在 Arduino IDE 中创建新的草图。

  2. 将草图另存为array_example

  3. 更新代码,如下所示:

    int numbers[5];
    int moreNumbers[5] = {1,2,3,4,5};
    
    void setup() {
      // put your setup code here, to run once:
    Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    for(int i = 0; i < 5; i++){
      Serial.println(numbers[i]);
      }
    
    for(int i = 0; i < 5; i++){
      numbers[i] = moreNumbers[i];
    }
    
    for(int i = 0; i < 5; i++){
      Serial.println(numbers[i]);
      }
    
    numbers[1] = 12;
    
    for(int i = 0; i < 5; i++){
      Serial.println(numbers[i]);
      }
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  4. 保存文件。

  5. 将草图上传到你的 Arduino。

  6. 单击工具➤串行监视器。

控制结构

像 Python 一样,Arduino 提供了几个结构来给你的代码增加一些控制。这些应该相当熟悉,因为它们与 Python 中的对应部分非常相似。当然,语法是不同的,你需要注意你的分号和括号。

如果和否则

这通常被认为是最基本的构造。它只是允许您根据布尔条件的结果执行代码。如果条件评估为真,则代码执行;否则,程序跳过代码并执行下一个命令。下面是一个if语句的例子:

if(val == 1){doSomething();}

  • 1
  • 2

在这个例子中,我们简单地评估了变量val的内容。如果val包含整数 1,则执行括号内的代码;否则,程序跳过代码并继续下一行。

整个子句不需要,并且通常不局限于一行。一般来说,即使括号中的代码只有一行,我也会将语句扩展为使用多行。我只是觉得这更容易阅读。这段代码在功能上与前面的示例相同。

if(val == 1){
        doSomething();
        }

  • 1
  • 2
  • 3
  • 4

您可以使用else语句计算多个值,它的工作方式与您预期的完全一样。您只是告诉编译器,如果前一个条件的计算结果为 false,就要计算每个连续的条件。

if(val == 1){
        doSomething();
}
else if(val == 2){
        doSomethingElse();
}
else if(otherVal == 3){
        doAnotherThing();
}
else {
        doAlternateThing();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这段代码的第一部分与前面的例子相同。如果val的值是 1,那么做点什么。如果这个条件为假,val不为 1,那么检查一下是否为 2。如果是,那就做点别的。如果也不成立,那么检查otherVal的值。如果是 3,那么做另一件事。最后,如果前面的条件都不成立,那么执行下面的代码。

最后的else语句是不必要的。您可以省略这个语句,代码会继续运行后面的任何代码。最后一个else语句是针对那些你只希望在所有其他条件都不成立的情况下运行的代码。

另外,请注意第二条else/if语句。您不必为另一个条件计算同一个变量。任何评估为 true 或 false 的运算都是有效的。

while 循环

只要条件为真,循环就会重复执行一段代码。在 Python 中,我们用它来创建一个连续的循环,不断地执行我们的程序。这种做法在 Arduino 中是不必要的,因为标准的Loop()函数提供了这种功能。

if语句一样,while评估一个条件。如果条件评估为真,则执行代码块。一旦代码块执行,它将再次计算条件。如果条件仍然为真,则再次执行代码块。这将持续到条件评估为假。因此,确保在条件中计算的值在代码块中得到更新非常重要。

这是一个while循环的例子:

int i = 0;

while(i < 3){
        doSomething();
        i++;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这个例子中,我们在进入while循环之前创建了一个值为 0 的整数。while语句计算值 I。由于它当前是 0,小于 3,所以它执行代码块。在代码块中,如果 I,while语句再次计算这个值,我们就增加这个值。这次是 1,仍然小于 3,所以再次执行代码块。这一直持续到i的值增加到 3。因为 3 不小于 3,所以while循环不执行代码块就退出。

像所有其他循环一样,while循环正在阻塞。这意味着只要条件评估为真,代码块就执行,防止任何其他代码被执行。

此功能通常用于防止代码在条件出现之前运行,以防止以后出现错误或意外结果。例如,如果您的代码需要串行连接才能继续,您可以将此代码添加到您的程序中:

Serial.begin(9600);
while(!Serial){}

  • 1
  • 2
  • 3

Serial函数是标准 Arduino 库的一部分。它只是检查串行连接是否可用。如果串行连接已经建立,Serial评估为真。然而,它前面的惊叹号(!)表示不是。所以我们说,“只要没有串行连接,就执行这段代码。”代码块为空,因此没有要运行的代码。结果是代码停止,直到串行连接可用。

对于循环

while循环一样,for循环重复执行一个代码块,直到条件评估为真。这两者的区别在于,for循环也定义和转换被评估的变量。一般来说,这只是设置一个整数作为计数器,根据设置的阈值评估该值,然后递增该值。随着每一个增量,代码块被执行,直到条件不再评估为真;例如:

for(int i = 0; i < 3; i++){
        doSomething();
}

  • 1
  • 2
  • 3
  • 4

在这个例子中,我们声明了一个名为i的整数。只要 I 小于 3,我们就希望继续循环代码块。每次执行代码时,I 的值都会增加 1,直到 I 的值为 3。因为 3 不小于 3,所以循环在不执行代码块的情况下退出。

当您希望执行一段代码特定的次数时,这很有用。您也可以使用递增的值。例如,如果我们希望引脚 13 上的 LED 逐渐变亮,而不是简单地打开,我们可以这样编码:

pinMode(11, OUTPUT);
for(int i = 0; i < 255; i++){
        analogWrite(11, i);
}

  • 1
  • 2
  • 3
  • 4
  • 5

首先,我们告诉 Arduino,我们想使用引脚 13 作为输出引脚。稍后您将了解更多关于使用大头针的信息。然后我们设置我们的for循环,将i的值从 0 增加到 254。然后i的值被写入引脚 13,设置 PWM 值。回想一下上一章,PWM 值通过确定给定周期内引脚为高电平的频率来控制 LED 的亮度。因此,我们有一个增加到最大亮度的 LED。

当我们开始处理引脚时,我们实际上编写了 LED 衰减代码。

功能

像 Python 一样,Arduino 允许你通过函数将代码分解成更小的部分。Arduino 函数与 Python 中的函数非常相似。当然,语法是不同的。但是,使用这两种方法,您需要声明函数名,列出任何需要的参数,并提供调用函数时要执行的代码块。

您已经熟悉了 Arduino 函数的语法。Arduino 草图中的setuploop块都是函数。唯一的区别是这些是在运行时自动调用的系统函数。如果你熟悉 C 或 C++,它们类似于这些语言的main()函数。

当你有一段可能要在多个地方使用的代码时,你就可以使用函数。这样,您只需编写一次,无论您从哪里调用它,它总是执行相同的操作。

函数的一般语法如下所示:

returnType functionName(parameterType parameterName){
        doSomething();
}

  • 1
  • 2
  • 3
  • 4

引导您完成函数的创建和使用可能更好也更容易。

在本练习中,我们将创建一个简单的函数,将两个数字相加。这不是一个特别实用的函数,但是它提供了一个如何创建函数的例子。

  1. 在 Arduino IDE 中创建新的草图。

  2. 将草图另存为function_example

  3. 将代码更新为:

    int a = 1;
    int b = 2;
    int val;
    int answer;
    
    int add_vars(){
      val = a+b;
      return val;
    }
    int add_params(int p1, int p2){
      val = p1+p2;
      return val;
    }
    
    void printVal(){
      Serial.println(val);
    }
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      add_vars();
      printVal();
    
      add_params(a,b);
      printVal();
    
      answer = add_vars();
      Serial.println(answer);
    
      a++;
      b++;
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  4. 将草图上传到你的 Arduino。

  5. 从“工具”菜单打开串行监视器。

在本练习中,我们创建了三个函数。前两个函数返回一个类型为int的值。因此,我们在函数名前面加上数据类型int。第三个函数不返回数据;它只是执行一个任务,所以前面有void

第一个函数add_vars(),将两个全局变量相加。这强调了全局变量的好处和危险。程序中的任何代码都可以操作全局变量。这是对相同数据执行任务,然后将数据从一个函数传递到另一个函数的简单方法。但是,您必须意识到,您对变量所做的任何更改都会应用到使用该变量的任何地方。

一个更安全的替代方法是在函数中使用参数。这样,您就有了更多的控制权,因为您正在提供这些值。第二个函数add_params()演示了这一点。参数是作为函数声明的一部分创建的。我们提供了每个变量的数据类型以及函数中使用的变量名。因此,这就像声明一个变量,除了在运行时函数被调用时赋值。

最后一个函数不返回任何数据,也不需要任何参数。在这个特殊的例子中,我们将全局变量val的值打印到串行端口。

使用大头针

Arduino 的主要用途是与其他组件、传感器或其他设备连接。为此,我们需要知道如何与引脚交互。Arduino 的引脚直接连接到 AVR 处理器的核心。

Arduino 提供对 14 个数字引脚、6 个模拟引脚、6 个硬件 PWM 引脚、TTL 串行、SPI 和双线串行的访问。我强调硬件 PWM,因为任何数字或模拟引脚都可以用于软件 PWM。我不会在本书中涵盖所有这些功能,但是我建议您花时间了解它们。

我们将看看您的基本数字和模拟输入和输出。这些是您最常用的功能。

在使用任何管脚作为输入或输出之前,必须首先声明如何使用它。这是使用pinMode()功能完成的。为此,您只需提供 pin 码和模式。例如,此代码将引脚 13 设置为输出引脚:

pinMode(13, OUTPUT);

  • 1
  • 2

我经常使用变量来表示 pin 号。这使得识别您在代码中所做的事情变得更加容易;例如:

int servoPin = 11;
int LEDPin = 13;

  • 1
  • 2
  • 3

现在,当我需要引用一个引脚时,就更容易理解了。

pinMode(LEDPin, OUTPUT);

  • 1
  • 2
数字操作

现在我们已经定义了 pin,我们可以开始使用它了。

与 Python 一样,可以通过将引脚设置为高或低来打开或关闭引脚。这是通过使用digitalWrite()函数来完成的,使用该函数,您可以提供 pin 号以及高电平或低电平;例如:

digitalWrite(LEDPin, HIGH);

  • 1
  • 2

pinMode()为例,这将使引脚 13 变为高电平或开启。

同样,您可以通过将引脚设为低电平来关闭引脚。

另一方面,您可以使用digitalRead()读取引脚的当前状态。为此,首先必须将模式设置为输入。

int buttonPin = 3;
int val;
pinMode(buttonPin, INPUT);
val = digitalRead(buttonPin);

  • 1
  • 2
  • 3
  • 4
  • 5

这段代码片段将值 3 赋给buttonPin变量,我们创建一个变量来存储结果。然后,它将引脚模式设置为输入,以便我们可以读取它。最后,我们将引脚 13 的值读入val变量。

模拟输入

模拟输入的工作方式略有不同;虽然可以使用任何 IO 引脚进行数字操作,但只能使用指定的模拟引脚进行模拟输入。正如我在 Python 介绍中所讨论的,微控制器不能真正模拟。不管怎样,信号必须在模拟和数字之间转换。对于模拟输出,这是通过脉宽调制(PWM)完成的。对于模拟输入,我们使用模数转换器(ADC)将模拟信号转换为数字信号。这是一项硬件功能,因此必须在特定的引脚上执行。对于 Arduino Uno,这些引脚是 A0 至 A5。

由于这些引脚专用于模拟输入,因此没有必要将其声明为输入。我仍然建议这样做,因为这表明这些引脚正在使用中。

analogRead()函数用于读取管脚;例如:

val = analogRead(A0);

  • 1
  • 2

这将 A0 的值赋给变量val。这是一个 0 到 1023 之间的整数值。

模拟输出(PWM)

PWM 的工作方式与 Python 中的基本相同。在指定的管脚上,可以提供 0 到 255 之间的值来改变管脚的输出。值 0 相当于数字低电平或关闭;而值 255 类似于数字高或开。因此,值 127 提供 50%的占空比,大致相当于一半功率。

使用 Arduino,您可以使用analogWrite()来设置引脚的 PWM 信号。在 Arduino Uno 上,PWM 引脚为 5、11、12、15、16 和 17。以下代码片段将引脚 11 的输出设置为大约 25%。

int PWMPin = 11;
pinMode(PWMPin, OUTPUT);
analogWrite(PWMPin, 64);

  • 1
  • 2
  • 3
  • 4
脉冲 LED

在本练习中,我们将制作一个 LED 脉冲。引脚 13 不是 PWM 引脚,所以这次我们不能使用内置 LED,这意味着是时候断开试验板和一些跳线了。

该电路

为了连接电路,我们需要一个 220 欧姆的电阻、一个 5V 的 LED、你的 Arduino、试验板和一些跳线。参见图 5-8 来连接该练习。

树莓派和 Arduino 机器人入门手册(二)

图 5-8

LED fade exercise circuit layout

  1. 将 LED 连接到试验板。
  2. 连接电阻,使其一端连接到与 LED 长引脚共享的通道。
  3. 将一根跳线从二极管的另一个引脚连接到 Arduino 上的 GND 引脚。
  4. 将一根跳线从电阻器的另一端连接到 Arduino 上的第 11 针。
代码

之前我们在一个for循环示例中使用了analogWrite()。现在我们编写代码在 Arduino 上实现这个例子。

  1. 在 Arduino IDE 中创建新的草图。

  2. 将草图另存为PWM_Example

  3. 将代码更新为:

    int PWMPin = 11;
    
    void setup() {
      // put your setup code here, to run once:
      pinMode(PWMPin, OUTPUT);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      for(int i = 0; i < 255; i++){
        analogWrite(PWMPin, i);
      }
    
      for(int i = 255; i >= 0; i$$){
        analogWrite(PWMPin, i);
      }
      delay(100);
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  4. 保存草图并上传到 Arduino。

试验板上的 LED 现在应该会闪烁。要改变脉冲的速率,改变延迟功能中的值。

对象和类

创建对象和类超出了本书的范围。你很少需要在 Arduino 中创建一个。但是,您经常使用其他库中的对象或类。

对象的实例化方式通常与声明任何其他变量的方式相同;你告诉编译器对象的类型,后跟引用它的变量的名字。

ObjectType variableName;

  • 1
  • 2

一旦声明,您就可以访问该类的所有属性和方法。伺服类就是一个很好的例子。这是一个带有 Arduino 的标准库。以下代码片段创建了一个伺服对象,并将其附加到第 12 号插针:

#include <Servo.h>

Servo myServo;
myServo.attach(12);

  • 1
  • 2
  • 3
  • 4
  • 5

首先,我们包括伺服库。一旦包含了伺服库,我们可以很容易地创建一个Servo类的实例。在本例中,我们创建了一个名为myServoservo对象。一旦创建了对象,我们就可以使用attach()方法来分配引脚 12 来控制伺服。

连续的

Arduino 上有几个串行通道。我们在 Raspberry Pi 和 Arduino 之间使用 USB 连接。这是目前为止两者之间最简单的沟通方式。

连接到串行

要使用串行通信,您必须首先启动它。为此,使用Serial.begin(baudRate)。例如,此行以 9600bps 的波特率启动串行连接:

Serial.begin(9600);

  • 1
  • 2

您选择的波特率完全取决于您和您的需求。重要的是,它与计算机连接的波特率相匹配。因此,当您初始化 Pi 上的串行连接时,您需要确保它们匹配。我稍后将讨论如何建立这种连接。

要验证串行连接是否成功,您可以查询Serial关键字。Serial 是一个布尔对象,表示串行连接是否可用。如果连接可用,则为真;否则就是假的。使用Serial其实有几种方法。您可以将它用作一个if语句的布尔条件,并将相关代码放在if语句的代码块中。或者,你可以用它作为一个while循环的布尔条件。

这里有两种方法来检查串行连接。如果有可用的代码,只运行代码。

if(Serial){
        doSomething();
}

while(Serial){
        doSomething();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果串行连接可用,第一个块执行代码,然后继续执行if语句后面的代码。只要有连接,第二个块就会持续运行代码。任何跟随while循环的代码将不会运行,直到串行连接终止并且循环退出。

第三种方法是在连接不可用时暂停所有代码的运行。这是我们之前见过的另一个while循环。

while(!Serial){}

  • 1
  • 2

这使用了“非”运算符,或感叹号(!)。为了使条件评估为真,它必须不满足标准。在这种情况下,只要连接不可用,就执行块中的代码。但是,由于没有代码,它只是暂停程序,直到有可用的代码。

发送串行数据

我们要做的大部分工作只是简单地打印到串口。事实上,这就是我们在前面的例子中所做的。方法Serial.println()将括号内的数据发送到串口。Arduino IDE 中的串行监视器允许您查看此输出。

要将数据写入串行流,通常使用串行打印方法之一。Serial.print()将括号中的内容打印到串行流中,不带新的行结束符。这意味着使用这种方法打印的所有内容都出现在串行监视器的同一行上。

Serial.println()方法包括新的行结束符。所以用这种方法打印出来的东西后面都是新的一行。

接收串行数据

当然,串行端口的工作方式也是相反的。您可以使用 serial 对象的几种方法从 Pi 中读取串行流。许多从串行读取数据的方法都是针对单个字节的。如果您刚刚开始,这可能会令人困惑和麻烦。如果您熟悉并习惯于处理单个字节的数据,那么Serial.read()Serial.readByte()和其他一些可能会有用。

然而,这不是我们要用的。为了使事情简单一点,我们将使用Serial.parseInt()Serial.readString()方法。当从串行流中读取时,这两种方法完成了大部分工作。

Serial.parseInt()读取传入的串行流并返回;但是,它不会一次解析所有的整数。当您第一次调用它时,它返回遇到的第一个整数。下一个调用返回下一个整数。每次迭代返回找到的下一个整数,直到到达行尾。

我们来看看parseInt()是如何工作的。在下面的代码中,Arduino 等待接收来自串行流的输入。然后,它遍历输入并解析出整数,将每个整数打印在新的一行上。

  1. 在 Arduino IDE 中打开一个新的草图。

  2. 将草图另存为parseInt_example

  3. 输入此代码:

    int val;
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      while(Serial.available() > 0){
        val = Serial.parseInt();
        Serial.println(val);
      }
    }
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  4. 将草图上传到你的 Arduino。

  5. 打开串行监视器。

  6. 在串行监视器顶部的数据输入字段中,输入 1,2,3,4。请确保用逗号分隔每个值。

  7. 单击发送。

串行监视器将每个整数写在新的一行上。如果您输入一个字母字符,它会打印一个 0,因为它是一个字母数字字符,而不是一个整数。

Serial.readString()将串行流中的整行作为字符串读取。这可以分配给一个String变量以备后用。如果您正在向 Arduino 发送文本信息,这种方法非常有效。但是,它很慢,并且您会注意到发送一行的时间与接收、处理和提供该行的时间之间有很大的延迟。

Arduino 到 Pi,然后再回来

你需要知道一些关于串行通信的知识,因为这是我们在 Raspberry Pi 和 Arduino 之间通信的方式。Pi 和 Arduino 处理串行通信的方式不同。

我没有在 Python 这一章中讨论串行,因为结合 Arduino 进行讨论是很重要的。因此,在所有 Arduino 编码之后,你可能想跳回到第三章来快速回顾一下 Python。

我已经谈到了如何在 Arduino 上打开串行连接。树莓派只是稍微复杂一点。首先,串行通信不是默认框架的一部分。所以,我们需要安装它。安装后,我们的代码需要导入串行库。一旦完成,我们就创建了一个 serial 类的实例,它让我们可以访问我们需要的方法。

安装 PySerial

PySerial 包提供了串行功能。要使用它,首先需要确保它安装在 Python 实现中。

  1. 在你的 Raspberry Pi 上,打开一个终端窗口。
  2. 类型python –m pip install pyserial。如果尚未安装 PySerial 包,这将安装它。
  3. 类型python。这将在终端中开始一个新的 Python 会话。
  4. 类型import serial。这将验证您的 PySerial 版本。

现在 PySerial 已经安装好了,我们可以在我们的程序中使用它了。

要在 Python 中使用 serial,我们需要导入库并创建一个连接。以下代码片段可能出现在大多数与 Arduino 交互的脚本中:

import serial
ser = serial.Serial('/dev/ttyAMC0', 9600)

  • 1
  • 2
  • 3

在 Python 中创建串行连接类似于我们在 Arduino 中所做的。最大的不同是我们将串行对象赋给了一个变量;在这种情况下,ser。在初始化调用中,我们提供 Arduino 所在的端口以及我们正在连接的波特率。同样,确保这与您在 Arduino 上设置的波特率相匹配。如果这些不匹配,您会得到奇怪的字符和意想不到的结果——如果您得到任何东西的话。

向 Raspberry Pi 发送数据

这与其说是关于向 Pi 发送数据,不如说是关于 Pi 如何接收数据以及如何处理数据。

在 Pi 上接收串行数据的最简单方法是使用串行对象的readLine()方法。这会从串行流中读取字节,直到它到达新的行字符。然后这些字节被转换成一个字符串。线路上发送的所有数据都存储在一个字符串中。根据从 Arduino 发送数据的方式,您可能需要使用split()方法将数据解析成一个元组。

值得注意的是,readLine()方法继续读取串行流,直到接收到一个新的行字符。如果不从 Arduino 发送,Pi 会继续尝试读取数据。为了帮助防止锁定您的程序,您可能希望在尝试readLine()之前设置超时间隔。这可以通过在创建连接时添加超时参数来实现。以下代码行创建了一秒钟超时的串行连接:

ser = serial.Serial('/dev/ttyAMC0', 9600, timeout=1)

  • 1
  • 2

我在 Pi 和 Arduino 之间发送数据的首选方法是通过一系列逗号分隔的值。根据项目的复杂程度,我可以直接读取,其中传递的每个值对应一个特定的变量。这样做的好处是非常简单。我所要做的就是将串行流解析成整数,并将每个整数按顺序分配给各自的变量,以备后用。

在更复杂的项目中,我可能会成对或成组地发送值。解析时,第一个整数通常表示消息的功能或设备;第二个是分配给变量的值。

在 Arduino 中,我简单地将值和它们的逗号分隔符写在许多以Serial.println()结尾的Serial.print()命令中,以确保该行被正确终止。

在 Pi 上,我使用readLine()方法将整行捕获为一个字符串,然后使用split()方法将该字符串解析为一个元组。可以根据需要将元组进一步解析为单个变量。

为了说明这一点,让我们创建一个简单的程序,每隔 500 毫秒从 Arduino 向 Raspberry Pi 发送一个数字序列。这足够频繁,不会超时。

在 Pi 上,我们解析这些值,并将它们分配给单独的变量。这是将传感器读数从 Arduino 发送到 Pi 的常见用例。

为此,我们必须编写两个程序:一个用于 Arduino,一个用于 Pi。让我们从 Arduino 开始。

  1. 在 Arduino IDE 中创建新的草图。

  2. 将草图另存为Arduino_to_Pi_example

  3. 输入以下代码:

    int a = 1;
    int b = 2;
    int c = 3;
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      while(!Serial){};
      Serial.print(a); Serial.print(",");
      Serial.print(b); Serial.print(",");
      Serial.println(c);
      delay(500);
      a++;
      b++;
      c++;
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  4. 保存草图并上传到 Arduino。

  5. 在空闲时打开一个新的 Python 文件。

  6. 将文件另存为Arduino_to_pi_example.py

  7. 输入以下代码:

    import serial
    
    ser = serial.Serial('/dev/ttyACM0',9600,timeout=1)
    
    while 1:
        val = ser.readline().decode('utf-8')
        parsed = val.split(',')
        parsed = [x.rstrip() for x in parsed]
        if(len(parsed) > 2):
            print(parsed)
            a = int(int(parsed[0]+'0')/10)
            b = int(int(parsed[1]+'0')/10)
            c = int(int(parsed[2]+'0')/10)
        print(a)
        print(b)
        print(c)
        print(a+b+c)
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  8. 保存并运行文件。

在空闲 shell 窗口中,您应该会看到类似如下的输出:

['1','2','3']
1
2
3
6

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们在这里做了一些需要回顾的 Python 魔术。

首先,我们导入串行库并打开一个串行连接。一旦连接打开,我们就进入了永久的while循环。在那之后,我引入了一些新的元素,

val = ser.readline().decode('utf-8')

  • 1
  • 2

我们阅读来自串行流的下一行。但是,该字符串是作为字节字符串检索的,这与标准字符串的工作方式不同。为了更容易使用,我们使用了decode()方法将字符串从字节字符串转换为标准字符串。这允许我们使用 string 类的方法来处理行。

parsed = val.split(',')

  • 1
  • 2

接下来,我们将字符串解析成一个列表。因为我们使用逗号将我们的数字与 Arduino 分开,所以将其提供给split()方法。然而,现在列表中的最后一个元素包含了行尾字符/n/r。我们不想要那些角色。

parsed = [x.rstrip() for x in parsed]

  • 1
  • 2

这一行重建没有额外字符的解析列表。rstrip()方法从字符串中删除任何空白。所以,这一行所做的是遍历列表中的每个成员,并应用rstrip()方法。留给我们是字符串形式的数字列表。

if(len(parsed) > 2):

  • 1
  • 2

两块板之间的串行通信面临的挑战之一是丢包。这在我们重置 Arduino 时尤其普遍,每次我们建立新的串行连接时都会发生这种情况。这种丢失会导致串行字符串中丢失字符。

为了克服这个挑战,我们在这个脚本中测试了列表的长度。函数的作用是:返回列表中成员的数量。因为我们知道我们的列表至少需要包含三个数字,所以我们只想在这个条件为真的情况下运行剩余的代码。

print(parsed)

  • 1
  • 2

这一行只是将解析后的列表打印到 shell 窗口。

a = int(int(parsed[0]+'0')/10)
b = int(int(parsed[1]+'0')/10)
c = int(int(parsed[2]+'0')/10)

  • 1
  • 2
  • 3
  • 4

当我们将值赋给它们各自的变量时,Python 的最后一个魔术就完成了。这些行包括字符串和数字操作。

为了理解这里发生的事情,我们必须从中间开始,在这里我们将字符'0'添加到每个列表成员的末尾。我们这样做是因为,尽管我们之前做了努力,列表中可能仍然有空字符串。空字符串不能转换为整数,代码也不会编译。通过加上 0,我们确信这里有一个实际值。

然后,我们将该字符串转换为整数。但是,该整数现在在末尾附加了一个 0,使 1 读作 10,依此类推。为了对此进行调整,我们将该数字除以 10,得到一个浮点数。因为我们在寻找一个整数,所以我们必须将最终结果转换成一个int

代码的最后一部分只是将每个变量的值打印到 shell 窗口。最后一行用来证明我们操作的是整数而不是字符串。

向 Arduino 发送数据

在 Arduino 方面,向 Arduino 发送数据是一件相当简单的事情。然而,Python 稍微复杂一些。使用与前面相同的场景,我们需要将值放入一个元组,然后使用join()方法将元组中的值合并成一个字符串。然后,该字符串被写入串行连接。

在 Arduino 上,我们所要做的就是再次使用parseInt()将字符串分成三个独立的整数。

在本练习中,我们将向 Arduino 发送三个整数。在现实世界中,这些数字可能代表 LED 的颜色或亮度或伺服角度。然而,这将很难验证 Arduino 端发生了什么,因为我们不能使用串行监视器。为了克服这个问题,我们将要求 Arduino 对整数求和,并将结果返回给 Pi。这意味着两块板都在读写串行流。

还是那句话,让我们从 Arduino 这边开始。

  1. 在 Arduino IDE 中打开一个新的草图。

  2. 将草图另存为roundtrip_example

  3. 输入以下代码:

    int a = 0;
    int b = 0;
    int c = 0;
    int d = 0;
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      while(!Serial){}
      if(Serial.available()>0){
        a = Serial.parseInt();
        b = Serial.parseInt();
        c = Serial.parseInt();
      }
    
      d = a+b+c;
      Serial.print(a); Serial.print(",");
      Serial.print(b); Serial.print(",");
      Serial.print(c); Serial.print(",");
      Serial.println(d);
    
      //delay(500);
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  4. 保存草图并上传到您的 Arduino。

  5. 在空闲时打开一个新的 Python 文件。

  6. 将文件另存为roundtrip_example.py

  7. 输入以下代码:

    import serial
    import time
    
    ser = serial.Serial('/dev/ttyACM0',9600,timeout=1)
    a = 1
    b = 2
    c = 3
    
    while 1:
        valList = [str(a),str(b),str(c)]
        sendStr = ','.join(valList)
    
        print(sendStr)
    
        ser.write(sendStr.encode('utf-8'))
    
        time.sleep(0.1)
    
        recStr = ser.readline().decode('utf-8')
        print(recStr)
    
        a = a+1
        b = b+1
        c = c+1
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  8. 保存并运行文件。

在 Python shell 窗口中,您应该会看到如下输出:

1,2,3
1,2,3,6

  • 1
  • 2
  • 3

输出继续增加,直到您停止程序。

这里有一些新的元素,但是,在大多数情况下,它与我们以前所做的没有什么不同。让我们来看看一些新元素。

第一个区别是我们导入了时间库。这个库提供了很多与时间相关的功能。在本练习中,我们对sleep()函数感兴趣。sleep()功能会在规定的秒数内暂停处理。正如您在我们的代码中看到的,我们希望暂停处理 0.5 秒。这为串行流的两端提供了处理缓冲区的时间。如果您注释掉那一行并再次运行程序,您会得到一些有趣的结果。试试看。

valList = [str(a),str(b),str(c)]

  • 1
  • 2

这里我们把变量放在一个列表中。在下一步中,当我们将元素连接成一个字符串时,整数需要是字符串。因此,我们在这里进行了转换。

sendStr = ','.join(valList)

  • 1
  • 2

接下来,我们使用 string 类的join()方法将列表转换成字符串。注意join()方法是如何附加到','字符串上的。join是 string 类的方法,不是 list 类的方法,所以必须从 string 中调用。因为该操作实际上是在列表上工作,而不是在字符串上,所以您必须提供一个字符串才能使它工作。在这种情况下,所提供的字符串是您希望在列表的每个成员之间使用的分隔符。它可以是任何字符,但是为了让parseInt()在 Arduino 端工作,字符必须是非字母数字的。

ser.write(sendStr.encode('utf-8'))

  • 1
  • 2

另一个值得注意的区别是我们使用write()方法向 Arduino 发送数据。这类似于 Arduino 中的Serial.println()方法。最大的区别是你必须在发送之前对字符串进行编码。

企鹅!企鹅

连接一个或多个传感器以检测机器人周围世界的常见用例。在下一个练习中,我们将在 Arduino 上设置我们的 HC-SR04 超声波测距仪,并将距离信息作为串行字符串发送回 Pi。为此,我们需要在两块电路板之间建立一个串行连接。Arduino 触发传感器,并像我们之前的研讨会一样,读取返回的脉冲。我们将计算距离,然后将结果发送给 Pi。

在 Pi 端,我们只需要一个程序来监听串口,然后打印它从 Arduino 读取的任何内容。

设置电路

设置电路再简单不过了。事实上,我们不使用试验板。我们将传感器直接连接到 Arduino 接头(见图 5-9 )。

  1. 将 VCC 连接到 Arduino 上的 5V 引脚。
  2. 将 GND 连接到 Arduino 上的一个 GND 引脚。哪一个都无所谓,但是 5V 引脚旁边有两个。
  3. 将 TRIG 连接到 Arduino 上的引脚 7。
  4. 将 ECHO 连接到 Arduino 上的第 8 针。

树莓派和 Arduino 机器人入门手册(二)

图 5-9

Pinguino exercise circuit layout

代码

我们需要为两块板都编写代码,这样才能工作。在 Arduino 上,我们触发超声波传感器并捕捉返回信号。然后,我们将把它转换成厘米,并把数值打印到串行端口。

Pi 从串行端口读取该行,并将结果打印到 Python shell 窗口。

阿尔杜伊诺
  1. 打开一个新的草图窗口,并将其另存为serial_test

  2. 输入以下代码:

    int trig = 7;
    int echo = 8;
    int duration = 0;
    int distance = 0;
    
    void setup() {
            Serial.begin(9600);
            pinMode(trig, OUTPUT);
            pinMode(echo, INPUT);
    
            digitalWrite(trig,LOW);
    }
    
    void loop() {
            digitalWrite(trig, HIGH);
            delayMicroseconds(10);
            digitalWrite(trig, LOW);
    
            duration = pulseIn(echo, HIGH);
            distance = duration/58.2;
    
            Serial.write(distance);
    
            delay(500);
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

保存草图并上传到 Arduino。

树莓派
  1. 打开一个新的空闲文件,并将其另存为serial_test.py

  2. 输入以下代码:

    import serial
    import time
    
    ser = serial.Serial('/dev/ttyAMC0', 9600)
    
    while 1:
            recSer = ser.readline().decode('utf-8')
            recSer.rstrip()
    
            distance = int(recSer + '0')/10
    
            print("Distance: " + str(distance) + "cm     ", end = '
    ')
            time.sleep(0.5)
    
    

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  3. 保存并运行文件。

现在,您应该可以在 Python shell 窗口中看到以厘米为单位的距离文本。

该代码输出来自单个超声波传感器的结果。在现实中,你的机器人应该有三个或更多的传感器以不同的角度指向前方。原因是,只要障碍物在机器人的正前方,超声波传感器就能很好地工作。如果机器人以倾斜的角度接近墙壁或其他障碍物,声音不会反弹回传感器。在不同的角度有一个以上的传感器允许机器人检测不在其正前方的障碍物。

摘要

将 Arduino 添加到 Raspberry Pi 为您提供了更广泛的可能性。与 Pi 本身相比,您可以添加更多的传感器和 led。好处包括增加模拟输入数量、更多 PWM 输出和更多数字输出。

Arduino 非常适合编程。如果你已经熟悉 C 或 C++,那么为 Arduino 编写代码应该是非常熟悉的。然而,记住 Arduino 和 Python 之间的区别是非常重要的。Python 不在行尾使用字符,但是 Arduino 用分号结束每一行。编写条件和循环涉及到更多一点的语法。代码块包含在花括号中。缩进在 Arduino 中并不重要,但是如果缩进关闭,Python 将不会编译。

尽管有这些不同,Arduino 还是简化了一些事情。串行通信不需要太多设置,串行命令是核心 Arduino 库的一部分。在 Python 中,你必须导入串行库。两者都使得对串行端口的写入相当简单。然而,Python 需要编码和解码成 utf-8 才有用。此外,Arduino 使用parseInt()方法使得从串行流中解析一行中的数字变得容易。用 Python 从一个字符串中取出一个数字需要一点小小的操作。

当你使用 Arduino 时,不要忘记社区支持是极好的。很少有其他人没有做过和记录的事情。还要记住,在 IDE 中,您有大量的示例代码形式的资源。利用这一点。随着您为更多设备添加更多库,您会发现更多示例草图对您有所帮助。

六、驱动电机

在第四章中,我们使用 Raspberry Pi 的 GPIO 引脚来控制 LED 并接收来自超声波传感器的信息。在第五章中,我们讨论了 Arduino,并讨论了为什么它是通用 GPIO 功能的更好选择。我们将超声波测距仪和 LED 连接到 Arduino,并学习如何在两块电路板之间传递数据。

但这并不意味着我们已经完成了 Raspberry Pi 的 GPIO 头。在这一章中,我们将使用 GPIO 引脚连接到一个叫做电机驱动器的电路板,该电路板用于与 DC 电机和步进机交互。我将介绍一些不同类型的电机,并讨论什么是电机驱动器,以及为什么它在我们的工作中很重要。

我们将把 DC 马达连接到马达控制器上,并编写一个小程序使它们转动。作为示例程序的一部分,我们将研究如何控制电机的速度和方向。我们还将了解为研讨会选择的特定电机控制器的一些属性。

您可能选择不使用建议的电机控制器,因此我们也将看看一个常见的替代方案:L298N 电机驱动器。许多制造商提供的驱动板旨在连接到 L298N H 桥控制器芯片的核心。但是因为这些板依赖 PWM 信号来设置速度,我们必须通过 Arduino 连接它。我将在这一章的结尾回顾这一切。

在本次研讨会结束时,你将拥有开始制造机器人所需的最后一个组件:运动。在第七章中,我们将把所有东西和底盘套件放在一起,让你的机器人动起来。

电机和驱动器

在讨论电机控制器之前,让我们花点时间看看我们在控制什么。我们使用的驱动器是为简单的 DC 电机设计的,尽管它也可以用来驱动步进电机。让我们在这一节看看驱动器和电机。

电机类型

马达将电能转化为旋转能。它们有许多不同的类型,它们为几乎所有移动的东西提供动力。最常见的电机类型是简单的 DC 电机,它甚至用于许多其他类型的电机。例如,伺服电机是一种将 DC 电机与电位计或其他反馈设备以及控制精确运动的传动装置结合在一起的设备,无论是角度运动还是方向运动。其他类型的电机包括步进电机,它使用电脉冲来控制非常精确的运动,以及无芯电机,它重新排列 DC 电机的典型部件以提高效率。

DC 汽车公司

DC 发动机由磁场中的一系列线圈组成。当电荷加在线圈上时,会使线圈绕着它们共有的轴旋转。简单的电动机的线圈围绕一个中心轴排列和连接。当轴和线圈旋转时,与轴接触的电刷保持电气连接。轴又从组件中伸出,以利用旋转力做功。图 6-1 显示了典型的 DC 电机。

树莓派和 Arduino 机器人入门手册(二)

图 6-1

DC motor operation

你通常会发现这些电机连接到变速箱、皮带或链条上,以转速为代价来放大电机的扭矩。这样做是因为一个裸露的 DC 发动机可以产生很大的速度,但原始速度很少有用。

我们正在使用的发动机就是这种类型的。它们是附在齿轮箱上的简单的 DC 发动机。

无刷电机

另一种类型的电机将机械连接移动到磁体。线圈保持静止。当施加电荷时,磁铁绕着共同轴上的线圈旋转(见图 6-2 )。这样就不需要电刷了,所以被称为无刷电机。

在业余爱好领域,无刷电机通常与多旋翼飞机联系在一起。它们也广泛应用于其他需要高速和高效率的领域,例如 CNC(计算机数字控制)主轴。你可能熟悉 Dremel 工具或路由器;这两种设备都是主轴类型,使用无刷电机。

树莓派和 Arduino 机器人入门手册(二)

图 6-2

Brushless motor operation

步进电机

到目前为止,我所讨论的所有马达都有一个或多个线圈,利用单一电荷工作。电荷可以是正的,也可以是负的,从而改变马达的方向。

步进电机则不同。步进器使用带有不同电荷的多个线圈(见图 6-3 ),将一次完整的旋转分成多个步进。通过操纵这些电荷,我们可以使马达移动并保持在某一步的位置上。这使得这些电机非常适用于数控机床、3D 打印机和机器人等应用中的有限控制。

树莓派和 Arduino 机器人入门手册(二)

图 6-3

Stepper motor operation

伺服系统

伺服电机是一个移动到特定角度并保持该位置的电机。它们通常在任一方向上具有 45 至 90 度的最大旋转。他们通过在最终输出齿轮上连接一个电位计来实现这一点。电位计向内部控制板提供反馈。当伺服系统接收到信号时,通常是以 PWM 的形式,电机旋转直到电位计和信号达到平衡。

图 6-4 显示了一个典型的爱好伺服。

树莓派和 Arduino 机器人入门手册(二)

图 6-4

Common servomotor

去掉限制器和电位计的伺服系统称为连续旋转伺服系统。它们用于需要扭矩的应用中。许多机器人由连续旋转伺服系统驱动。

这是一个一种爱好极大地有益于另一种爱好的例子。常见的爱好伺服最初用于爱好遥控飞机。由于大多数爱好者买不起昂贵的设备来控制他们的工艺,他们想出了如何大幅降低价格。这当然有助于我们这些机器人爱好者。

电机属性

在我们的项目中,有一些关于电机的事情需要记住。最重要的是电机的电气特性,特别是电压和电流。

电压

你已经对电压有些熟悉了,电压是运行一个设备所需要的电能的一种度量。Pi 由 5 伏电压供电,但在 3.3 伏电压下运行。Arduino 在 5 伏电压下运行,由 Pi 的 USB 端口供电。我们正在使用的马达是 6 伏的。保持这些电压是很重要的。如果你在一个 3.3 伏的设备上加 5 伏电压,你可能会损坏你的设备。

有专门设计的设备来帮助管理项目中的电压。电压调节器(升压或降压)保持恒定的电压。常见的 7805 5V 稳压器,取 6 到 12 伏,转换成 5 伏。多余的能量以热的形式消散,它们会变得非常热。

电压调节器非常适合电压供应,但对于转换器件中的 5 伏和 3.3 伏电压用处不大。为此,我们使用逻辑电平转换器,它需要来自两个器件的基准电压,但可以安全地转换器件之间的电压。

现在,您已经意识到了设备电压需求的差异。接下来,我们看看安培数。

安培数

安培数是电流的量度,或者是我们的设备运行所需的电压。最常见的类比是流水线中的水,其中电压是流水线的大小,安培数是流经流水线的水量。实际上,我喜欢将类比改为使用橡胶管。如果你试图通过一个橡胶管推出太多的水,就会发生不好的事情。

在电子领域,这通常用较小的单位毫安来衡量,通常记为 mA。例如,大多数设备的 USB 端口被限制在 800 毫安的功率。这恰好与我们选择的电机使用的功率相同;但是,它没有考虑功率尖峰。

设备上的电压有些被动。该设备使用你提供的电压,从不试图获取更多。安培数完全相反。一个设备渴望安培数,并继续汲取它所需要的,直到它满意地做它的工作,或超过可用的供应。

组件和设备需要一定的功率来工作。他们也有一个他们可以承受的最大功率。由于额外的电能转化为热量,如果你超过一个设备的最大耐受电流,它就会变热并死亡。有时非常壮观。

这个故事的寓意是“永远注意你正在画的水流。”这不仅适用于电机。led 因消耗大量电流而臭名昭著。

电机和放大器

众所周知,电机是耗电设备。他们不断地试图实现他们的目的:旋转。当电机上没有负载、重量或阻力时,它会以最小的电流消耗愉快地旋转。然而,开始增加电阻,电机汲取越来越多的电流,直到它达到它可以汲取的最大值,这被称为失速电流。失速电流实质上是当轴被物理限制移动时电机的安培数。

当电机启动、快速改变方向或遇到太大的旋转阻力时,它消耗的功率会急剧增加。如果这种突然的汲取对于供应来说太多了,那么一定有什么东西损坏了。我们拿一个 800mA 的源来说,比如一个 USB 插口;如果电机突然消耗 1 安培或更大的电流,USB 插孔可能会损坏。

电机驱动器

大多数微控制器、微处理器和电子设备只能处理少量电流。如果电流过大,它就会开始烧坏。因为电机通常很容易超过这个最大电流,所以通常不希望将任何大尺寸的电机直接连接到处理器。因此,我们将使用一种称为电机驱动器或电机控制器的设备。

电机控制器就是为此目的而设计的。它使用来自微控制器的低功率信号来控制大得多的电流和/或电压。在我们的例子中,我们使用电机控制器来控制来自 GPIO 引脚的 3.3 伏电压和 6 伏电压。我们通过一系列具有更大 1.2A (1,200mA)电流容差的元件来实现这一点,这些元件可以处理高达 3.0A (3,000mA)的短暂尖峰。

使用电机控制器

让我们看看两个电机控制器。第一个是阿达果的 DC &步进电机帽。该控制板专门设计用于安装在 Raspberry Pi 上。实用性和便利性的结合使它成为我对像我们这样的项目的首选。

另一个电机控制器是 L298N,它是一个 H 桥 IC。虽然 L298N 实际上是一个独立的组件——一个芯片,但有许多制造商已经构建了一个方便的分线板。当有人提到 L298N 电机控制器时,通常指的就是这种类型的电路板。本书中使用的是我在亚马逊上花 5 美元找到的通用版本。我的一些朋友说我为此付出了太多。

Adafruit DC &步进电机

本项目中的电机驱动器为 Adafruit 的一款,可在 www.adafruit.com/products/2348 买到。关于如何使用它的信息在 https://learn.adafruit.com/adafruit-dc-and-stepper-motor-hat-for-raspberry-pi 。事实上,我们要讲的大部分内容都来自这个 Adafruit 网站。

为我们的机器人选择这个设备有几个原因;更重要的是,它直接安装在树莓皮上,从而限制了在机器人上安装电子设备所需的面积。你很快就会知道,安装空间对大多数机器人来说都是非常珍贵的;尤其是如果你想保持它的紧凑。以下是使用此板的一些其他原因:

  • 它可以控制多达四个 DC 电机或两个步进电机。
  • 通信是通过 I2C 串行通道处理的,它允许多个器件堆叠在一起(这就是为什么我们在接头上使用较长的引脚)。
  • 因为它使用 I 2 C,它有自己专用的 PWM 模块来控制电机,所以我们不必依赖 Pi 本身的 PWM。
  • 它有四个 H 桥电机控制电路,电流为 1.2A,峰值电流为 3.0A,具有热关断功能,内部保护二极管可保护您的主板。
  • 有四个双向电机控制,带 8 位速度控制(0 至 255)。
  • 使用端子板可以轻松连接。
  • 有现成的 Python 库。
需要一些组件

该板以套件形式提供,需要焊接。如果您还没有这样做,您需要在继续项目之前组装它。请记住,我们为接头指定了更长的引脚,所以不要使用套件附带的引脚。

焊接练习时间到了。

有许多小引脚(40 个)需要焊接。如果你不熟悉焊接,你需要花点时间来学习。尽管焊接指导非常简单,但它超出了本书的范围。互联网上有很多有用的视频。我也强烈建议你找到你当地的创客空间。那里肯定有人能给你上一堂速成课。图 6-5 显示了我的简单焊接设置。

树莓派和 Arduino 机器人入门手册(二)

图 6-5

Preparing to assemble the motor HAT

组装电机帽非常容易,虽然有焊接涉及。你可以在 Adafruit 网站 https://learn.adafruit.com/adafruit-dc-and-stepper-motor-hat-for-raspberry-pi/assembly 找到组装的详细说明。

在这个练习中,你需要一个烙铁和焊料。我建议手边准备一些助焊剂,以及一些保持焊接头清洁的东西。回到学校后,我们用湿海绵来清洁顶端,但现在有更好的东西来做这项工作。你的覆盆子酱也会有帮助。

  1. Mount the extended header onto the Raspberry Pi’s 40-pin header (see Figure 6-6). This helps stabilize things as you solder.

    树莓派和 Arduino 机器人入门手册(二)

    图 6-6

    Extended stacking header on the Pi’s 40-pin GPIO

  2. Mount the Motor HAT circuit board onto the headers (see Figure 6-7). To help hold the board at a better angle for soldering, you may want to put something to support the other side. One of the terminal blocks works well for this.

    树莓派和 Arduino 机器人入门手册(二)

    图 6-7

    Circuit board mounted on the header

  3. 焊接第一个引脚。

  4. Once the first pin is soldered, heat it up again and adjust the board so that it sits properly (see Figure 6-8). When the solder for the pin cools, it will hold the board at the right angle while you solder the rest of the pins. If you supported the board with a terminal block or something else so that the board is sitting straight, you may be able to skip this step .

    树莓派和 Arduino 机器人入门手册(二)

    图 6-8

    Adjusting the placement and angle of the board

  5. Solder the rest of the first row (see Figure 6-9). You want a nice, clean, shiny joint.

    树莓派和 Arduino 机器人入门手册(二)

    图 6-9

    Solder the first row of pins

  6. Rotate the board 180 degrees and solder the second row (see Figure 6-10).

    树莓派和 Arduino 机器人入门手册(二)

    图 6-10

    Rotate the Pi and solder the remaining pins

  7. 从码头上取下帽子。

  8. Mount the screw terminals onto the board (see Figure 6-11).

    树莓派和 Arduino 机器人入门手册(二)

    图 6-11

    Adding the terminal blocks to the circuit board

  9. Use tape to hold the terminals in place while you flip the board over (see Figure 6-12).

    树莓派和 Arduino 机器人入门手册(二)

    图 6-12

    Tape helps hold the terminal blocks on the board while you turn it over to solder them into place

  10. Solder the terminals in place (see Figure 6-13).

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fgitee.com%2FOpenDocCN%2Fvkdoc-cv-zh%2Fraw%2Fmaster%2Fdocs%2Fbegin-bot-raspi-arduino%2Fimg%2FA457480_1_En_6_Fig13_HTML.jpg&pos_id=img-rfOImBfr-1724432347781)

图 6-13

Soldering the terminal pins  

  • 1
  • 2
  • 3
  • 4
  • 5

一旦你拿掉胶带,你就完成了。马达帽已经可以使用了。将帽子安装到码头上。您需要支撑带有端子的一侧,使其不会在 HDMI 外壳上短路(参见图 6-14 )。

树莓派和 Arduino 机器人入门手册(二)

图 6-14

The completed board mounted on the Raspberry Pi. The orange support piece is 3D printed .

连接电机控制器

连接发动机罩非常简单。只需将电路板安装在 Pi 的 GPIO 接头上。然而,有几件事需要注意。首先,小心不要弄弯树莓皮或帽子上的任何一个别针。这非常容易做到。电机帽上的接头销特别容易弯曲。

您还需要注意不要使端子板短路。你会注意到,安装时,焊点非常靠近 HDMI 连接的金属外壳(见图 6-15 )。对此有两个简单的解决方案。第一种解决方案(在您应用第二种解决方案之前,这是我们将在研讨会中进行的工作)是简单地在 Pi 的 micro USB 和 HDMI 连接器的金属外壳上放置一块绝缘胶带。第二个解决方案(推荐)是获得一些偏移量来支持帽子的侧面。垫片和螺钉也可以完成这项工作。重点是,你不希望电路板下垂并与外壳接触,这可能会导致短暂的灯光表演,并破坏电机帽和 Pi。

树莓派和 Arduino 机器人入门手册(二)

图 6-15

Adafruit Motor HAT mounted on a Raspberry Pi

一旦电路板安装好,并安全地与短路绝缘,就该连接电机了。在第一个教程中,我们将只使用一个电机。第二段代码控制两个,所以我们现在最好把它们连接起来。

但在此之前,我们必须准备好马达。现在,如果你的马达附带了引线,那么你就领先了。否则,您需要将导线焊接到您的电机上,如图 6-16 所示。我倾向于为有问题的电机使用适当尺寸的黑色和红色电线。我还喜欢确保每个电机上的导线匹配(黑线连接到每个电机的同一个电极,红线连接到每个电机的同一个电极)。这样我就不必事后猜测事情是如何联系在一起的。

树莓派和 Arduino 机器人入门手册(二)

图 6-16

Leads soldered to motor terminals

在这种情况下,我使用的是 26AWG 绞合线。铅丝一般有两种:绞合的和实心的。固体更加坚硬,非常适合跳投或者没有太多力矩的情况。绞合线由包裹在一个护套中的多根较细的线组成。它更加灵活,非常适合可能会有移动的应用。绞合线稍难处理,进入端子板的末端应镀锡,或涂上焊料(见图 6-17 )。这使得该端具有刚性,并且它在端子板中连接得更好。

树莓派和 Arduino 机器人入门手册(二)

图 6-17

Tinned lead

接下来的步骤是将电机连接到端子板。

树莓派和 Arduino 机器人入门手册(二)

图 6-18

Motor connected to the Motor HAT

  1. 确保接线盒是打开的,接线盒内的螺钉一直拧到顶部。确保不要拆下螺丝。你需要用一把相当好的十字螺丝刀。
  2. 将一根镀锡导线插入标有 M1 的端子板一侧的孔中(参见图 6-18 )。哪根线连接到哪个端口并不重要,只要两根线连接到同一个驱动程序的不同端口(在本例中是 M1)。
  3. 拧紧与电线插入的孔相对应的螺钉。
  4. 对电机的第二根导线重复该程序。

在这一点上,如果你是如此倾向,你可以连接第二个电机。我倾向于颠倒引线的顺序,因为它们是指向机器人的相反侧。您需要一个正向命令来使左马达朝一个方向转动,右马达朝另一个方向转动。如果它们都朝同一个方向转动,那么机器人就原地旋转。

您将对四节 AA 电池组的电源端子重复该过程。确保红色导线指向正极(+)侧,黑色导线指向负极(–)侧(参见图 6-19 )。

树莓派和 Arduino 机器人入门手册(二)

图 6-19

External battery pack connected to the Motor HAT

您的电路板和电机看起来应该与图 6-20 相似。连接好电机和电池组后,您就可以开始编码了!

树莓派和 Arduino 机器人入门手册(二)

图 6-20

Completed connections to the Motor HAT

使用摩托帽

安装好电机帽,连接好电机和电机电源后,就可以启动 Pi 并登录了。

安装库

一旦启动并连接到您的 Pi,您需要为 Motor HAT 安装 Python 库。这些可以从 Adafruit GitHub 站点获得。

  1. 打开终端窗口。

  2. 导航到您的 Python 代码目录。我的情况是TRG-RasPi_Robot

  3. 输入以下内容:

    git clone https://github.com/adafruit/Adafruit-Motor-HAT-Python-Library.git
    cd Adafruit-Motor-HAT-Python-Library
    
    

    • 1
    • 2
    • 3
  4. 安装 Python 开发库。

    sudo apt-get install python-dev
    
    

    • 1
    • 2
  5. 安装马达帽库。

    sudo python setup.py install
    
    

    • 1
    • 2

至此,您的 Pi 已经用必要的库更新了,是时候开始编写代码了。

代码

在我们开始编码之前,有一个快速但重要的注意事项。以前,我们几乎什么都用 Python 3。在本次研讨会中,我们将使用 Python 2.7。为什么?嗯,Adafruit 提供的默认库在 Python 2.7 中。可能会有 Python 3.x 库,但我们将使用本次研讨会的默认库。

正如您在之前的研讨会中所发现的,无论何时使用 GPIO 引脚,您都需要以超级用户的身份运行您的代码(sudo)。有几种方法可以做到。一种方法是保存您的 Python 代码,使文件可执行,然后用sudo运行程序。这是您将来运行代码的正确方式。对于车间,我们将走捷径。我们将从命令行使用sudo启动 IDLE IDE,这使得任何程序都可以使用sudo从 IDLE run 实例运行。

转动单个电机
  1. 打开终端窗口。您可以使用安装时使用的同一台计算机;但是只要 IDLE 是打开的,这个终端窗口就是锁定的。

  2. 类型sudo idle

  3. 在空闲的 IDE 中,创建一个新文件并另存为motors.py

  4. 输入以下代码:

    from Adafruit_MotorHAT import Adafruit_MotorHAT as amhat
    from Adafruit_MotorHAT import Adafruit_DCMotor as adcm
    
    import time
    
    # create a motor object
    mh = amhat(addr=0x60)
    myMotor = mh.getMotor(1)
    
    # set start speed
    myMotor.setSpeed(150)
    
    while True:
        # set direction
        myMotor.run(amhat.FORWARD)
    
        # wait 1 second
        time.sleep(1)
    
        # stop motor
        myMotor.run(amhat.RELEASE)
    
        # wait 1 second
        time.sleep(1)
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  5. 保存文件。

  6. 按 F5 运行程序。

让我们看一下代码。

我们首先从 Adafruit_MotorHAT 库中导入我们需要的对象,并为它们分配别名,这样我们就不必在每次使用它们时都写下完整的名称。我们还为代码后面的延迟导入了时间库。

from Adafruit_MotorHAT import Adafruit_MotorHAT as amhat
from Adafruit_MotorHAT import Adafruit_DCMotor as adcm

import time

  • 1
  • 2
  • 3
  • 4
  • 5

接下来,我们创建一个电机对象的实例。为此,我们告诉 Python 我们正在使用位于默认 I2C 地址0x60的 Motor HAT。然后,我们为连接到 M1 的马达或马达 1 创建一个马达对象。这让我们可以访问马达的方法和属性。

mh = amhat(addr=0x60)
myMotor = mh.getMotor(1)

  • 1
  • 2
  • 3

在我们打开马达之前,对于这个程序,我们将启动速度设置为半速多一点。

myMotor.setSpeed(150)

  • 1
  • 2

现在我们将电机驱动代码的剩余部分包装在一个while循环中。只要True值为真,这段代码就会一直执行。

while True:

  • 1
  • 2

驱动电机的代码非常简单。我们向前驱动电机一秒钟,然后停止电机一秒钟。程序一直这样做。

    # set direction
    myMotor.run(amhat.FORWARD)

    # wait 1 second
    time.sleep(1)

    # stop motor
    myMotor.run(amhat.RELEASE)

    # wait 1 second
    time.sleep(1)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在键盘上按 Ctrl-C。

请注意,程序结束,但电机继续转动。那是因为马达帽是自由运转的。这意味着控制器继续执行从 Pi 接收的最后一个命令。如果我们不让它停下来,它就不会停下来。

现在我们要做一些有趣的事情。一些我们以前没做过的事。我们将把电机驱动代码打包到一个try/except块中。这是一段代码,它允许我们捕获发生的任何错误,然后优雅地处理它们。

在这个特殊的例子中,我们将使用try/except块来捕获KeyboardInterrupt事件。当我们使用 Ctrl-C 退出程序时,这个事件被触发。

  1. while循环的代码修改如下:

    try:
        while True:
            # set direction
            myMotor.run(amhat.FORWARD)
    
            # wait 1 second
            time.sleep(1)
    
            # stop motor
            myMotor.run(amhat.RELEASE)
    
            # wait 1 second
            time.sleep(1)
    
    except KeyboardInterrupt:
        myMotor.run(amhat.RELEASE)
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  2. 运行程序。

  3. 让它运行一会儿,然后按 Ctrl-C。

程序退出时,电机将停止。

Python 捕获了KeyboardInterrupt事件,并在退出之前执行最后一行代码。代码释放电机,并简单地关闭它。

转动两个电机

转动单个马达很好,但我们的机器人将有两个马达,我们希望它们独立运行。我们还希望它们能够改变速度和方向。

要操作多个电机,只需为每个电机创建一个不同的电机对象实例。假设您之前连接了两台电机,我们将创建两台电机并向每台电机发送命令。我们还改变了马达的速度和方向。

  1. 从空闲状态创建新的 Python 文件。

  2. 将文件另存为two_motors.py

  3. 输入以下代码:

    from Adafruit_MotorHAT import Adafruit_MotorHAT as amhat, Adafruit_DCMotor as adcm
    
    import time
    
    # create 2 motor objects
    mh = amhat(addr=0x60)
    
    motor1 = mh.getMotor(1)
    motor2 = mh.getMotor(2)
    
    # set start speed
    motor1.setSpeed(0)
    motor2.setSpeed(0)
    
    # direction variable
    direction = 0
    
    # wrap actions in try loop
    try:
        while True:
            # if direction = 1 then motor1 forward and motor2 backward
            # else motor1 backward and motor2 forward
            if direction == 0:
                motor1.run(amhat.FORWARD)
                motor2.run(amhat.BACKWARD)
            else:
                motor1.run(amhat.BACKWARD)
                motor2.run(amhat.FORWARD)
    
            # ramp up the speed from 1 to 255
            for i in range(255):
                j = 255-i
    
                motor1.setSpeed(i)
                motor2.setSpeed(j)
    
                time.sleep(0.01)
    
            # ramp down the speed from 255 to 1
            for i in reversed(range(255)):
                j = 255-i
    
                motor1.setSpeed(i)
                motor2.setSpeed(j)
    
                time.sleep(0.01)
    
            # wait half a second
            time.sleep(0.5)
    
            # change directions
            if direction == 0:
                direction = 1
            else:
                direction = 0
    
    # kill motors and exit program on ctrl-c
    except KeyboardInterrupt:
        motor1.run(amhat.RELEASE)
        motor2.run(amhat.RELEASE)
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
  4. 保存文件。

  5. 按 F5 运行程序。

在很大程度上,代码是相同的。我们添加了几个for循环来计数到 255,然后再返回。我们创建了两个变量来保存这个值;第二个函数通过从 255 中减去该值来反转该值。我们也有一个变量来跟踪电机转动的方向。一旦两个马达都再次加速和减速,我们改变方向,再做一次。我们使用和以前一样的退出代码。

L298N 通用电机驱动器

L298N 是一种常见的 H 桥电机控制器芯片。一些制造商已经将芯片安装在电路板上,并添加了所有必要的支持电子设备。最终结果是一个流行的,通用的电机控制器。

h 桥电机控制器

H 桥电机控制器是你会遇到的最常见的电机控制器。它的名字来源于示意图中独特的 H 形。H 桥本质上由四个控制电机电流的栅极组成。根据闸门打开和关闭的方式,您可以控制电机旋转的方向。

在 L298N 上,有两个使能引脚(每个电机一个)和四个输入引脚。in1 和 in2 引脚控制电机 1,而 in3 和 in4 控制电机 2。图 6-21 显示了浇口的排列方式;in1 控制着 S1 和 S4,in2 控制着 S3 和 S2。当 in1 或 in2 为高电平时,它们各自的栅极关闭。当它们处于低位时,门就会打开。

当 in1 为高电平,in2 为低电平时,电流流动,使电机顺时针旋转。如果 in1 为低电平,in2 为高电平,电机逆时针旋转。如果两个引脚都为高电平,电机就不会旋转,实际上是踩了刹车。如果两个引脚都为低电平,则没有电流流过电机,电机自由旋转。

树莓派和 Arduino 机器人入门手册(二)

图 6-21

H-bridge motor controller operation

剩下使能引脚 enA 和 enB,用于设置电机速度。这就是我们在这些引脚上使用 PWM 的原因。PWM 允许我们改变每个电机的速度。如果我们使用标准的数字引脚,我们可以启动和停止电机,但它要么是全功率或无功率。PWM 让我们能够更好地控制电机。

使用 L298N

L298N 有几种使用方法;各有利弊。一种方法是将引脚连接到 Raspberry Pi,其优点是可以直接由 Pi 控制。缺点是您可能必须使用逻辑电平转换器,因为 Pi 的引脚是 3.3 伏,而控制器是 5 伏。而且,你失去了控制速度的能力。速度控制需要 PWM,正如我在前面章节中所讨论的,这是 Pi 所不具备的一个方面。

我首选的连接 L298N 的方法是通过 Arduino。这样,你就可以通过 PWM 来控制速度。此外,由于 Arduino 和控制器都是 5 伏,所以不需要使用逻辑电平转换器。当然,这里的缺点是,你必须通过串行将电机指令传递给 Arduino。

Arduino 伫列

在本练习中,Arduino 只是充当电机控制器的通道。我们将从串行流中读取指令,并将这些值传递给电机控制器。Arduino 将不执行任何逻辑。如果在现实世界中实现这一点,您可能希望传感器充当中断。通过允许传感器中断正常操作,您可以在项目中建立一些安全性。

  1. 在 Arduino IDE 中打开一个新的草图。

  2. 将草图另存为L298N_passthrough

  3. 输入以下代码:

    int enA = 9;
    int in1 = 8;
    int in2 = 7;
    int in3 = 5;
    int in4 = 4;
    int enB = 3;
    
    int enAVal, in1Val, in2Val, in3Val, in4Val, enBVal;
    
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    
      pinMode(enA, OUTPUT);
      pinMode(in1, OUTPUT);
      pinMode(in2, OUTPUT);
      pinMode(in3, OUTPUT);
      pinMode(in4, OUTPUT);
      pinMode(enB, OUTPUT); 
    
    }
    
    void loop() {
      // Only work if there is data in the serial buffer
      while(Serial.available() > 0){
    
        // Read the ints from the serial port
        enAVal = Serial.parseInt();
        in1Val = Serial.parseInt();
        in2Val = Serial.parseInt();
        // Only read the next three if there is data
        if(Serial.available() > 0){
          in3Val = Serial.parseInt();
          in4Val = Serial.parseInt();
          enBVal = Serial.parseInt();
        }
    
        // Write the values to the L298N
        analogWrite(enA, enAVal);
        digitalWrite(in1, in1Val);
        digitalWrite(in2, in2Val);
        digitalWrite(in3, in3Val);
        digitalWrite(in4, in4Val);
        analogWrite(enB, enBVal);
    
        // Purge any remaining data because we don't need it
        while(Serial.available() > 0){
          char x = Serial.read();
        }
      }
    }
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  4. 保存草图并上传到 Arduino。

你不会在 Arduino 上看到任何事情发生。我们所做的是用代码加载 Arduino,该代码简单地读取串行端口并将读取的值传递给 L298N。

我们在这段代码中做了一些你想注意的事情。

if(Serial.available() > 2){

  • 1
  • 2

首先要注意的是我们读入in2Val的值后的if语句。接下来的两个练习中都会用到此代码。第一个练习将只传递三个值。第二个将传递六个值。我们只读取后三个值,如果它们存在的话;否则,我们会得到一个错误。为了确保避免错误,如果有三个或更多的值要读取,我们只想读取接下来的三个值。

while(Serial.available() > 0){
  char x = Serial.read();
}

  • 1
  • 2
  • 3
  • 4

在草图的最后,我们添加了一个小的while循环。如果在读取完所有六个值后,串行缓冲区中还有任何剩余数据,我们需要将其清除,以便下一个周期缓冲区中没有任何离散数据。这个块只是读取所有剩余的字节,并将它们从缓冲区中删除。

连接 L298N

连接电机控制器比仅仅将其插入割台要复杂一些。我们将通过 Arduino 进行连接,以利用 PWM 引脚。与电机帽一样,我们将从四节 AAA 电池组为电机控制器提供外部电源。这提供了电机需要的 6 伏电压,而不会烧坏 Arduino。

转动一个电机

在 L298N 的第一个练习中,您将学习如何转动单个马达。我们设定马达的速度和方向,改变方向,改变速度。图 6-22 显示了本练习的电路。

  1. 将电机控制器上的 enA 连接到 Arduino 上的引脚 9。您可能需要移除跳线。

  2. 将 in1 连接到引脚 8。

  3. 将 in2 连接到引脚 7。

  4. 将 Arduino 上的接地引脚连接到螺丝端子上的接地柱。这可能是中柱。

  5. 将一根引线连接到 out1,另一根引线连接到 out2,从而将电机连接到电机控制器。目前,哪个领导去哪个输出岗位并不重要。

  6. 将电池组的黑色导线连接到 L298N 上的接地端子。

  7. Connect the red lead from the battery pack to the positive terminal. It is usually labeled + or VCC.

    树莓派和 Arduino 机器人入门手册(二)

    图 6-22

    L298N single motor wiring

  8. 在空闲状态下打开一个新文件。

  9. 将文件另存为L298N_1_motor_example.py

  10. 输入以下代码:

```py
import serial
import time

directon = 1

ser = serial.Serial("/dev/ttyACM0",9600,timeout=1)

def driveMotor(int speed, int drct):
    enA = speed

    # determine direction
    if drct == 1:
        in1 = 1
        in2 = 0
    else if drct == -1:
        in1 = 0
        in2 = 1
    else:
        in1 = 0
        in2 = 0

    valList = str(enA) + ',' + str(in1) + ',' + str(in2)
    serString = ','.join(valList)
    ser.write(serString)
    time.sleep(0.1)

while 1:
    # ramp up speed
    while motSpeed < 256:
        driveMotor(motSpeed, direction)
        motSpeed = motSpeed + 1

    # ramp down speed
    while motSpeed > 0:
        driveMotor(motSpeed, direction)
        motSpeed = motSpeed - 1

    # reverse direction
    direction = -direction

```

树莓派和 Arduino 机器人入门手册(二)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  1. 保存并运行文件

电机应该开始旋转,越来越快,直到它达到最高速度。在那时,它减速到停止,反转方向,然后重复。这种情况一直持续到您按 Ctrl-C 停止程序。

转动两个电机

接下来,我们旋转两个马达。设置和代码与我们刚才做的非常相似,只是多了一个马达。您应该已经连接了第一个电机。如果没有,请完成上一个练习中的步骤 1 到 7。让我们在增加第二个电机的情况下开始(见图 6-23 )。

  1. 将 Arduino 上的引脚 5 连接到电机控制器上的 in3。

  2. 将引线从引脚 4 连接到 in 4。

  3. 将引脚 3 连接到 enB。

  4. Connect the leads from the second motor to the out2 terminals. Again, it matters very little in this exercise which lead goes to which terminal. Later, when you are mounting the motors onto the robot, you want to make sure that the motors are connected so that they turn opposite to each other. For now, however, we only care that they actually turn.

    树莓派和 Arduino 机器人入门手册(二)

    图 6-23

    L298N two motor wiring

  5. 在空闲状态下打开一个新文件。

  6. 将文件另存为L298N_2_motor_example.py

  7. 输入以下代码:

    import serial
    import time
    
    directon = 1
    
    ser = serial.Serial("/dev/ttyACM0",9600,timeout=1)
    
    def driveMotor(int motor, int speed, int drct):
        enA = speed
    
        # determine direction
        if drct == 1:
            in1 = 1
            in2 = 0
            in3 = 1
            in4 = 0
        else if drct == -1:
            in1 = 0
            in2 = 1
            in3 = 0
            in4 = 1
        else:
            in1 = 0
            in2 = 0
            in3 = 0
            in4 = 0
    
        valList = str(enA) + ',' + str(in1) + ',' + str(in2) +
            ',' + str(in3) + ',' + str(in4) + ',' + str(enB)
        serString = ','.join(valList)
        ser.write(serString)
        time.sleep(0.1)
    
    while 1:
        # ramp up speed
        while motSpeed < 256:
            driveMotor(motSpeed, direction)
            motSpeed = motSpeed + 1
    
        # ramp down speed
        while motSpeed > 0:
            driveMotor(motSpeed, direction)
            motSpeed = motSpeed - 1
    
        # reverse direction
        direction = -direction
    
    

    树莓派和 Arduino 机器人入门手册(二)

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

这段代码与之前的练习没有太大的不同。我们所做的只是为第二台电机添加启用和输入变量。两个马达应该以相同的速度旋转。他们加速,减速,然后转向。看一下代码,确定如何让马达彼此独立地旋转。

摘要

在这一章,我们看了常见类型的电机:DC,无铁芯,步进电机和伺服电机。我们组装了阿达果 DC &步进电机帽子。(你现在应该对烙铁相当熟悉了。)然后,你学会了如何把你的马达连接到它上面,让它们旋转起来。

我们还研究了一种常见的通用电机控制器。L298N 的工作方式略有不同,因为方向是通过改变两个引脚的状态来设置的。我们通过 Arduino 连接 L298N,以利用 PWM 引脚来控制电机的速度和方向。我们可以轻松地将使能引脚连接到 Raspberry Pi GPIO 接头上的数字输出引脚。然而,对电机速度进行离散控制是很重要的。在接下来的一章中,你会看到为什么这很重要。

至此,您已经获得了构建一个简单的小机器人所需的所有信息。你已经学习了 Python 和 Arduino 编程。你已经用传感器让你的机器人探测周围环境。最后,你让你的马达旋转,所以你有运动。逻辑、感知和运动是每个机器人的本质。其他一切都是这些元素的更高级版本。

现在你已经了解了你需要的关于机器人的一切,我们将组装底盘套件并制造一个机器人。之后,我们开始让我们的机器人变得更有能力、更聪明。我们从红外传感器开始,继续控制算法,然后给机器人眼睛。嗯,一只眼睛。

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...