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

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

协议:CC BY-NC-SA 4.0

一、机器人学导论

机器人这个词可以有很多含义。对某些人来说,它是任何自己移动的东西;运动艺术是机器人技术。对其他人来说,机器人意味着可移动的东西或可以自己从一个地方移动到另一个地方的东西。实际上有一个领域叫做移动机器人学;自动吸尘器,如 Roomba 或 Neato,就属于这一类。对我来说,机器人学介于运动艺术和移动机器人学之间。

机器人是一种应用逻辑以自动方式执行任务的技术。这是一个相当宽泛的定义,但机器人学是一个相当宽泛的领域。它可以涵盖从儿童玩具到一些汽车的自动平行泊车功能的所有内容。在这本书里,我们制作了一个小型移动机器人。

你在本书中接触到的许多原则很容易转移到其他领域。事实上,我们将从头到尾经历制造一个机器人的整个过程。在本章的稍后部分,我将回顾我们将要构建的项目。到时候,我会提供一本书中用到的零件清单。这些部件包括传感器、驱动器、电机等等。欢迎您使用手头的任何东西,因为在很大程度上,我们在本书中讨论的所有内容都可以应用到其他项目中。

机器人基础

我喜欢告诉那些机器人新手,或者只是对机器人好奇的人,机器人由三个要素组成。

  • 收集数据的能力
  • 处理或处理收集到的数据的能力
  • 与环境互动的能力

在接下来的章节中,我们将应用这个原理来制作一个小型的移动机器人。我们将使用超声波测距仪和红外线传感器来收集环境数据。具体来说,我们将识别何时有要避开的物体,何时我们将开车离开桌子的边缘,以及桌子和我们将跟随的线之间的对比。一旦我们有了这些数据,我们将应用逻辑来确定适当的响应。

我们将在 Linux 环境中使用 Python 来处理信息,并向我们的电机发送命令。我选择 Python 作为编程语言,因为它很容易学习,而且你不必有复杂的开发环境来构建一些非常复杂的应用。

我们与环境的互动将只是控制马达的速度和方向。这将允许我们的机器人在桌子或地板上自由移动。驾驶汽车真的没什么难的。我们将研究两种实现方式:一种是为 Raspberry Pi 设计的电机驱动器,另一种是通用的电机控制器。

这本书旨在具有挑战性。我涵盖了一些相当复杂的材料,而且做得很快。我没有办法对这些话题中的任何一个提供详细的报道,但是我希望在本书结束的时候让你看到一个功能机器人。在每一章中,我都试图为您提供更多的资源来跟进所讨论的主题。你有时会挣扎;我过去是这样,现在也经常这样。

不是每个人都对所有的科目感兴趣。期望你能在本书之外拓展你最感兴趣的领域。坚持有回报。

在书的最后,我增加了一点挑战。在第九章中,我们开始利用树莓派的真正力量。我们看计算机视觉。具体来说,我们看一个叫做 OpenCV (CV 代表计算机视觉)的开源包。这是一个常见的和非常强大的实用程序集合,使处理图像和视频流变得非常容易。它也是在最新版本的树莓派基础上构建的六小时版本。为了让事情变得简单一些,节省更多的时间,我可以下载一个已经安装了 OpenCV 的操作系统版本。我将在第二章中详细讨论这一点。

Linux 和机器人技术

Linux 是基于 Unix 的操作系统。它非常受程序员和计算机科学家的欢迎,因为它简单明了。他们似乎很喜欢终端基于文本的界面。然而,对于包括我在内的许多其他人来说,Linux 可能非常具有挑战性。那么,我为什么要选择这个环境来写一本机器人导论的书呢?这个问题有三个答案。

首先,当你从事机器人工作时,你最终不得不面对 Linux。那只是事实。您可以不用输入一个sudo命令就做很多事情,但是您的能力有限。sudo命令代表 Linux 中的超级用户 do。这告诉操作系统,您将要执行一个受保护的功能,该功能需要比一般用户更多的访问权限。当我们开始使用树莓派时,你会了解到更多。

第二,Linux 具有挑战性。正如我之前所说的,这本书会挑战你。如果你之前在 Linux 工作过,那么这个理由对你不适用。然而,如果你不熟悉 Linux,Raspberry Pi,或者在命令行中工作,那么我们做的一些事情将会很有挑战性。这很好。你正在学习新的东西,这应该是一个挑战。

第三,也是最重要的一点,Raspberry Pi 使用 Linux。是的,您可以在 Pi 上安装其他操作系统,但是它是为使用 Linux 设计的。事实上,Raspberry Pi 有它自己的 Linux 风格,叫做 Raspbian。这是推荐的操作系统,所以我们将使用它。使用预构建的操作系统的好处之一,除了它的易用性,是许多工具已经安装并准备好了。

由于我们使用的是 Linux,我们将广泛使用命令行指令。这是大多数新用户遇到问题的地方。命令行代码是通过终端输入的。Raspbian 有一个我们将使用的 Windows 风格的界面,但它的大部分使用终端。图形用户界面(GUI)中有一个终端窗口,因此我们将使用它。然而,当我们设置 Pi 时,我们将默认设置它引导到终端模式。进入 GUI 只是一个简单的startx命令。所有这些都包含在第二章中。

传感器和 GPIO

GPIO 代表通用输入/输出。它表示设备的各种连接。Raspberry Pi 有很多 GPIO 选项:HDMI、USB、音频等等。然而,当我在本书中谈论 GPIO 时,我通常指的是 40 针 GPIO 接头。此接头提供了对大多数电路板功能的直接访问。我将在第二章中讨论这一点。

Arduino 也有 GPIO。事实上,有人可能会说 Arduino 完全是 GPIO。鉴于所有其他连接都允许您与 Arduino 核心的 AVR 芯片通信并为其供电,这与事实相差不远。

所有这些接头和 GPIO 连接都在那里,因此我们可以访问电路板外部的传感器。传感器是一种收集数据的设备。有许多不同类型的传感器,它们都有各自的用途。传感器可用于检测光线水平、与物体的距离、温度、速度等。特别是,我们将使用配有超声波测距仪和红外探测器的 GPIO 接头。

运动和控制

大多数机器人的定义都有一个共同点,那就是它需要能够移动。当然,你可以拥有一个实际上不动的机器人,但这种类型的设备通常被称为物联网。

有许多方法可以将运动添加到项目中。最常见的是使用马达。但是你也可以使用螺线管、空气或水压。我在第六章中详细讨论了电机。

虽然可以用 Raspberry Pi 或 Arduino 板直接驱动电机,但强烈建议不要这样做。电机消耗的电流往往超过电路板上的处理器所能处理的。相反,建议您使用电机控制器。像电机一样,电机控制器也有多种形式。我们将使用的电机控制板是通过 Raspberry Pi 的头部访问的。我还讨论了如何用 L298N 双电机控制器驱动电机。

树莓派和 Arduino

我们将使用 Raspberry Pi(见图 1-1 )结合 Arduino(见图 1-2 )作为我们机器人的处理平台。

A457480_1_En_1_Fig2_HTML.jpg

图 1-2

Arduino Uno

A457480_1_En_1_Fig1_HTML.jpg

图 1-1

Raspberry Pi 3 B+

Raspberry Pi 是一种单板计算机,大约有信用卡大小。尽管它的尺寸很小,但它是一个非常强大的设备。Pi 运行一个 Linux 版本,该版本被定制为在驱动它的 ARM 处理器上工作。这将许多功能放入一个小设备中,该设备很容易嵌入到像机器人这样的东西中。但是,尽管它是一台伟大的计算机,但也有一些地方它并不出色。一个领域是与外部设备接口。它可以与传感器和外部设备一起工作,但 Arduino 在这方面做得更好。

Arduino 是另一种易于获得和使用的小型处理设备。然而,与 Raspberry Pi 不同,它不具备完整操作系统的能力。它不是运行 ARM 这样的微处理器,而是使用一种不同类型的芯片,称为微控制器。不同之处在于,微控制器是专门设计来与传感器、电机、灯和各种设备进行交互的。它直接与这些外部设备交互。Pi 在到达设备所连接的引脚之前,需要经过多层处理。

通过结合 Raspberry Pi 和 Arduino,我们能够利用各自的优势。Raspberry Pi 提供了完整计算机的高级处理能力。Arduino 提供对外部设备的原始控制。Pi 允许我们处理来自简单 USB 摄像机的视频流;而 Arduino 允许我们从各种传感器收集信息,并应用逻辑来理解所有数据,然后将简明的结果返回给 Pi。

在第二章你会学到更多关于树莓派的知识。稍后,您将把 Arduino 连接到 Pi,并学习如何对它进行编程,以及如何在 Arduino 和 Pi 之间来回传递信息。

项目概述

在本书中,我们将构建一个小型移动机器人。这个机器人被设计用来演示你在每一章中学到的课程。然而,在我们实际建造机器人之前,我们需要涵盖大量的材料,并为未来的课程奠定基础。

机器人

我们将要建造的机器人是一个小型的两轮或四轮自主漫游车。它将能够探测障碍物和桌子的边缘,并跟随一条线。我选择的底盘是一个四轮机器人,但也有适合这个项目的其他设计(见图 1-3 和 1-4 )。

A457480_1_En_1_Fig4_HTML.jpg

图 1-4

The back of our robot shows the Raspberry Pi and motor control board

A457480_1_En_1_Fig3_HTML.jpg

图 1-3

The front of our robot shows the ultrasonic sensors and Pi T Cobbler on a breadboard

虽然我提供了一份我在这个项目中使用的零件的清单,但是你可以随意使用。重要的是,他们的行为方式与我所列举的方式相似。

材料清单

在很大程度上,我试图让材料列表尽可能通用。有几项是特定于供应商的。我选择它们是因为它们提供了很多功能和便利。DC &步进电机控制器和圆饼匠来自一家名为 Adafruit 的在线零售商,这是一个零件、教程和灵感的绝佳资源。底盘套件来自一家名为 ServoCity 的在线零售商,该零售商为机器人生产许多机械零件。

以下是我们在本书中使用的特殊零件(如图 1-5 所示):

A457480_1_En_1_Fig5_HTML.jpg

图 1-5

Runt Rover chassis parts and the Pi T Cobbler, ribbon cable, motor control hat, and extended header

  • 来自 ServoCity.com 的小型机器人底盘
  • 阿达果 DC &树莓派步进电机帽–迷你套件 PID: 2348
  • Pi A+/B+/Pi 2/Pi 3 的 GPIO 堆叠接头–超长 2×20 引脚 PID: 2223(允许使用额外的板和补鞋匠来连接到试验板)
  • 组装的 Pi T-Cobbler Plus–GPIO 分线点–Pi A+、B+、Pi 2、Pi 3、零 PID: 2028

以下零件(如图 1-6 所示)相当普通,可以从大多数供应商处购买:

  • raspberry Pi 3–型号 B–arm V8,带 1G RAM

  • Arduino 一号

  • 4 × AA 电池盒,带开/关开关(为电机供电)

  • USB 电池组–2200 mAh 容量–5V 1A 输出 PID: 1959(为 Raspberry Pi 供电)

  • 半尺寸试验板

  • 超声波传感器–HC-sr04 您可能想要几个这样的传感器。你会发现,超声波传感器在角度上是不可靠的,有一组这样的传感器是很好的。我在大部分项目中至少使用三种。

  • 一组跳线(见图 1-7 )您需要公对公跳线和公对母跳线。这是一个好主意,让他们在一些颜色。黑色和红色用于为您的设备供电。其他颜色的集合可以帮助你理解你的电路。幸运的是,你可以用彩色带状电缆制作各种类型的跳线。

  • Arduino 的 USB 电缆

  • 一根微型 USB 线,用于您的树莓派

  • 一个普通的 USB 手机充电器,最好是现代智能手机或平板电脑的充电器,可以提供 2 安培的电源

  • HDMI 电视或电脑显示器大多数电脑显示器上没有 HDMI 端口。不过,您可以购买 HDMI-DVI 转换器,以便使用现有的显示器。

  • USB 键盘和鼠标(我喜欢罗技 K400 无线键盘和触摸板的组合,但有无数的选择)

  • 联网的计算机

  • Wi-Fi or Ethernet cable for the Pi

    A457480_1_En_1_Fig7_HTML.jpg

    图 1-7

    Jumpers in ribbon cable form. Pull off what you need

    A457480_1_En_1_Fig6_HTML.jpg

    图 1-6

    Common parts: Raspberry Pi, Arduino Uno, ultrasonic sensor, battery holder, and breadboard

你不需要对显示器和键盘着迷。一旦你阅读了第二章,在那里我们安装和配置了 Raspberry Pi,你不再需要它们了。我有几个无线键盘,因为我通常会同时进行几个项目。对于显示器,我只需使用一台带有 HDMI-DVI 适配器的电脑显示器。

如果您没有使用包含电机和车轮的底盘套件,您还需要以下零件(参见图 1-8 ):

A457480_1_En_1_Fig8_HTML.jpg

图 1-8

DC geared motor and wheels

  • 业余齿轮马达-200 转/分(一对)
  • 车轮–65 毫米(橡胶轮胎,一对)

如果您不想使用 Adafruit 电机和步进器,您也可以使用几乎任何电机控制器,尽管每个控制器都有不同的接口和代码。一个常见且相当受欢迎的选项是 L298N 双电机控制器(见图 1-9 )。

A457480_1_En_1_Fig9_HTML.jpg

图 1-9

The L298N dual motor controller module comes in numerous varieties, but essentially work the same

我还保留了一些其他的供应品,因为它们几乎在每个项目中都要用到。第七章,我们组装机器人;你还需要双面胶、4 英寸的拉链和自粘 Velcro。随着你继续学习机器人技术,你会发现自己会经常接触到这些东西。事实上,你可能想储备各种尺寸的拉链。相信我。

摘要

机器人入门并不困难。然而,这很有挑战性。如果你想在这个领域取得成功,这本书介绍了一些你需要发展的技能。我们制造的机器人向你介绍树莓派、Linux、Arduino、传感器和计算机视觉。这些技能很容易扩展到更大的机器人和其他类似的项目。

二、树莓派简介

这本书的目的是挑战你建立一个简单的机器人,它将随着时间的推移而扩展。这本书被认为是难的;然而,这并不太困难或不必要的复杂。在这个过程中,你会经历很多复杂的事情,但是在你的 Raspberry Pi 上安装操作系统并不是其中之一。

下载并安装 Raspbian

本质上,在您的 Pi 上安装操作系统(OS)有两种方法。

第一步包括下载最新的 Raspbian 映像,将其写入 SD 卡,然后从那里开始。这种方法需要安装第三方软件包,该软件包在 SD 卡上写入可引导映像。好处是它在你的 SD 卡上占用的空间更少。如果你使用最低 8GB 的 SD 卡,这可能会有所帮助;如果你变得更大,那么这个考虑是没有意义的。

尽管直接安装并不复杂(实际上相当简单),但有一种更简单的方法,不需要在系统上安装额外的软件。NOOBS(新的开箱即用软件)旨在使您的树莓派 的安装和配置更容易。它允许您从多个操作系统中进行选择,并简单地安装。然而,NOOBS 包仍然在 SD 卡上,并吃掉宝贵的空间。它确实允许您返回并修复您的操作系统或完全更改操作系统,但这可以非常容易地手动处理。

最终,选择权在你。我将介绍这两个选项,以便您可以选择最适合您的安装路径。无论您选择哪个选项,您的旅程都是从 www.raspberrypi.org/downloads/ 的 Raspbian 下载页面开始的(见图 2-1 )。

A457480_1_En_2_Fig1_HTML.jpg

图 2-1

Raspbian download screen

使用 OpenCV 的 Raspbian

在这本书的结尾,我们将与计算机视觉一起向你展示为什么你应该使用一个树莓派 而不是一个能力较差的平台。然而,为了做到这一点,您需要在您的 Pi 上安装 OpenCV。不幸的是,对于 Raspberry Pi 没有简单的 OpenCV 安装程序。因为 Pi 运行在 ARM 处理器上,所以软件包必须从源代码编译,这是一个六小时的过程。

为了方便起见,我在 Raspbian Jesse 中预编译了 OpenCV,并在 https://github.com/jcicolani/Jesse-OpenCV 创建了一个可下载的映像。

您仍然需要完成安装和配置过程来定制安装。该映像包含您需要更改的默认设置(除了构建时需要的一些例外)。

“艰难”的方式

更困难的方法是将 Raspbian 操作系统映像直接安装在 SD 卡上——准备启动。这是我使用的方法,因为它并不比前面的方法更复杂,而且它允许我使用 NOOBS 没有的版本。

对于 Raspbian 安装,您有两种选择。Jessie 是操作系统的最新稳定版本;这是我们将要使用的。第一个选项是 Raspbian Jessie 和 PIXEL——他们新的优化 GUI。这是一个 1.5GB 的下载,解压缩后是一个 4.2GB 的图像。第二个选项是 Raspbian Jessie Lite,这是一个最小的映像,下载量小得多,只有 300MB(解压缩后只有 1.4GB)。然而,最小意味着没有 GUI,所以一切都是通过命令行完成的。如果你是无头 Linux 的粉丝,那么这是你的选择。我们将使用更大的像素安装。

如果安装了 BitTorrent 客户端,请单击“下载 Torrent”。这比下载。zip 文件。

  1. 导航到 www.raspberrypi.org/downloads/

  2. 单击 Raspbian 图像。

  3. 选择您想要安装的 Raspbian 版本。

  4. 下载完成后,将文件解压到你容易找到的地方。

  5. 下载并安装 Win32 磁盘映像器。这允许您将刚刚下载的图像文件写入 micro SD 卡。可以在 https://sourceforge.net/projects/win32diskimager/ 获得。

  6. 或者,您可能还想下载 SDFormatter,以确保您的 SD 卡已准备妥当。可以在 www.sdcard.org/downloads/formatter_4/ 获得。

  7. 将您的 micro SD 卡插入连接到计算机的读卡器。

  8. If you have downloaded and installed SDFormatter, open it. You should see a dialog box similar to the one shown in Figure 2-2.

    A457480_1_En_2_Fig2_HTML.jpg

    图 2-2

    SD Card Formatter

  9. 确保您选择的驱动器代表您的 SD 卡。你将要格式化它,所以如果你选择了错误的东西,它会清除你在那个驱动器上的所有东西。工具通常默认选择正确的,但是要仔细检查。明智的做法是断开任何其他外部存储设备。

  10. 确保“格式大小调整”设置为“开”。这将删除卡上的任何其他分区,并使用整个分区。将所有其他设置保留为默认值。

  11. 单击开始。该过程完成后,您就可以安装操作系统了。

  12. 要将图像刷新到 SD 卡,请打开 Win32 Disk Imager。

  13. 在“图像文件”字段中,选择您下载的 Raspbian 图像。您可以点击文件夹图标来浏览它。

  14. 确保在设备下拉框中选择了您的 SD 卡。同样,选择错误的设备可能会导致世界的伤害;所以要注意。

  15. 单击写入。

  16. 该过程完成后,从读卡器中取出卡。

  17. 将卡插入 Raspberry Pi 上的 micro SD 读卡器。

这听起来很长,但是做起来非常快速和容易。接下来,我们来看一下 NOOBS 的安装过程。

“简单”的方法

我把这个方法称为“简单”的方法,尽管困难的方法实际上很简单。让这变得容易的是,你不必直接写图像。你可能想要格式化卡片,但是如果是一张新卡,那就没有必要了。更简单的是,如果你购买的 Pi 是入门套件的一部分,那么它可能已经在 micro SD 卡上安装了 NOOBS。如果是这种情况,可以跳过前几步。

你有两个选择:NOOBS 和 NOOBS 建兴。NOOBS 在下载中包含了 Raspbian 的图像,所以一旦它存在你的 SD 卡上,你就不需要连接到网络来下载任何东西了。如果你愿意,你可以选择另一个操作系统,但是你需要把你的 Pi 连接到网络上,这样 NOOBS 才能下载它。NOOBS 建兴不包括完整的拉斯扁形象。出于我们的目的,选择标准 NOOBS 安装。

  1. 单击下载页面上的 NOOBS 图像。
  2. 选择你的 NOOBS 风味。如果安装了 BitTorrent 客户端,请单击“下载 Torrent”。这比下载。zip 文件。
  3. 或者,您可能还想下载 SDFormatter,以确保您的 SD 卡已准备妥当。可以在 www.sdcard.org/downloads/formatter_4/ 获得。
  4. 如果您下载并安装了 SDFormatter,请将其打开。
  5. 确保您选择的驱动器代表您的 SD 卡。你将要格式化它,所以如果你选择了错误的东西,它会清除你在那个驱动器上的所有东西。工具通常默认选择正确的,但是要仔细检查。明智的做法是断开任何其他外部存储设备。
  6. 确保“格式大小调整”设置为“开”。这将删除卡上的任何其他分区,并使用整个分区。将所有其他设置保留为默认值。
  7. 单击开始。该过程完成后,您就可以安装操作系统了。
  8. 将 NOOBS 文件直接解压到 SD 卡上。
  9. 从读卡器中取出卡。
  10. 将卡插入 Raspberry Pi 上的 micro SD 读卡器。
  11. 此时,您需要连接您的 Pi 以继续。所以,请跳到本章的“连接树莓派”一节。完成这些步骤后,请返回本部分继续设置。
  12. 当你连接电源到树莓派,它启动到 NOOBS 安装屏幕。如果你使用 NOOBS 建兴,你有你的操作系统的选择。如果您使用标准的 NOOBS 下载,您唯一的选择是 Raspbian(这是可以的,因为这是我们正在使用的)。
  13. 单击“Raspbian”以确保它已被选中。还要确保在屏幕底部选择了正确的语言(在我的例子中,是英语(美国))。
  14. 单击屏幕顶部的安装按钮。

安装可能需要一点时间,所以去喝杯咖啡吧。

连接树莓派

现在你的微型 SD 卡已经准备好了,你需要连接上你的树莓派。如果你使用的是原始的第一代 Pi,这就有点复杂了。

然而,第一代之后的每个型号都包括多个 USB 端口和一个 HDMI 连接器,使事情变得更简单。连接 Pi 非常简单。

  1. 通过 HDMI 电缆连接显示器。如果您使用的是配有分量连接而不是 HDMI 的小型电视机,Pi 上的音频插孔是一个四极分量插孔。你需要一个 RCA 到 3.5 毫米的转换器,通常是电缆形式,来做到这一点。
  2. 将键盘和鼠标连接到 USB 端口。我使用无线键盘/触摸板组合,因为它小巧便携。
  3. 确保您的带有 Raspbian 或 NOOBS 的 micro SD 卡安装在 Pi 上的 micro SD 端口中。本质上,这是你的小型电脑的硬盘,所以它必须在正确的位置。它不会通过连接到其中一个 USB 端口的 SD 读卡器读取操作系统。
  4. 如果您使用以太网电缆,请将其连接到以太网端口。您也可以将 Wi-Fi 加密狗插入 USB 端口。如果你和我一样用的是 Pi 3,Wi-Fi 是内置的。
  5. 将 5V 电源连接到微型 USB 端口。此端口仅用于电源。您不能通过 USB 访问主板。

就是这样。你的树莓派 应该看起来类似于图 2-3 所示。Pi 应该在您的显示器上启动。如果您正在安装 NOOBS,请返回 Noobian 安装的步骤 10 以完成安装过程。

A457480_1_En_2_Fig3_HTML.jpg

图 2-3

Raspberry Pi connections

现在您已经连接并启动,您需要登录。以下是 Raspbian 安装的默认凭据:

  • 用户名:pi
  • 密码:树莓

当然,默认的用户名和密码从来都不安全。因此,为了防止你的网络安全朋友带着你的机器人逃跑,我们要做的第一件事就是更改密码。在稍后的配置中,我们将更改默认用户名。

配置您的 Pi

现在我们已经完成了初始安装,接下来我们将进行一些定制。根据您的特定用途,Pi 有几个您可以启用的功能。最初,它们无法减少运行操作系统所需的一些开销。我们将要实现的配置设置是为了安全和方便。

使用 raspi-config

为了进行定制,Raspberry Pi Foundation 的优秀人员提供了一个名为 raspi-config 的实用程序。使用它需要命令行终端。现在只输入了一条命令,但是随着我们在研讨会中的深入,您会对终端窗口更加熟悉。如果你是 Linux 新手(Raspbian 是基于 Linux 的),这可能有点吓人。不需要这样,我会尽力让你适应。但你必须学会如何应对。

你可以在 www.raspberrypi.org/documentation/configuration/raspi-config.md 找到更多关于 raspi-config 实用程序的信息。

此时,您应该已经启动了您的 Raspberry Pi。如果没有,现在就做。

我们将做几件事来配置 Pi,首先扩展文件系统以利用整个 SD 卡。默认情况下,Raspbian 不使用整个 SD 卡,所以我们想告诉它。如果您正在使用 NOOBS,这已经为您完成,所以您可以跳过这一步。

  1. 单击屏幕顶部的 Raspberry Pi 图标。这将打开一个应用列表。

  2. Select Accessories ➤ Terminal, as shown in Figure 2-4. When opened, the terminal window is displayed (see Figure 2-5).

    A457480_1_En_2_Fig5_HTML.jpg

    图 2-5

    Terminal window

    A457480_1_En_2_Fig4_HTML.jpg

    图 2-4

    Terminal selection from the applications list. The terminal icon is also on the quick access bar.

  3. Type sudo raspi-config. This opens the Raspberry Pi Software Configuration Tool, as shown in Figure 2-6.

    A457480_1_En_2_Fig6_HTML.jpg

    图 2-6

    The raspi-config screen. Most OS-level options can be set here, including activating and deactivating services. Newer versions of Raspbian automatically expand your file system the first time you start the Pi. Unless you are using an older version of Raspbian, you should be able to skip this next step and move on to changing the password.

  4. 确保突出显示扩展文件系统。

  5. 按回车键。系统弹出一条关于扩展文件系统的消息,要求您重新启动。(我们将在完成大部分更改后重启。)接下来,我们将更改用户密码。

  6. 确保突出显示“更改用户密码”。

  7. 按回车键。系统显示一条消息,提示您输入新密码。

  8. 按回车键。这将使您进入终端,输入新密码。

  9. 输入您的新密码,然后按 Enter 键。

  10. Confirm your new password and press Enter. This displays a confirmation that the password was successfully updated (see Figure 2-7).

![A457480_1_En_2_Fig7_HTML.jpg](https://img-blog.csdnimg.cn/img_convert/e16a3301fcc32c753f2f4aa776134c71.jpeg)

图 2-7

A password change confirmation in raspi-config  
  1. 按回车键。接下来的几个步骤将激活我们稍后会用到的一些服务。首先,我们将把您的 Pi 的主机名改为在网络上更容易找到的唯一名称。当你和另外 20 个树莓派共处一室时,这变得尤为重要。
  2. Make sure that advanced options is highlighted, and then press Enter. This displays the interface and other options (see Figure 2-8).
![A457480_1_En_2_Fig8_HTML.jpg](https://img-blog.csdnimg.cn/img_convert/e2d381764134a1a5cfe9ef84bff411dc.jpeg)

图 2-8

raspi-config advanced options. Hostname and service activation is accessed here. The hostname is how your Raspberry Pi appears on the network. You’ll want to give your Pi a unique name, especially when you consider how many of them may be on the network at any given time. The hostname should be both meaningful to the application and unique.  
  1. 突出显示主机名,然后按回车键。
  2. 一个对话框解释了主机名的要求。它只能是字母数字字符:没有符号,没有连字符,没有下划线。按回车键继续。
  3. 输入您的新主机名,然后按 Enter 键。SSH 允许我们从另一台计算机通过终端窗口(SSH 客户端)访问 Pi。在 Windows 上,PuTTY 是一个非常流行的免费 SSH 客户端。SSH 不提供 GUI。所有交互都是使用终端命令完成的。如果您想要快速执行程序、安装软件等等,这是很有帮助的。随着您对终端越来越熟悉,您可能会发现自己使用 SSH 来连接简单的命令,而将 VNC(远程桌面)留给更复杂的任务,如编写程序。
  4. 返回高级选项菜单。
  5. 选择启用 SSH 并按回车键。
  6. 确认您想要启用 SSH,然后按 Enter 键。
  7. 再次按回车键返回菜单。I2C 是一种串行通信协议,在 Pi、Arduino 等嵌入式系统中非常流行。通过使用多个引脚,它可以与多个器件进行稳定的通信。我们将使用的电机控制板通过 I2C 进行通信。(如果你后来选择添加其他板,如伺服控制板,它也将使用 I2C。)只要设备有不同的地址,就可以一直堆叠。
  8. 返回高级选项菜单。
  9. 选择启用 I2C 并按回车键。
  10. 确认您要启用 SSH,然后按 Enter。
  11. 再次按回车键返回菜单。因为我们还计划使用 Raspberry Pi headless(没有连接显示器、键盘或鼠标),所以让我们将它设置为自动引导到控制台。不用担心;正如您将看到的,当您想启动桌面 GUI 时,它是非常容易的。
  12. 转到启动选项,然后按回车键。
  13. 选择控制台并按回车键。如果您相信只有您能直接访问您的 Pi,您可以选择控制台自动登录。自动登录不适用于远程会话,只能通过键盘和显示器直接访问。
  14. 更新所有设置后,突出显示 Finish 并按 Enter 键。
  15. Pi 会询问您是否要重新启动。选择是并按回车键。此时,您的 Pi 会重新启动。这可能需要几分钟,特别是如果您没有通过 NOOBS 安装,Pi 必须扩展您的文件系统。记住,我们设置 Pi 默认引导到控制台。因为接下来的几个步骤都是通过命令行完成的,所以我们不需要加载 GUI。然而,让我们无论如何做它以便你能看见它是多么容易。
  16. 键入startx并按回车键。

你现在在 GUI 桌面上。

要退出桌面,请执行以下操作

  1. 单击程序菜单(左上角的树莓图标)。
  2. 单击电源按钮。
  3. 选择退出到命令行。

您现在应该回到命令行了。

用户

Raspbian 的每个安装的默认用户是 pi。之前,我们更改了密码,使其更加安全。但是,您可能不希望总是以 pi 用户的身份登录。

记得我说过我们会更多地使用终端吗?好吧,现在开始。创建和管理用户最简单的方法是通过命令行。我们现在要走一遍这个过程。

保护根

除了默认用户 pi 之外,Pi 上还有另一个默认用户。这是根用户。根用户本质上是机器用来执行低级命令的管理用户。这个用户可以访问任何东西,可以做任何事情,因为它是一台机器。但是,与默认 pi 用户不同,root 没有默认密码。它没有密码。

因此,当我们为我们的机器人配置和保护计算机时,让我们给根用户一个密码。

  1. 打开终端窗口。
  2. 类型sudo passwd root(注意,passwd是正确的命令,而不是输入错误。)
  3. 输入 root 用户的新密码。
  4. 再次输入密码进行确认。

您的 root 用户现在是安全的,这很好,因为您将在配置的下一步需要它。

更改默认用户名

您要做的第一件事是将默认用户名更改为您选择的名称。这将把用户名 pi 替换为您自己的用户名。这在设备上提供了另一层安全性;现在,不仅有人需要找出密码,他们甚至没有默认的用户名。它还保留了默认用户被赋予的一些特殊的、未记录的权限。

  1. 注销 pi 用户。你可以通过菜单系统或者简单地在终端中输入logout来实现。

  2. 使用现在安全的 root 用户登录。

  3. 键入

    usermod -l <newname> pi
    
    

    <newname>是您选择的新用户名。命令中不要包含<>

  4. 要更新主目录名,请再次键入

    usermod -m -d /home/<newname> <newname>
    
    

    <newname>是您在上一步中使用的新用户名。

  5. 注销 root 用户,并使用您的新用户名重新登录。

此时,您已经更改了默认用户和 root 用户的默认用户凭据。您还更改了主机名。这是保护你的私人侦探和机器人的最低要求。

您的 Raspberry Pi 现在已经设置、配置好了,可以使用了。在我们进入下一章之前,我们还有一件事要做,那就是设置你的 Pi 为无头。

让一台机器“无头”仅仅意味着对它进行配置,这样你就不再需要连接显示器、键盘和鼠标来操作它。这通常通过两种方式实现:使用 KVM 交换机或设置远程访问。在移动机器人上,连接 KVM 并不是一个真正的选择。事实上,这与简单地将一切都连接到它没有什么不同。我们想要做的是设置 Pi,以便我们可以通过网络远程访问它。但首先,让我们确保您已连接到网络。

连接到无线网络

当您最初连接您的 Raspberry Pi 时,您可以选择连接以太网电缆。如果你这样做了,那么你已经在网络上了。但是,您仍然希望连接到无线网络。这允许你在机器人移动的时候遥控它。你可以向它发送命令,更新代码,甚至从第十章安装的网络摄像头观看视频。

若要连接到无线网络,您需要 Wi-Fi 连接。如果你使用的是 Raspberry Pi 3,那么你已经内置了一个;否则,你需要一个 Wi-Fi 加密狗,最好是可以通过 USB 端口供电的。这里的一点研究大有帮助。

  1. 通过键入startx登录 GUI 界面。

  2. Click the network icon at the top right of your screen. This icon looks like Figure 2-9.

    A457480_1_En_2_Fig9_HTML.jpg

    图 2-9

    Network connection icon

  3. 从列表中选择您的无线网络。

  4. 输入网络的安全密钥。

您现在应该已连接到无线网络。

变得没头了

在参加这些研讨会时,您不会想要携带额外的显示器、键盘和鼠标。为了让您的生活更加轻松,让我们设置一下,这样您就可以无头访问 Pi 了。

远程存取

有两种方法可以获得远程访问。一种方法是使用 SSH,它允许您使用终端客户端连接到远程设备。另一种方法是设置远程桌面。

使用 xrdp 的远程桌面

让我们从从另一台计算机远程访问桌面开始。以下说明适用于 Windows 用户。大多数现代的 Windows 安装都已经安装了远程桌面连接,这是我们在设置好 Pi 后用来连接它的。

让我们在 Pi 上安装几个服务:tightVNCserver 和 xrdp。理论上,xrdp 应该自己安装 VNC 服务器。事实上,并不是这样。此时,您应该在 Pi 的命令行上。

  1. 类型sudo apt-get install tightvncserver

  2. 完成安装。

  3. 类型sudo apt-get install xrdp。安装完成后,您就可以开始工作了。若要连接,请执行以下操作。

  4. 在 Pi 上,键入sudo ifconfig

  5. 如果您使用以太网电缆,请记下 eth0 块中的互联网地址(inet addr ),如果使用 Wi-Fi,请记下 wlan0 块中的互联网地址。

  6. On your laptop, open Remote Desktop Connection. This displays the connection dialog box, as shown in Figure 2-10.

    A457480_1_En_2_Fig10_HTML.jpg

    图 2-10

    Windows Remote Desktop Connection

  7. 从您的 Pi 输入 inet 地址。

  8. Click Connect. You should see the remote desktop screen with the xrdp login form (see Figure 2-11).

    A457480_1_En_2_Fig11_HTML.jpg

    图 2-11

    XRDP remote desktop login screen

  9. 输入您的用户名和密码。

  10. Click OK. This opens the desktop from your Pi (see Figure 2-12).

![A457480_1_En_2_Fig12_HTML.jpg](https://img-blog.csdnimg.cn/img_convert/239716a7781ac758d1a874573cb27ad2.jpeg)

图 2-12

Default Raspbian desktop viewed through a remote desktop session  

只要您的 Pi 的 IP 地址不变,您就不再需要键盘、鼠标或显示器来使用您的 Pi。

带 PuTTY 的 SSH

最常见的 SSH 客户端可能是 PuTTY。免费使用,可从 www.chiark.greenend.org.uk/~sgtatham/putty/download.html 下载。

您为 PuTTY 下载的文件是一个可执行文件,不需要安装。把它放在你的桌面或容易找到的地方。若要连接,请执行以下操作。

  1. Open the PuTTY interface (see Figure 2-13).

    A457480_1_En_2_Fig13_HTML.jpg

    图 2-13

    PuTTY configuration window

  2. 输入您的 Raspberry Pi 的 IP 地址。

  3. 单击打开。

  4. You will likely get a security warning , as shown in Figure 2-14, but we know that this is the proper connection, so click Yes.

    A457480_1_En_2_Fig14_HTML.jpg

    图 2-14

    Security warning on first SSH connection with PuTTY A terminal window opens, asking for your username and password.

  5. 输入您的用户名和密码。你现在应该看到终端提示,如图 2-15 所示。

就是这样。您现在通过 SSH 连接到您的 Raspberry Pi。你可以同时拥有多个连接,但是不要超过你所需要的。当你使用像机器人操作系统(ROS)这样的东西时,多重连接是很方便的。(别担心,还有一段路呢。)ROS 通过终端运行多个程序。每一个都需要自己的终端窗口。使用 PuTTY,您可以根据需要拥有任意多的远程终端连接。

A457480_1_En_2_Fig15_HTML.jpg

图 2-15

Open SSH connection

在网络上查找您的设备

要远程访问您的 Pi,您需要知道它在网络上的 IP 地址。通常,网络交换机从一个会话到另一个会话保留设备的 IP;然而,这并不能保证。

在网络上找到你的设备的 IP 地址可能会很棘手。如果您在家,并且可以访问路由器的管理面板,这可能是找到您的设备的最直接的方法。只需登录您的路由器,找到连接的设备列表,向下滚动,直到找到您的 Raspberry Pi 的主机名。

如果你需要找到 IP 地址,但不在家,有几种方法可以做到这一点。最简单的是在手机上使用 Nmap 应用。我在安卓手机上用一个叫 Fing 的应用。一旦手机连接到本地 Wi-Fi 网络,该应用就会扫描网络,并列出该网络上的所有其他设备。您可以向下滚动列表,直到找到您的主机名。

如果网络对您来说是陌生的,您的 Raspberry Pi 不会自动连接到它。这种情况让事情变得有点棘手。为了方便起见,出门前要做好准备。我是 Windows 用户;如果不是,您需要为您的操作系统查找正确的过程。我在旅行时用我的笔记本电脑做这个操作。它允许我远程进入 Pi 足够长的时间来连接到本地 Wi-Fi 并获得 wlan0 IP 地址。

请记住,IP 是由笔记本电脑分配的。您在此操作结束时获得的 IP 可能无法在任何其他机器上工作。

在 Windows 7 或更高版本的机器上,您可以执行以下步骤来直接远程访问您的 Pi 以获取其 IP 地址。如果您需要直接连接到 Pi 来建立新的 Wi-Fi 连接,请记下 IP 地址。你需要一根短的以太网电缆,它应该放在你的工具箱里。

确定您能够在设置了显示器、键盘和鼠标的情况下,或者通过 Wi-Fi 网络的远程连接来查看您的 Raspberry Pi。Pi 不能通过以太网电缆连接到网络,因为此操作需要该端口。

  1. 将以太网电缆连接到您的笔记本电脑。

  2. 将另一端连接到 Pi 上的以太网端口。

  3. 在 Pi 上打开一个终端窗口。

  4. 类型sudo ifconfig

  5. 找到 eth0 模块中的 inet 地址。

  6. 打开笔记本电脑上的终端窗口。您可以通过在开始菜单中搜索cmd来完成此操作。

  7. 在 Windows 终端中键入以下内容:

    ping <your.Pis.IP.address>
    
    

    <your.Pis.IP.address>是来自 Pi 的 eth0 IP 地址。

  8. 打开笔记本电脑上的远程桌面连接。

  9. 从 Pi 输入 IP 地址,然后按 Enter 键。

现在,您应该可以从笔记本电脑直接远程连接到 Raspberry Pi。请确保保存此 IP 地址,以便以后可以找到它。远程桌面连接应该记住它,但最好也将它保存在其他地方。

现在,无论您何时尝试连接到新的 Wi-Fi 网络,您都可以使用以太网电缆直接从笔记本电脑远程连接到您的 Pi。远程登录后,只需从可用网络列表中选择网络,然后输入密码(如果有)。

摘要

树莓派是为业余爱好者设计的。小型 Linux 计算机使它对许多不同类型的项目非常有用,但这意味着你需要学习一点 Linux。Raspberry Pi 基金会的开发人员提供了一个简单易用的 Debian Linux 版本,名为 Raspbian。

我们通过配置远程访问将基本安装向前推进了一步。这使你可以通过网络远程访问你的机器人,这意味着不再需要显示器和键盘。

三、Python 速成课

这本书的目的是挑战你建立一个简单的机器人,并随着时间的推移而扩展。这是有意为难的。它旨在提供一种实践经验,帮助你克服学习机器人最困难的部分:被技术吓倒。我将教你一些机器人的基础知识,就像我学习游泳一样——被扔进深水区,同时有更有经验的人在旁边看着,确保你不会淹死。

因此,我希望你能吸取你的经验,并通过自己的学习加以补充。我会带你去正确的方向,但不会有很多牵手。你必须自己填补空白,学习一些细节——尤其是一些特定应用的细节。

这篇 Python 入门也不例外。我将向您展示如何安装工具,使用编辑器,并编写一些简单的程序。我们将快速浏览程序结构、语法和格式问题、数据类型和变量,并直接进入 Python 的控制结构和一些面向对象的方面。如果这些听起来像是技术术语,不要担心,在本章结束之前你会明白的。

在这一章的最后,我不指望你能自己写程序。我希望你知道如何写代码,如何使用编辑器,如何编译和执行程序。最重要的是,你应该能够看别人的代码并且能够阅读它,对他们试图做的事情有一个基本的理解,并且能够识别构建模块。剖析他人的代码对快速学习很重要。这本书的一个前提是不要多此一举。你要做的大部分事情以前已经有人做过了,如果你稍微搜索一下就能找到。能够阅读和理解你的发现将有助于你达到目标。

就资源而言,这里有一些建议:

  • 社区对 Python 的支持非常好。Python 网站是学习和发展 Python 的宝贵资源。特别是,请务必查看 www.python.org/about/gettingstarted/ 的初学者页面。下一节我们将从这里开始。
  • 给自己找一本或两本关于 Python 的好书。这本书让你开始,但有很多细节不会涵盖。寻找关于 Python 中的设计模式和构建算法的不同方法的书籍,以使您的代码尽可能高效。找一些对你的申请有深入了解的书籍。
  • 不要认为你必须自己学习 Python,或者本书中的任何其他主题。外面有一个巨大的社区。寻找当地聚会、俱乐部和课程。找到你当地的黑客空间。我保证你会在那里找到能够帮助你的人。

Python 概述

Python 是一种高级编程语言,由吉多·范·罗苏姆在 20 世纪 80 年代末创建。它已经成为一种非常流行的通用语言,因为它灵活,相对容易学习和编码。在许多方面,Python 是一种非常宽容的语言,这有助于它的易用性。正如你将在本章后面看到的,Python 以一种对编程新手来说非常直观的方式管理数据。因此,它是一个非常受欢迎的编程基础教学工具。它使用变量管理大型数据集的独特方式也使它在不断发展的数据科学领域非常受欢迎。数据科学家可以导入大量数据,并用很少的代码对数据集执行操作。当然,Python 有一些特性,我们将在本章中更深入地探讨。

下载和安装 Python

首先,让我们讨论一些关于版本的事情。Python 本质上有两种风格:Python 2.7 和 Python 3。在 Python 3 中,创造者吉多·范·罗苏姆决定清理代码,而不太强调向后兼容性;因此,2.7 版本的一些代码根本无法在 3 版本中运行,反之亦然。Python 3 是当前版本,所有东西最终都会转移到它上面。事实上,在这一点上,几乎一切都有。在机器人学方面,最大的坚持者是 OpenCV,一个计算机视觉函数库的开源库,我们将在第九章中用到它。还有一些还没有完全迁移,所以您需要弄清楚您想要做什么,以及您需要的包是否已经被移植。我们将在我们的项目中使用 Python 2.7,因为您将在自己的研究中找到的许多例子都在 2.7 中。

如果你使用的是 Ubuntu 或 Debian Linux 系统,比如 Raspberry Pi,那就大功告成了。Python 工具已经安装完毕,可以使用了。大多数基于 Debian 的发行版安装 Python 作为基本映像的一部分。

如果您在 Windows 或另一个操作系统中跟随,您需要安装 Python。

  1. 导航到 www.python.org/about/gettingstarted/
  2. 单击下载。
  3. 如果您使用的是 Windows,请单击下载 Python 2.7.13。
  4. 如果您正在使用另一个操作系统,请从使用不同的操作系统查找 Python?这会将您带到相应的下载链接。
  5. 如果您想使用旧版本的 Python(出于某种奇怪的原因),您可以通过向下滚动页面找到合适的链接。
  6. 下载完成后,运行安装程序并按照屏幕上的指示进行操作。

Python 工具

有许多工具可以支持您的 Python 开发。像大多数编程语言一样,代码只是可以用任何文本编辑器编写的文本。可以在 Windows 机器上用记事本编写 Python 代码。我不会推荐它,但是你能做它。像 Notepad++这样的应用根据文件扩展名识别 Python 脚本,然后相应地突出显示代码。

你的选择范围很广。然而,在我们的练习中,我们将使用 Python 的每个安装都附带的本机工具:Python shell 和 Python 编辑器。

Python 外壳

Python shell 是 Python 解释器的一个接口。从技术上讲,如果你是命令行的爱好者,你可以启动一个终端窗口并调用 Python 解释器。然而,我们将使用随 Python 一起安装的 Python shell 接口,如图 3-1 所示。它为查看和执行命令提供了一个非常简洁的界面。

A457480_1_En_3_Fig1_HTML.jpg

图 3-1

The IDLE Python shell

当您打开本机空闲 IDE 时,Python shell 会启动。取决于你问谁,IDLE 或者代表集成开发环境,或者代表集成开发和学习环境。我喜欢后者,只是因为它对我来说更有意义。但本质上,它是 Python 解释器的窗口接口。它提供了一些在简单的命令行中无法获得的特性。

  • 简单的编辑功能,如查找、复制和粘贴
  • 语法突出显示
  • 带有语法突出显示的文本编辑器
  • 具有单步执行和断点的调试器

因为我们将在整本书中大量使用这个接口,所以谨慎的做法是学习更多关于 IDLE 接口及其提供的许多工具的知识。您可以从 https://docs.python.org/3/library/idle.html 的闲置文档页面开始。

Python 编辑器

IDLE 还有一个非常重要的方面:文本编辑器。我们将在整本书中使用它来编写我们的程序和模块。文本编辑器是 IDLE 的另一个方面,不是一个单独的程序,尽管它总是在一个单独的窗口中打开(见图 3-2 )。您可以在任何文本编辑器中编写 Python 程序,并且有许多支持 Python 的 ide。然而,正如我在上一节提到的,IDLE 接口提供了很多优势。

A457480_1_En_3_Fig2_HTML.jpg

图 3-2

The IDLE file editor

稍后您将会了解到,格式化在 Python 中非常重要。对于其他语言,如 C 和 Java,空白与编译器无关。空格、制表符、换行符和空行使代码对人们来说更具可读性。然而,在 Python 中,缩进表示代码块。IDLE 为您管理这一切。它自动缩进你的代码,减少由于不恰当的缩进而导致的语法错误的可能性。

还有几个工具可以帮助你编写代码。例如,当您键入时,IDLE 会显示一个适合您所在行的可能语句列表。有几种方法可以调用这个特性。很多时候,它会在你打字的时候自动弹出。这通常发生在函数调用中,可能性有限。您也可以在键入时按 Ctrl-space 来强制打开它。当您这样做时,您会看到一个可供选择的语句列表。当您选择其中一个语句时,它会为您补全单词,并为您提供其他可用的选项,例如任何适当的参数。这些被称为呼叫提示。

调用提示显示可访问函数的预期值,当您在函数名后键入"("时,调用提示会打开。它显示函数签名和文档字符串的第一行。它将保持打开状态,直到光标移出函数或输入结束的")"

上下文突出显示是通过颜色来实现的。当您键入代码时,一些单词会改变颜色。颜色是有意义的,是一种快速、直观的方式来验证你是否在正确的轨道上。以这种方式突出显示的上下文是输出、错误、用户输出和 Python 关键字、内置的类和函数名、类和定义后面的名称、字符串和注释。

让我们来看看实际情况。

  1. 空闲时打开。
  2. 单击文件➤新建文件。这将打开一个新的文本编辑器窗口。
  3. 类型pr
  4. 按 Ctrl-空格键。这将显示完成列表,单词print高亮显示。
  5. 类型(。这有几个作用。它选择突出显示的文本。在这种情况下print。它还显示打印功能的调用提示。
  6. 类型"Hello World"
  7. 在您键入结束语)后,calltip 会关闭。
  8. 按回车键。
  9. 将该文件另存为hello_world.py
  10. 按 F5 或从菜单中选择运行➤运行模块。

在 Python shell 窗口中,您应该会看到类似这样的内容:

RESTART: D:/Projects/The Robot Group/Workshops/Raspberry Pi Robot/hello_world.py
Hello World

如果您看到这个,那么您的代码是成功的。如果您收到某种类型的错误,请返回以确保您的代码如下所示:

print("Hello World")

哦,对了,你刚刚编写并运行了你的第一个 Python 程序。与其他语言相比,你会注意到 Python 的简单性。通常,在 C、C++或 Java 中,几行 Python 操作需要多几行。

Python 的禅

Python 的长期贡献者 Tim Peters 写了 Python 开发背后的管理原则。我认为它实际上适用于所有的代码,以及我们在机器人学中所做的一切,也许在生活中也是如此。它们作为复活节彩蛋被藏在 Python IDE 中。

A457480_1_En_3_Fig3_HTML.jpg

图 3-3

The Zen of Python

  1. 空闲时打开。
  2. 键入import this并按回车键。
  3. 它应该显示如图 3-3 所示的文本(但无论如何都要这样做)。

编写和运行 Python 程序

如果你按照你应该做的去做,那么你已经编写并运行了你的第一个 Python 程序。如果您没有跟上,不要担心,我们现在会再做一次,但是需要更多的编程。

你好世界

让我们添加一个简单的变量调用。我们会在不久的将来讨论变量。

  1. 打开hello_world.py

  2. 如果你是我前面提到的那些反叛者之一,请空闲地打开。

  3. 单击文件➤新建文件。

  4. 将该文件另存为hello_world.py。现在我们都在同一页上…

  5. 让程序看起来像这样:

    message = "Hello World"
    print(message)
    
    
  6. 保存文件。

  7. 按 F5 或从菜单中选择运行➤运行模块。

您应该会看到与之前相同的输出:

RESTART: D:/Projects/The Robot Group/Workshops/Raspberry Pi Robot/hello_world.py
Hello World

我们在这里所做的只是将文本从 print 函数移动到一个变量中,然后告诉 Python 打印该变量的内容。我将很快介绍变量。

基本结构

在我们开始研究程序细节之前,我们需要熟悉 Python 程序的结构。我们将看看程序的不同部分,如何使用缩进来格式化程序,以及如何使用注释来添加有意义的上下文。

程序部分

正如您所看到的,Python 程序没有太多必需的部分。大多数编程语言要求你至少创建某种主函数。对于 Arduino,它是loop()功能。在 C++里是main()。Python 没有这个功能。它会直接执行在文件中找到的任何命令。然而,这并不意味着它完全是线性的。我将在研讨会的后面讨论函数和类,但是我只知道解释器会扫描文件,并在执行其他命令之前构建它找到的任何函数和类。这是 Python 如此容易学习的原因之一。它不像大多数其他语言那样有严格的框架。

对了,对于编程语言纯粹主义者来说,Python 在脚本语言和编程语言之间游走,在脚本语言中一切都通过解释器来执行。一些代码被编译成可执行文件,如 C 和 C++。事实上,当我们开始构建模块时,这正是所发生的事情。然而,在本书中,我们通常通过解释器来运行它。

刻痕

随着我们在车间的工作,我们的程序变得更加复杂。特别是,我们将从代码块开始,代码块是组合在一起的命令,在函数或循环中执行,或者作为条件的一部分。这种结构对于编写有效的程序至关重要。

所有编程语言都有格式化代码块的语法。基于 c 的语言,包括 Java,使用花括号{}来包含代码块。Python 不会这样做。Python 使用缩进。代码块是缩进的,以表明它们是相关的块。如果块中的一行没有正确缩进,就会出现错误。这是我们使用闲置的关键原因之一。它会自动管理缩进。这并不意味着你,作为一个用户,不能修补程序;这只是意味着这种错误大大减少了。

随着课程的进行,你会看到缩进的重要性。同时,你要知道这很重要。

最后,我想对缩进和编辑器做一个简单的说明。文本编辑器使用不同形式的缩进。有些使用制表符,而有些使用两个或四个空格。您看不到正在使用的内容,因为这些是不可见的字符。当您从一个编辑器切换到另一个编辑器时,这会导致无尽的挫败感。更好的编辑器允许您选择 tab 键的工作方式(使用一个 tab 字符或多个空格,通常是四个)。默认情况下,IDLE 使用四个空格。

评论

随着时间的推移,注释代码变得越来越重要。这是一个众所周知程序员缺乏的领域。他们使用注释,但是他们经常含糊其辞,或者对程序的知识做出假设,这对于后来学习代码的人来说可能不成立。如果他们在其他形式的文档方面做得更好,这就不是问题了。但是,唉,他们不是。

撇开我的悲叹不谈,评论是很重要的,尤其是在你学习的时候。所以,要养成用评论的习惯。用它们来解释你在做什么,或者输入关于逻辑的小注释。

Python 中的注释是以#开头的任何一行。Python 忽略了从#到行尾的所有内容;例如:

# create a variable to hold the text
message = "Hello World"

# print the text stored in the variable
print(message)

在前面的代码中,我向我们的hello_world.py程序添加了两行注释。我还添加了一个空行来帮助使代码更容易阅读。如果您保存并运行这个程序,您将得到与之前完全相同的输出。

您还可以使用三重引号"""创建注释块。Python 编译器会忽略这些标记集之间的任何代码。你用它们打开和关闭街区。这允许你在多行上写尽可能多的信息。这种符号常用于文件开头的标题块。

"""
Hello World
the simplest of all programs
By: Everyone who has written a program, ever
"""

在编写代码之前,简单地用注释概述代码是一个好习惯。在你开始写之前,想想你需要你的代码做什么,以及你将如何去做。创建一个流程图,或者简单地写下实现目标的步骤。然后,在编写任何实际代码之前,在文件中将其转换成一系列注释。这有助于你在头脑中构建问题,并改善你的整体流程。如果你发现你正在重复的一个步骤,你可能就有了一个函数的候选者。如果你发现你引用了一个结构化的概念(比如机器人),你想赋予它特殊的属性和功能,你就有了一个类。稍后,我将进一步详细讨论函数和类。

运行程序

正如您之前看到的,有几种方法可以运行 Python 程序。

在空闲状态下,您只需按 F5 即可。您需要首先确保文件已保存,但这将运行您的文件。如果文件未保存,系统会提示您保存。它等同于从菜单栏中选择运行➤运行模块。

如果您的系统正确配置为从命令行运行 Python,您也可以从那里执行程序。您需要导航到文件的位置,或者在调用中使用完整的文件位置。要从命令行执行 Python 脚本,可以键入python,后跟要运行的文件。

> python hello_world.py
> python c:\exercises\hello_world.py
> python exercises\hello_world.py

这三个命令都将运行我们的 Hello World 程序,尽管其中两个是特定于操作系统的。第一个命令假设您正在文件存储的目录中执行该文件。第二个命令在 Windows 中运行程序,假设它保存在 C:\驱动器根目录下的一个名为exercises的文件夹中。第三个命令在 Linux 机器上运行程序,假设文件保存在主目录中名为exercises的文件中。

Python 编程

在接下来的几节中,我们将使用 Python shell 并直接输入命令。过了一会儿,我们又开始写程序文件。但是,现在,我们所做的一切都可以在 shell 窗口中演示。

变量

变量本质上是一个存储信息的便利容器。在 Python 中,变量非常灵活。不需要声明类型。类型通常是在您为其赋值时确定的。事实上,你通过给变量赋值来声明变量。当你从数字开始时,记住这一点很重要。在 Python 中,1 和 1.0 是有区别的。第一个数字是整数,第二个数字是浮点数。稍后会有更多内容。

以下是变量的一些通用规则:

  • 它们只能包含字母、数字和下划线
  • 它们区分大小写;比如variableVariable不一样。那以后会咬你一口的。
  • 不要使用 Python 关键字

除了这些严格的规则之外,这里还有一些提示:

  • 用尽可能少的字符使变量名有意义
  • 使用小写 L 和大写 o 时要小心,这些字符看起来非常类似于 1 和 0,会导致混淆。我不是说不要使用它们;确保你清楚自己在做什么。强烈建议不要将它们用作单字符变量名。

数据类型

Python 是一种动态类型语言。这意味着存储在变量中的数据类型直到程序被执行时才被检查,而不是在程序被编译时。这允许您在赋值之前推迟分配类型。然而,Python 也是强类型的,如果您试图执行对该数据类型无效的操作,它将会失败。例如,不能对包含字符串的变量执行数学运算。因此,跟踪变量引用的数据类型非常重要。

我将讨论两种基本的数据类型:字符串和数字。然后我将讨论一些更复杂的类型:元组、列表和字典。Python 允许您将自己的类型定义为类。我将在本章的最后介绍类,因为我们还需要先介绍一些其他的概念。

用线串

字符串是包含在引号中的一个或多个字符的集合。引号是表示字符串的方式。例如,“100”是一个字符串;100 是一个数字(更准确地说是一个整数)。你可以使用双引号或单引号(只要记住你用了什么)。您可以将一种类型的引号嵌套在另一种类型的引号中,但是如果您交叉使用引号,将会出现错误,或者更糟的是,会出现意想不到的结果。

这里有一个双引号的例子:

>>>print("This is text")
This is text

这里有一个单引号的例子:

>>>print('This is text')
This is text

下面是双引号内单引号的一个例子:

>>>print("'This is text'")
'This is text'

下面是单引号内双引号的一个例子:

>>>print('"This is text"')
"This is text"

三重引号用于跨越一个字符串中的多行。

这里有一个三重引号的例子:

>>>print("""this
is
text""")
this
is
text

如果您先对单引号进行转义,则可以使用单引号作为撇号。转义一个字符仅仅意味着你告诉解释器把一个字符看作一个字符串,而不是一个函数。

这里有一个转义引用的例子:

>>>print('This won't work')
File "<stdin>", line 1
        print('this won't work')
                        ^
SyntaxError: invalid syntax

>>>print('This won\'t error')
This won't error

您可以通过使整个字符串成为原始字符串来实现这一点。在下一个例子中,'/n'用于移动到新的一行。

下面是一个原始字符串的示例:

>>>print('something\new')
something
ew

>>>print(r'something\new')
something/new

字处理

操纵字符串有很多种方法。有些相当简单,比如串联——将字符串加在一起。不过,其中有些还是有点让人意外的。字符串被视为字符值的列表。在本章的后面,我们将更详细地探讨列表。然而,我们将使用列表的一些特性来处理字符串。

因为字符串是列表,类似于其他语言中的数组,所以我们可以引用字符串中的特定字符。像列表一样,字符串是零索引的。这意味着字符串的第一个字符位于零位置。

对于字符串,如列表,第一个字符位于索引[0]:

>>>robot = 'nomad'
>>>robot[0]
n

当使用负数时,索引从字符串的末尾开始并向后工作。

>>>robot[-1]
t

分割字符串允许您提取子字符串。对字符串进行切片时,需要提供两个用冒号分隔的数字。第一个数字是起始索引,第二个数字是结束索引。

>>>robot[0:3]
nom

请注意,切片时,第一个索引是包含性的,第二个索引是排他性的。在前面的示例中,索引[0]处的值返回“r”;而索引[3]处的值不是“0”。

切片时,如果您留下一个索引,则假定是字符串的开头或结尾。

>>>robot[:3]
nom

>>>robot[3:]
ad

将字符串相加称为串联。使用 Python,您可以轻松地将字符串添加到一起。这适用于字符串和字符串变量。您也可以将字符串相乘以获得有趣的效果。

你可以把两个字符串加在一起。

>>>print("Ro" + "bot")
Robot

您可以将字符串变量加在一起,如下所示:

>>>x = "Ro"
>>>y = "bot"
>>>z = x + y
>>>print(z)
Robot

您可以添加一个字符串变量和文字。

>>>print(x + "bot")
Robot

您可以将字符串文字相乘。

>>>print(2 * "ro" + "bot")
rorobot

然而,字符串的乘法只适用于文字。它对字符串变量不起作用。

我建议花些时间探索这些和其他字符串操作方法。更多信息,请访问 https://docs.python.org/3/tutorial/introduction.html#stringshttps://docs.python.org/3.1/library/stdtypes.html#string-methods

民数记

Python 中的数字有几种风格,最常见的是整数和浮点数。整数是整数,而浮点数是小数。Python 也使用值为 1 或 0 的布尔类型。这些常用作标志或状态,其中 1 表示“开”,0 表示“关”布尔值是整数的子类,在执行运算时被视为整数。

正如您所料,您可以对数字类型执行数学运算。通常,如果对一种类型执行算术运算,结果就是该类型。使用整数的数学通常会产生一个整数。但是,如果对整数执行除法,结果是浮点数。使用浮点运算会导致浮点运算。如果对这两种类型都执行算术运算,结果是浮点型。

两个整数相加得到一个整数。

>>>2+3
5

添加两个浮点导致一个浮点。

>>>0.2+0.3
0.5

将一个浮点数和一个整数相加会产生一个浮点数。

>>>1+0.5
1.5

减法和乘法的工作原理是一样的。

>>>3-2
1

>>>3-1.5
1.5

>>>2*3
6

>>>2*0.8
1.6

除法总是产生浮点数。

>>>3.2/2
1.6

>>>3/2
1.5

**运算符产生第一个数字的第二次幂。

>>>3**2
9

然而,Python 浮动有一个问题。解释程序有时会产生看似任意数量的小数位。这与浮点符号在 Python 中的存储方式以及解释器中的数学运算方式有关。

>>>0.2+0.1
0.30000000000000004

关于这种异常的更多信息,请访问 https://docs.python.org/3/tutorial/floatingpoint.html

列表

列表是按特定顺序排列的项目的集合。在其他语言中,它们通常被称为数组。你可以在列表中放入任何你想要的东西。存储在列表中的值不必是相同的数据类型。但是,如果您在一个列表中混合了多种数据类型,请确保您在使用它时知道您得到的是哪种类型。

当你处理字符串的时候,你处理的是列表。字符串本质上是一系列字符。因此,索引和切片也适用于列表。

使用方括号[]创建一个列表。

>>> robots = ["nomad","Ponginator","Alfred"]
>>> robots
['nomad', 'Ponginator', 'Alfred']

像字符串一样,列表也是零索引的。这意味着列表中的第一个元素位于位置 0,第二个元素位于位置 1,依此类推。您可以通过调用列表中的索引或位置来访问列表中的各个元素。

>>>robots[0]
'nomad'

>>>robots[-1]
'Alfred'

列表也可以切片。当列表被切片时,结果是一个包含原始列表子集的新列表。

>>>robots[1:3]
['Ponginator','Alfred']

使用切片和连接很容易添加、更改和删除列表成员。

本示例向列表中添加成员。

>>>more_bots = robots+['Roomba','Neato','InMoov']
>>>more_bots
['nomad', 'Ponginator', 'Alfred', 'Roomba', 'Neato', 'InMoov']

本示例更改列表中的成员。

>>>more_bots[3] = 'ASIMO'
>>>more_bots
['nomad', 'Ponginator', 'Alfred', 'ASIMO', 'Neato', 'InMoov']

本示例删除列表中的成员。

>>>more_bots[3:5] = []
>>>more_bots
['nomad', 'Ponginator', 'Alfred', 'InMoov']

将列表成员分配给变量:

>>>a,b = more_bots[0:2]
>>>a
'nomad'
>>>b
'Ponginator'

列表中会自动包含许多方法。例如,您可以强制名称的第一个字母大写。

>>> print(robots[0].title())
Nomad

正如我提到的,列表可以包含任何类型的数据,包括其他列表。事实上,当我们开始使用计算机视觉时,我们会频繁地使用列表来保存图像数据。

列表是 Python 的一个非常强大和重要的方面。访问 https://docs.python.org/3/tutorial/introduction.html#lists 花些时间探索列表。

元组

在使用 Python 时,您会经常听到术语 tuple。元组只是一种特殊的不能改变的列表。把一个元组想象成一个常量列表,或者一个常量列表。使用圆括号而不是方括号来声明元组。

元组是不可变的,这意味着一旦元组被创建,它就不能被改变。要更改元组的内容,必须创建一个新的元组。这是使用我们用于字符串和列表的相同切片技术来完成的。

>>> colors = ("red","yellow","blue")
>>> colors
('red', 'yellow', 'blue')
>>>colors2 = colors[0:2]
>>>colors2
('red','yellow')

注意,我们在分割元组时使用了列表符号,colors[0:2]而不是colors(0:2)。切片的结果仍然是一个元组。

然而,元组可以被替换。

>>>colors2 = (1,2,3)
>>>colors2
(1,2,3)

它们也可以用空元组替换。

>>>colors2 = ()
>>>colors2
()

字典

字典类似于列表,除了它允许您命名列表中的项目。这是通过使用键/值对来完成的。该键成为值的索引。这允许你给你的列表添加一些有意义的结构。它们对于保存参数或属性列表很有用。

字典是用花括号而不是方括号声明的。

>>> Nomad = {'type':'rover','color':'black','processor':'Jetson TX1'}
>>> print(Nomad['type'])
Rover

使用字典的方式与使用数组的方式非常相似。除了不是提供索引号,而是提供访问元素的键。

在使用字典之前,有几件事需要了解。

  • 该键必须是不可变的值,如数字或字符串。元组也可以用作键。

  • 一个键不能在字典中定义多次。像变量一样,键值是最后分配的。

    >>>BARB = {'type':'test-bed','color':'black','type':'wheeled'}
    >>>BARB
    {'color':'black','type':'wheeled'}
    
    

    在本例中,第一个'type'值被第二个值覆盖。

  • 字典可以作为值嵌套在其他字典中。在下面的例子中,我将正在进行的机器人项目 Nomad 的定义嵌入到我的机器人字典中。

    >>>myRobots = {'BARB':'WIP','Nomad':Nomad,'Llamabot':'WIP'}
    >>>myRobots
    {'BARB': {'color':'black','type':'wheeled'},'Nomad': {'color':'black','type':'wheeled'},'Llamabot':'WIP'}
    
    

当然,如果不能更新和操作字典中包含的值,那么字典就没有多大用处。对字典进行更改类似于对列表进行更改。唯一真正的区别是,您使用键而不是位置来访问各种元素。

要更新值,请使用键引用要更改的值。

>>>myRobots['Llamabot'] = 'Getting to it'
>>>myRobots
{'BARB': {'color':'black','type':'wheeled'},'Nomad': {'color':'black','type':'wheeled'},'Llamabot':'Getting to it'}

可以用del语句删除一个键/值对。

>>>del myRobots['Llamabot']
>>>myRobots
{'BARB': {'color':'black','type':'wheeled'},'Nomad': {'color':'black','type':'wheeled'}}

可以用 dictionary 类的copy方法复制一个字典。要访问copy方法,从字典名开始,在末尾添加.copy()

>>>workingRobots = myRobots.copy()
>>>workingRobots
{'BARB': {'color':'black','type':'wheeled'},'Nomad': {'color':'black','type':'wheeled'}}

要将一个字典附加到另一个字典的末尾,使用update方法。

>>>otherRobots = {'Rasbot-pi':'Pi-bot from book','spiderbot':'broken'}
>>>myRobots.update(otherRobots)
>>>myRobots
{'BARB': {'color':'black','type':'wheeled'},'Nomad': {'color':'black','type':'wheeled'},'Rasbot-pi':'Pi-bot from book','spiderbot':'broken'}

无类型

在处理从其他来源导入的类和对象时,有一种特殊的数据类型非常重要。这是 none 类型,它是一个空的占位符。当我们想声明一个对象,但后来又定义它时,就使用它。它也用来清空一个对象。在本章的后面,当我们讨论类的时候,你会看到 none 类型的作用。同时,要知道它是存在的,它本质上是一个空的占位符。

关于变量的最后一点说明

当您完成本节中的示例时,您正在处理变量。注意变量是如何接受您提供的任何值,并愉快地返回您指定的值的。如果将一个列表赋给一个变量,它将返回该列表—方括号和所有内容。这同样适用于元组、字典、字符串和数字。无论你分配给它什么,它就是你所得到的。当我们将一个字典嵌套在另一个字典中时,我们看到了这一点。通过简单地将字典名添加到另一个字典的定义中,我们将所有的值嵌入到新字典中。

我为什么指出这一点?

在本书的后面,当我们开始使用函数和类时,你将为你的变量分配复杂的数据结构。重要的是要知道,无论你赋给一个变量什么,它就包含什么,并且你可以应用任何适合该数据类型的方法或函数。

>>> robots = ["nomad","Ponginator","Alfred"]
>>> robots
['nomad', 'Ponginator', 'Alfred']
>>> myRobot = robots[0]
>>> myRobot
'nomad'
>>> myRobot.capitalize()
'Nomad'

我们在字符串变量myRobot上使用了字符串类的方法。方法是我们赋予类的功能。由于数据类型是一个内置的类,我们可以在变量上使用该类的方法。当我们在本章末尾开始处理类时,我会更详细地讨论方法。

控制结构

在这一节中,我们将探索如何给你的代码添加结构。你可能想要更多的控制,而不是一步一步地通过一个程序并执行遇到的每一行代码。这些控制结构允许您仅在特定条件存在时执行代码,并多次执行代码块。

在大多数情况下,向您介绍这些概念比试图描述它们要容易得多。

if 语句

if语句允许您在执行代码块之前测试一个条件。条件可以是计算结果为 true 或 false 的任何值或等式。

下一段代码遍历机器人列表,确定机器人是否是流浪者。

>>> for robot in robots:
        if robot=="Nomad":
                print("This is Nomad")
        else:
                print(robot + " is not Nomad")

This is Nomad
Ponginator is not Nomad
Alfred is not Nomad

同样,请注意键入时的缩进。IDLE 在以冒号结尾的每一行之后缩进另一级,这应该是表示一个新块的每一行,比如循环语句和条件语句。

同样重要的是要注意我们如何测试平等性。一个等号表示赋值。双等号告诉解释器比较是否相等。

>>> myRobot = "Nomad"
>>> myRobot == "Ponginator"
False
>>> myRobot == "Nomad"
True

以下是比较器列表:

| 等于 | `==` | | 不相等 | `!=` | | 不到 | `<` | | 大于 | `>` | | 小于或等于 | `<=` | | 大于或等于 | `>=` |

您还可以使用andor来测试多个条件。

>>> for robot in robots:
        if robot == "Ponginator" or robot == "Alfred":
                print("These aren't the droids I'm looking for.")

These aren't the droids I'm looking for.
These aren't the droids I'm looking for.

比较也经常用于确定一个对象是否存在或包含一个值。本质上,如果条件的计算结果不为 false、0 或 none,则条件的计算结果为 true。当您希望仅在对象存在的情况下执行一段代码时,这非常方便,例如当您通过串行端口或网络初始化传感器或连接时。

当您使用机器人技术时,有时您需要重复一段代码。无论是对一组对象执行一组指令,还是只要条件存在就执行一段代码,都需要使用循环。

循环允许您多次重复代码块来执行任务。有两种类型的循环:for循环和while循环。每一个都提供了特定的功能,这些功能对于编写高效的程序至关重要。

for 循环

一个for循环为列表中的每个元素执行一个代码块。值的集合——元组、列表或字典——被提供给for循环。然后,它遍历列表并执行代码块中包含的代码。当集合中的元素用完时,循环被退出,执行for块外的下一行代码。

if语句一样,您将想要作为循环的一部分运行的代码放入由缩进指示的块中。重要的是要确保你的缩进是正确的,否则你会得到一个错误。

当您将它输入 Python shell 时,请注意它对缩进做了什么。

在您输入了print命令并按下 Enter 键之后,您需要再次按下 Enter 键,这样 shell 就知道您已经完成了。

>>> for robot in robots:
        print(robot)

Nomad
Ponginator
Alfred

程序进入 robots 列表,取出第一个值Nomad,然后打印出来。由于这是块中的最后一行,解释器返回列表并提取下一个值。如此重复,直到列表中不再有值。此时,程序退出循环。

我倾向于用复数来命名我的列表。这允许我使用名称的单数形式来引用循环列表中的项目。例如,机器人元组中的每个元素都是一个机器人。

如果您想要遍历字典中的元素,您需要提供两个变量来存储各个元素。还需要使用 dictionary 类的items方法。这允许您依次访问每个键/值对。

>>>for name,data in Nomad.items():
        print(name + ': ' + data)

color: black
type: wheeled

您可以使用enumerate函数向for循环的输出添加一个连续的数值。

>>>for num,robot in enumerate(robots):
        print(num,robot)

(0, 'Nomad')
(1, 'Ponginator')
(2, 'Alfred')

while 循环

一个for循环为列表中的每个元素执行一段代码,而while循环执行一段代码,只要它的条件评估为真。它通常用于执行特定次数的代码,或者在系统处于特定状态时执行。

要在代码中循环特定的次数,可以使用一个变量来保存整数值。在下面的例子中,我们告诉程序只要 count 变量的值小于 5 就运行代码。

>>> count = 1
>>> while count < 5:
        print(count)
        count = count+1

1
2
3
4

我们首先声明一个变量count来保存我们的整数,我们给它赋值 1。我们进入循环,1 的值小于 5,因此代码将该值打印到控制台。然后我们将count的值增加 1。因为这是循环中的最后一条语句,并且由while条件评估的最后一个值小于 5,所以代码返回到while子句。count的值,现在是 2,仍然小于 5,所以代码再次执行。重复该过程,直到count的值为 5。五不小于五,所以解释器不执行块中的代码就退出循环。

如果我们忘记增加count变量,就会导致一个开放循环。因为count等于 1,并且我们从不递增它,所以count的值总是等于 1。一小于五,所以代码永远不会停止执行,我们需要按 Ctrl-C 来结束它。

它还被用作一种主循环,持续执行代码。你会在很多程序中看到以下内容:

while(true):

True 始终计算为 true;因此,该块中的代码会一直执行,直到程序退出。这被称为开环,因为没有闭环。幸运的是,有一种方便的方法可以退出开放循环。如果你发现自己处于一个开放循环中,或者你只是想随意退出一个程序,按 Ctrl-C。这将导致程序立即退出。你会经常用到这个。

这种技术也可以用来让程序等待一个特定的条件得到满足。例如,如果我们在继续之前需要一个串行连接,我们将首先启动 connection 命令。然后,我们可以等待连接完成,然后继续使用以下命令:

while(!connected):
        pass

感叹号,也叫砰,代表不。因此,在这种情况下,假设connected变量包含建立时评估为真的串行连接,只要它没有连接,我们就告诉程序执行包含在while块中的代码。

在这种情况下,我们告诉它执行的代码称为 pass,这是一个空命令。当你实际上不想做任何事情,但你需要一些东西的时候,就用它。因此,我们这样告诉系统:“当你没有连接时,不要做任何事情,直到你连接上为止。”

功能

函数是预定义的代码块,我们可以从程序中调用它来执行任务。在本章中,我们一直在使用print()函数。print()命令是 Python 中的内置函数。Python 中有许多预定义的函数,还有更多的函数可以使用模块来添加。有关可用函数的更多信息,请查看位于 https://docs.python.org/3/library/index.html 的 Python 标准库。

很多时候你都想创建自己的函数。函数有几个用途。

大多数情况下,您使用函数来包含要在整个程序中执行的代码。每当您发现自己在代码中重复相同的操作时,您就有了一个函数的候选对象。

功能也广泛用作一种内务管理形式。它们可以用来将长进程移动到主程序之外的地方。这可以使你的代码更容易阅读。例如,您可以将机器人的动作定义为函数。当您的主代码满足某个条件时,您只需调用该函数。比较这两个伪代码块:

while(true):
        if command==turnLeft:
              /*
              Lengthy list of instructions to turn left
              */
        if command==turnRight:
              /*
                Lengthy list of instructions to turn right
              */
        /* etc. */

while(true):
        if command==turnLeft:
                turnLeft()
        if command==turnRight:
                turnRight()
        /* etc. */

在第一个块中,移动机器人的代码包含在if语句中。如果左转需要 30 行代码(不太可能,但是请耐心等待),那么您的主代码将会多 30 行。如果向右转需要相同数量的代码,您将有另外 30 行代码——您必须遍历所有这些代码才能找到您要找的那一行。这变得非常乏味。

在第二个块中,要转换的代码被移动到一个单独的函数中。这个函数被定义在程序的其他地方,或者当我们讨论库和模块的时候你会知道,它可以存在于另一个文件中。这使得书写和阅读更容易。

定义函数

要定义自己的函数,需要创建函数名和包含要执行的操作的代码块。定义以关键字def开始,后面是函数名、括号和冒号。

让我们创建一个简单的函数:

>>> def hello_world():
        message = "Hello World"
        print(message)

>>> hello_world()
Hello World

在这段代码中,我们创建了一个简单的函数,它只打印消息“Hello World”。现在,每当我们想要打印该消息时,我们只需调用该函数。

>>> hello_world()
Hello World

为了让事情更有趣一点,我们可以为函数提供数据来使用。这些被称为争论。

传递参数

通常,我们希望给函数提供信息来使用或处理。为了提供信息,我们给函数一个或多个变量来存储这些信息,称为参数。

让我们创建一个问候用户的新函数。

>>> def hello_user(first_name, last_name):
        print("Hello " + first_name + " " + last_name + "!")

>>> hello_user("Jeff","Cicolani")
Hello Jeff Cicolani!

这里我们创建了一个名为 hello_user 的新函数。我们告诉它期望接收两条信息:用户的名和姓。函数定义中的变量名包含了我们想要使用的数据。该函数使用我们提供的两个参数打印问候语。

默认值

只要在声明函数时赋值,就可以为参数创建默认值。

>>> def favorite_thing(favorite = "robotics"):
        print("My favorite thing in the world is "+ favorite)

>>> favorite_thing("pie")
My favorite thing in the world is pie
>>> favorite_thing()
My favorite thing in the world is robotics

请注意,我们第二次调用该函数时,没有包含值。所以,函数只是使用了我们在创建函数时指定的默认值。

返回值

有时我们不仅仅希望函数自己做一些事情。有时我们希望它能给我们一些回报。这有助于将一个常见的计算移到一个函数中,或者如果我们希望函数验证它是否正确运行。许多内置函数和来自外部库的函数在函数成功时返回 1,在函数失败时返回 0。

要返回值,只需使用return关键字,后跟您想要返回的值或变量。请记住,return退出该函数,并将值提供给调用该函数的行。所以,确保你不要在return声明后做任何事情。

>>> def how_many(list_of_things):
        count = len(list_of_things)
        return count

>>> how_many(robots)
3

一个return语句可以返回多个值。要返回多个值,请用逗号分隔每个值。该函数将值放入一个可以被调用代码解析的元组中。

>>> def how_many(list_of_things):
        count = len(list_of_things)
        return count, 1

>>> (x, y) = how_many(robots)
>>> x
3
>>> y
1

通过模块添加功能

模块本质上是一个文件中的函数集合,可以包含在程序中。有数不清的模块让你的生活更轻松。许多模块都包含在标准 Python 安装中。其他的可以从不同的开发者那里下载。如果您找不到想要的内容,可以创建自己的自定义模块。

导入和使用模块

导入模块很容易。如您所见,您只需使用import关键字,后跟模块名称。这将加载该模块的所有功能供您使用。现在,要使用模块中的一个函数,您需要输入模块名,后跟函数。

>>> import math
>>> math.sqrt(9)
3.0

有些包非常大,您可能不想导入整个包。如果您知道程序中需要的特定函数,您可以只导入模块的该部分。

这将从数学模块导入sqrt函数。如果只导入函数,就不需要在函数前面加上模块名。

>>> from math import sqrt
>>> sqrt(9)
3.0

最后,您可以为导入的模块和函数提供一个别名。当您导入一个名称相当长的模块时,这变得非常方便。在这个例子中,我只是在偷懒:

>>> import math as m
>>> m.sqrt(9)
3.0
>>> from math import sqrt as s
>>> s(9)
3.0

内置模块

核心 Python 库为基本程序提供了许多功能。然而,还有其他开发人员和研究人员编写的更多可用功能。但是在我们进入第三方模块的奇妙世界之前,让我们看看 Python 附带了什么。

打开一个空闲实例,并键入以下内容:

>>> import sys
>>> sys.builtin_module_names

您应该得到如下所示的输出:

('_ast', '_bisect', '_codecs', '_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_collections', '_csv', '_datetime', '_functools', '_heapq', '_imp', '_io', '_json', '_locale', '_lsprof', '_md5', '_multibytecodec', '_opcode', '_operator', '_pickle', '_random', '_sha1', '_sha256', '_sha512', '_signal', '_sre', '_stat', '_string', '_struct', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', '_winapi', 'array', 'atexit', 'audioop', 'binascii', 'builtins', 'cmath', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', 'nt', 'parser', 'sys', 'time', 'winreg', 'xxsubtype', 'zipimport', 'zlib')

这是 Python 内置的模块列表,现在就可以使用。

要获得关于一个模块的更多信息,可以使用help()函数。它列出了当前安装并注册到 Python 的所有模块。(注意,为了打印,我必须截断列表。)

>>> help('modules')

Please wait a moment while I gather a list of all available modules...

AutoComplete        _random         errno            pyexpat
AutoCompleteWindow  _sha1           faulthandler     pylab
AutoExpand          _sha256         filecmp          pyparsing
Bindings            _sha512         fileinput        pytz
CallTipWindow       _signal         fnmatch          queue
...
Enter any module name to get more help.  Or, type "modules spam" to search
for modules whose name or summary contain the string "spam".

您还可以使用help()函数来获取特定模块的信息。首先,您需要导入模块。同样,为了简洁起见,下面的清单被截断了。

>>> import math
>>> help(math)
Help on built-in module math:

NAME
    math

DESCRIPTION
    This module is always available.  It provides access to the
    mathematical functions defined by the C standard.

FUNCTIONS
    acos(...)
        acos(x)

        Return the arc cosine (measured in radians) of x.
...
FILE
    (built-in)

你可以在 Python 文档网站 https://docs.python.org/3/py-modindex.html 上了解更多关于这些内置模块的信息。

扩展模块

除了每个 Python 安装都提供的内置模块之外,您还可以添加无数名为packages的扩展。幸运的是,Python 的优秀人员提供了一种了解第三方包的方法。更多信息请访问 https://pypi.python.org/pypi

一旦您找到了您想要或需要为您的应用安装的包,安装它最简单的方法就是使用 PIP。从 Python 2.7.9 和 Python 3.4 开始,下载中包含了 PIP 二进制文件。但是,由于软件包在不断发展,您可能需要升级它。如果一切安装和配置正确,您应该能够从命令行做到这一点。

  1. 打开终端窗口。

  2. 在 Windows 中,键入

    python -m pip install -U pip
    
    
  3. 在 Linux 或 macOS 中,键入

    pip install -U pip
    
    

一旦完成,您就可以使用 PIP 了。请记住,您将从终端运行 PIP,而不是从 Python shell 中运行。

在这个演示中,我们将安装一个用于绘制数学公式的包。matplotlib 是一个非常流行的使用 Python 可视化数据的包。此软件包的实际使用超出了本研讨会的范围。有关使用 matplotlib 的更多信息,请访问他们的网站 https://matplotlib.org

要安装新的软件包,请键入

pip install matplotlib

这将安装 matplotlib 库供您使用。

自定义模块

如果您有几个一直在使用的函数(通常称为帮助函数),您可以将它们保存在一个名为myHelperFunctions.py的文件中。然后,您可以使用import命令使这些功能在另一个程序中可用。

一般来说,您将要导入的自定义模块文件保存在与您正在使用的程序相同的文件位置。这是确保编译器能够找到文件的最简单也是最好的方法。可以将该文件保存在其他地方,但是您可以包含该文件的完整路径,或者对系统路径变量进行更改。现在,将您创建的任何模块文件保存在您的工作目录中(与您正在处理的程序在同一个位置)。这有助于你避免任何额外的心痛。

到目前为止,我们一直使用 IDLE shell。让我们创建一个自定义模块文件,然后将其导入到另一个程序中。

  1. 空闲时打开。

  2. 单击文件➤新建文件。这将打开一个新的文本编辑器窗口。

  3. 在新文件窗口中,点击文件➤保存并将其命名为myHelperFunctions.py

  4. 输入以下代码:

    def hello_helper():
            print("I'm helper. I help.")
    
    
  5. 保存文件。

  6. 单击文件➤新建文件创建新的代码文件。

  7. 键入以下内容:

    import myHelperFunctions
    myHelperFunctions.hello_helper()
    
    
  8. 在保存myHelperFunctions.py的同一目录下,将文件另存为hello_helper.py

  9. 按 F5 或从菜单中选择运行➤运行模块。

在 shell 窗口中,您应该看到以下内容:

I'm helper. I help.

班级

现在我们来看看好东西:类。类只不过是代码中物理或抽象实体的逻辑表示;例如,一个机器人。robot 类创建了一个向程序描述物理机器人的框架。你如何描述它完全取决于你,但它表现在你如何构建类。这种表达是抽象的,就像机器人这个词代表了机器人概念的抽象一样。如果我们站在一个满是机器人的房间里,我说,“把机器人递给我,”你的反应很可能是,“哪个机器人?”这是因为机器人这个词适用于房间里的每一个机器人。但是,如果我说,“把 Nomad 递给我”,你就会知道我说的那个机器人。Nomad 是机器人的一个实例。

这就是一个类的用法。从定义类开始。你可以通过构造你想要表现的实体的抽象来做到这一点;在这个例子中,是一个机器人。当您想要描述一个特定的机器人时,您可以创建一个应用于该机器人的类的实例。

关于类有很多要学的,但是下面是你需要知道的关键事情。

  • 一个类由称为方法的函数组成。方法是类中执行工作的函数。例如,你可能在机器人类中有一个名为drive_forward()的方法。在这个方法中,你添加代码使机器人向前行驶。
  • 一个方法总是需要self参数。此参数是对类实例的引用。
  • self总是一个方法的第一个参数。
  • 每个类都必须有一个叫做__init__的特殊方法。创建实例时会调用__init__方法,它会初始化该类的实例。在这个方法中,您执行类运行所需的任何操作。大多数情况下,您为类定义属性。
  • 类的属性是类中描述某些特性的变量。例如,对于 robot 类,我们想命名一些功能属性,比如方向和速度。这些是在__init__方法中创建的。

有几种类型的方法:

  • Mutator 方法:这些方法改变类中的值。例如,setters 是一种设置属性值的 mutator 方法。
  • 访问器方法:这些方法访问类中的属性。
  • 助手方法:这些方法包括在类中执行工作的任意数量的方法。例如,强制的__init__方法是一种叫做构造函数的助手。助手方法是在一个类中执行工作的任何东西,通常用于其他方法;例如,在输出前格式化字符串的方法。
创建一个类

在深入研究并开始编写代码之前,我建议您花一点时间来计划您将要构建的内容。这并不需要一个详尽的计划,但是在你开始构建之前,至少要有一个大概的轮廓。

规划

做计划最简单的方法是在一张纸上,但是如果你喜欢数字,你最喜欢的文本编辑器也可以。你想列一个课程清单或大纲。我们的示例类是针对一个模拟的轮式机器人的,所以我们想列出描述我们的机器人的属性,然后列出机器人将执行的动作。这些是我们的方法。

初始样本机器人类别
  • 属性
    • 名字
    • 描述
    • 原色
    • 物主
  • 方法
    • 向前行驶
    • 向后开
    • 向左转
    • 向右转

当你在写提纲的时候,想象你将如何使用每一种方法。你需要什么信息,如果有的话?它将返回什么信息(如果有)?如果你的方法需要参数形式的信息,有默认值吗?如果是这样,是否希望保留以编程方式更改默认值的能力?根据我的经验,最后一个问题的答案几乎总是肯定的。

所以,带着这些问题,让我们重温一下大纲。

初始样本机器人类别
  • 属性
    • 名字
    • 描述
    • 原色
    • 物主
    • 默认速度(默认值:125)
    • 默认持续时间(默认值:100)
  • 方法
    • 向前行驶(参数:速度)(返回:无)
    • 向后行驶(参数:速度)(返回:无)
    • 左转(参数:持续时间)(返回:无)
    • 右转(参数:持续时间)(返回:无)
    • 设定速度(参数:新速度)(返回:无)
    • 设置持续时间(参数:新持续时间)(返回:无)

正如您所看到的,在回顾了大纲之后,我们添加了一些新的属性和一些新的方法。默认速度为 0 到 255 之间的整数值。在本书的后面,我们用这个值来设置电机控制器的速度。半速是 125。默认持续时间是机器人移动的时间,以毫秒为单位。值 100 大约是 1/10 秒。我们还添加了两种方法来设置这两个属性的值。

在大多数编程语言中,属性是私有的,这意味着它们只能从类中包含的代码访问。因此,您可以创建get()set()方法来查看和更改这些值。在 Python 中,属性是公共的,可以通过简单的class.attribute调用来访问或更改。Python 属性不能成为私有属性;然而,Python 中的传统是在希望私有的属性前面加上下划线。这向其他开发人员表明,该属性应该被视为私有属性,不能在类的方法之外进行修改。

所以,严格来说,set speed 和 set duration 方法并不是严格需要的。如果我们想表明这些属性是私有的,并且只应使用方法进行更新,那么我们在名称前面加上一个下划线,如下所示:

_speed
_duration

您可以在代码中的任何地方创建一个类。类如此有用的原因是它们封装了允许您轻松地将其从一个项目移植到下一个项目的功能。因此,最好创建一个类作为它自己的模块,并将其导入到您的代码中。这就是我们在这里要做的。

让我们建立我们的机器人类,然后使用它。

  1. 创建一个新的 Python 文件,并将其另存为robot_sample_class.py。我们将从声明我们的类并创建所需的构造函数__init__开始。现在,我们需要__init__做的就是初始化属性并将值从参数移动到属性。注意,我们已经声明了speedduration的默认值分别为 125 和 100。

  2. 输入下面的代码:

    class Robot():
        """
        A simple robot class
        This multi-line comment is a good place
        to provide a description of what the class
        is.
        """
    
        # define the initiating function.
        # speed = value between 0 and 255
        # duration = value in milliseconds
        def __init__(self, name, desc, color, owner,
                    speed = 125, duration = 100):
                # initilaizes our robot
            self.name = name
            self.desc = desc
            self.color = color
            self.owner = owner
            self.speed = speed
            self.duration = duration
    
    

    初始化完成后,让我们看看如何编写我们的方法。如前所述,方法只是包含在类中的函数,在类中执行工作。由于我们目前没有要控制的机器人,所以我们简单地将确认消息打印到 shell 来模拟我们的机器人。

    def drive_forward(self):
            # simulates driving forward
            print(self.name.title() + " is driving" +
                    " forward " + str(self.duration) +
                    " milliseconds")
    
    def drive_backward(self):
            # simulates driving backward
            print(self.name.title() + " is driving" +
                    " backward " + str(self.duration) +
                    " milliseconds")
    
    def turn_left(self):
    # simulates turning left
            print(self.name.title() + " is turning " +
                    " right " + str(self.duration) +
                    " milliseconds")
    
    def turn_right(self):
            # simulates turning right
            print(self.name.title() + " is turning " +
                    " left " + str(self.duration) +
                    " milliseconds")
    
    def set_speed(self, speed):
            # sets the speed of the motors
            self.speed = speed
            print("the motor speed is now " +
                    str(self.speed))
    
    def set_duration(self, duration):
            # sets duration of travel
            self. duration = duration
            print("the duration is now " +
                    str(self. duration))
    
    
  3. 保存文件。现在我们已经创建了新的Robot类,我们将使用它将 Nomad 定义为一个Robot

  4. 创建一个新的 Python 文件,并将其另存为robot_sample.py。我们将从导入robot_sample_class代码开始,然后用它来创建一个名为 Nomad 的新机器人。

  5. 输入下面的代码:

    import robot_sample_class
    my_robot = Robot("Nomad", "Autonomous rover",
            black", "Jeff Cicolani")
    
    

    使用类定义创建类的新实例称为实例化。注意,我们没有提供最后两个参数的值,speedduration。因为我们为这些参数提供了默认值,所以不需要在实例化期间提供值。如果我们没有提供缺省值,当我们试图运行代码时会得到一个错误。对于我们的新机器人实例,让我们做一些工作。

    print("My robot is a " + my_robot.desc + " called " + my_robot.name)
    
    my_robot.drive_forward()
    my_robot.drive_backward()
    my_robot.turn_left()
    my_robot.turn_right()
    my_robot.set_speed(255)
    my_robot.set_duration(1000)
    
    
  6. 保存文件。

  7. 按 F5 运行程序。

在 Python shell 窗口中,您应该会看到类似这样的内容:

>>> ======================RESTART====================
>>>
My robot is an autonomous rover called Nomad
Nomad is driving forward 100 milliseconds
Nomad is driving backward 100 milliseconds
Nomad is turning left 100 milliseconds
Nomad is turning right 100 milliseconds
the motor speed is now 255
the duration is now 1000

式样

在我们结束这一章之前,我想花点时间讨论一下代码的样式。我们已经看到了缩进的重要性,它必须符合严格的准则来表示代码块等等。但是在一些领域,你可以影响不太重要的风格决定。当然,Python 社区中有一些传统是值得推荐的。

Python 的创建者和初级开发者提出了一些最佳实践。你可以在 www.python.org/dev/peps/pep-0008/ 的 Python 风格指南中读到他们所有的建议。我建议在你养成一些坏习惯(像我一样)之前,通读一下风格指南并实践他们的建议。现在,让我们关注如何命名变量、函数和类。

空白行

在代码块之间留出空行以实现逻辑上的、视觉上的分离是一个好主意。它使你的代码更容易阅读。

评论

在代码中编写注释。勤做,啰嗦。当您稍后回过头来阅读您的代码时(为了调试或者为了在另一个项目中重用它),您会想知道当代码被编写时您在想什么,以及您试图用它做什么。

如果你的代码被公之于众,被其他人阅读或审阅,他们也会需要评论。Python 是一个社区,代码共享频繁。良好的注释和描述的代码非常受欢迎。

命名规格

如何命名变量、函数和类是个人的决定。做你最舒服的事。Python 是一种区分大小写的语言。在一个地方使用大写字母而不在另一个地方使用会产生两个不同的变量和无尽的沮丧。

《样式指南》中没有提到常见的变量名,尽管惯例是使用混合大小写命名。大小写混合的名称以小写字符开头,但名称中的每个单词都是大写的;比如myRobots

函数和模块应该是小写的。为了便于阅读,在单词之间使用下划线。所以我们的hello world函数被命名为hello_world

类应该用大写字母命名。顾名思义,CapWords 将每个单词的首字母大写,包括名称中的第一个字符。这种风格更普遍地被称为骆驼案件。

最后,列表和其他集合应该是多元化的。这表明该变量代表多个对象。例如,robots是机器人的列表。如果我们处理列表中的单个项目,它看起来会像这样:

robot = robots[0]

摘要

我们在整本书中都使用 Python。这是一种非常容易学习的语言,并且它提供了很多强大的特性。很多软件开发者认为 Python 速度慢。但是当它在某些领域慢的时候,它会在其他领域弥补更多的时间,当我们在第九章开始使用计算机视觉的时候你会看到。

四、树莓派 GPIO

前几章介绍了 Raspberry Pi 硬件,您学习了如何使用 Python 对其进行编程。您安装了操作系统,对其进行了配置以供您使用,并设置了远程访问,这样您就可以在不直接连接键盘、鼠标和显示器的情况下对 Pi 进行编程。您学习了 Python 程序的基本结构、语法,以及足够开始编写程序的语言知识。

接下来,您将学习如何使用 Raspberry Pi 的 GPIO 接口与物理世界进行交互。这对机器人来说至关重要,因为这是处理器检测周围发生的事情并对外界刺激做出反应的方式。如果没有探测和作用于物理世界的能力,任何智能自主都是不可能的。

树莓派 GPIO

有几种方法可以连接到 Raspberry Pi。到目前为止,最简单的方法是通过内置在主板上的 USB 端口。USB 端口提供了四个串行连接,通过它们您可以访问外部组件,比如我们用来设置 Pi 的键盘和鼠标。然而,USB 端口需要特殊的硬件来将串行命令转换成操作设备所需的信号。Raspberry Pi 有一个更直接的连接外部设备的方法:GPIO 头。

GPIO 是电子设备与外界的接口。接头通常是指电路板上允许使用某些功能的一组引脚。GPIO 接头是一对 20 针的行,沿着电路板的一边延伸(见图 4-1 ),称为 40 针接头。

A457480_1_En_4_Fig1_HTML.jpg

图 4-1

Raspberry Pi with 40-pin header

务必注意,接头提供了与电路板上电子器件的直接连接。这些销既没有缓冲也没有内置安全功能。这意味着,如果你连接错误或使用错误的电压,你可能会损坏你的 Pi。在使用割台之前,您需要了解以下事项:

  • 虽然树莓派 采用 5 伏 USB 微型适配器供电,但电子设备为 3.3 伏。这意味着您需要注意传感器使用的电压。
  • GPIO 引脚上提供两种电压:5V 和 3.3V。请注意使用哪种电压,尤其是在试图通过 GPIO 为 Pi 供电时。
  • 可以通过 5V GPIO 引脚之一为 Raspberry Pi 供电;然而,没有提供电路保护和调节。如果提供的电压过高,或者出现电流尖峰,电路板可能会损坏。如果必须使用 GPIO 引脚为电路板供电,请务必提供外部稳压器。
  • GPIO 头有两种编号模式:板和 BCM。这意味着有两种不同的方法来引用代码中的管脚;你决定使用哪一个通常取决于你自己。您只需要记住您选择了哪个模式。

引脚编号

正如我提到的,40 引脚接头有两种编号模式:电路板和 BCM。

电路板编号只是按顺序对引脚进行编号。引脚 1 是最靠近 micro SD 卡的引脚,引脚 2 是最靠近 Pi 外边缘的相邻引脚。编号继续以这种方式进行,奇数编号的引脚在内侧,偶数编号的引脚在外侧。引脚 40 是电路板边缘的引脚,靠近 USB 端口。

BCM 编号远没有这么简单。BCM 代表博通,驱动 Pi 的 SoC(片上系统)的制造商。在树莓派 2 上,处理器是 BCM2836 在树莓派 3 上,是 BCM2837。BCM 编号是指 Broadcom 芯片的引脚编号,不同版本之间可能会有所不同。BCM2836 和 BCM2837 具有相同的引脚排列,因此 Pi 2 和 Pi 3 没有区别。

为了将电子元件连接到 40 针接头,我们将使用 Adafruit T-Cobbler Plus 和试验板。T-Cobbler 将 pin 信息印在板上,以便快速参考;然而,T-Cobbler 使用 BCM 编号。因此,我们将使用 BCM 编号。

连接到树莓派

有几种方法可以将接头的引脚连接到其他设备。我们将使用的电机控制器就是一个直接位于接头顶部的电路板。在 Raspberry Pi 术语中,这些板被称为帽子或盘子。

另一种选择是使用跳线直接连接到引脚。对于许多人来说,这是在原型开发期间的首选方法。

我更喜欢第三种方法,那就是使用 Adafruit 的另一个叫做 Pi 鹅卵石的板子。有几个版本的补鞋匠,但我更喜欢树莓派的 Adafruit T-Cobbler Plus(见图 4-2 )。该板设计用于通过带状电缆连接到试验板。它使用 40 引脚接头,垂直于插入试验板的引脚。这将带状电缆附件从试验板上移开,以便更好地接近孔。

A457480_1_En_4_Fig2_HTML.jpg

图 4-2

T-Cobbler mounted on the breadboard

使用补鞋匠的一个优点是引脚断点标记清晰。当我们开始构建我们的电路时,很容易就能看到你到底在连接什么。这也使得识别哪个管脚正被用于你的代码变得更加容易。当您将引脚 21 声明为输出引脚时,您将确切知道它是电路板上的哪个引脚。

Raspberry Pi 的 GPIO 局限性

在使用 GPIO 时,有几件事情需要记住。

首先,我们设置的 Raspberry Pi 不是一个实时设备。Debian Linux 是一个完整的操作系统,从硬件中抽象出许多层。这意味着对硬件的命令不是直接的。相反,在 CPU 将命令发送到电路板之前和之后,命令会经过几次操作。Python 在另一个抽象层中运行。每一层都会引入一定程度的滞后。我们一般感觉不到它,但它可以在机器人操作中产生巨大的差异。有一些 Debian 发行版更实时,是为工业应用设计的,但是我们正在使用的标准 Raspbian 版本不是其中之一。

第二,Pi 上没有模拟输入。嗯,有一个,但它是与串行端口共享的,我们以后可能会将它用于其他用途。因此,最好接受没有模拟输入引脚的事实。你会在第五章中看到为什么这很重要。

第三,Pi 只有两个支持 PWM 的引脚。PWM 代表脉宽调制,这是我们向外部设备发送不同信号的方式。这意味着接头上只有两个引脚可以模拟模拟输出。这两个引脚还与 Pi 的音频输出共享,这不是最佳选择。

好消息是,所有这些问题都有一个简单的解决方案,只需引入一个实时的外部微控制器,提供多个模拟输入,并提供两个以上的 PWM 输出。我们将在第五章中使用 Arduino。Arduino 基本上是 AVR AT 系列微控制器的原型板。这些芯片直接连接到硬件,没有大多数 SoC 处理器中的抽象层,如 Pi 上的那些。使用 Arduino 还有其他好处,我会在第五章中讨论。

用 Python 访问 GPIO

硬件只是等式的一部分。我们将使用新的 Python 技能来编程我们想要的行为。为了做到这一点,我们将使用 RPi。GPIO 库。你会从第三章回忆起,库是提供额外功能的类和函数的集合。

在机器人技术中,一个新的硬件、传感器或其他组件通常都有一个库,可以让你更容易地使用它。有时候库是通用的,比如 RPi。GPIO 其他时候,该库是为特定设备制作的。例如,我们将在第七章中使用特定于电机控制器板的库。当你给你的机器人增加更多的硬件时,你经常需要从制造商的网站上下载新的库。当我们开始使用电机控制器时,您将会看到这一点。

GPIO 库提供了访问 GPIO 管脚的对象和函数。Raspbian 附带了安装的库,所以应该可以使用了。有关如何使用该软件包的更多信息,请访问 https://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage/

要使用 GPIO 库,我们需要做两件事:导入包,然后告诉它我们将使用哪种模式来访问管脚。如前所述,有两种模式——电路板和 BCM——从本质上告诉系统使用哪个编号参考。

电路板模式参考树莓派 的 P1 标题上的编号。由于这种编号保持不变,为了向后兼容,您无需根据电路板版本更改代码中的引脚编号。

相比之下,BCM 模式指的是 Broadcom SoC 的引脚编号,这意味着在新版 Pi 上,引脚布局可能会改变。幸运的是,在 Pi 2 中使用的 BCM2836 和 Pi3 中使用的 BCM2837 之间,这种引脚布局没有改变。

出于我们的目的,我们将使用 BCM 模式——因为这就是 T 形鞋匠上所展示的。

每个使用 GPIO 头的程序都包含下面两行代码:

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

简单输出:LED 示例

最简单的例子就是无处不在的硬件版“Hello World”——闪烁的 LED。我们的第一个 GPIO 项目是将 LED 连接到 Pi,并使用 Python 脚本使 LED 闪烁。让我们从连接电路开始。为此,您需要一个试验板、T 形补鞋匠、一个 LED、一个 220 欧姆(ω)电阻和两根用作跳线的短导线。

连接电路
  1. Attach the T-Cobbler as shown in Figure 4-3. One row of pins should be on either side of the split in the board. The placement is up to you; however, I generally attach it such that the ribbon cable header is off the board. This allows maximum access to the breadboard.

    A457480_1_En_4_Fig3_HTML.jpg

    图 4-3

    Circuit layout for the LED example

  2. 在地轨和一个空的 5 孔轨之间连接一个 220ω电阻。

  3. Connect the LED cathode to the same rail as the resistor. The cathode is the pin closest to the flat side of the LED. On some LEDs, this pin is shorter than the other pin (see Figure 4-4).

    A457480_1_En_4_Fig4_HTML.jpg

    图 4-4

    LED polarity

  4. 将 LED 阳极连接到另一个空的 5 引脚轨道。

  5. 将一根跳线从阳极的供电轨连接到与 T 形补钉上的引脚 16 相连的供电轨。

  6. 从 LED 连接的接地轨和连接到 T 形补鞋匠的任何接地引脚的轨连接一根跳线。

如果您想在进入代码之前测试 LED,可以将跳线从引脚 16 移至其中一个 3.3V 引脚。如果您的 Pi 已打开,LED 将会亮起。在继续之前,请确保将跳线移回第 16 针。

编写代码

这个项目的代码非常简单。它是用 Python 3 写的。尽管代码在任一版本中都可以工作,但其中一行在 Python 2.7 中不能工作。具体来说,末尾的打印行使用了end参数,这是不兼容的。如果您使用的是 Python 2.7,则需要省略此参数。

end参数用一个/r替换附加到每一个打印行的默认/n/r是回车,与/n代表的新行相反。这意味着光标返回到当前行的开头,任何新文本都会覆盖前面的字符。但是,它不会首先清除该行。因此,我们在新文本的末尾添加任意数量的空格,以确保之前的所有文本都被完全删除。

GPIO 命令访问系统级内存。所有系统级命令必须以超级用户或超级用户权限运行。这意味着你需要用sudo运行 Python 或者授予自己永久的 root 权限,这可能很危险。一旦我们编写了代码,我们将从命令中执行。在我们这样做之前,我们必须使文件可执行,但是从终端做起来很简单。

首先,让我们在终端上使用 IDLE 或创建一个新的 Python 3 文件。

如果使用 IDLE,请执行以下操作:

  1. Python 3 的 Open IDLE。
  2. 单击新建。
  3. 将文件另存为项目文件夹中的gpio_led.py

如果使用终端,请执行以下操作:

  1. 打开终端窗口。

  2. 导航到您的项目文件夹。在我的圆周率上,是$ cd ~/TRG-RasPi-Robot/code

  3. 类型touch gpio_led.py

  4. 类型idle3 gpio_led.py。这将在 Python 3 的空闲 IDE 中打开空文件。

  5. 一旦你的文件被创建并且你在空闲的编辑器中,输入下面的代码:

    # GPIO example blinking LED
    
    # Import the GPIO and time libraries
    import RPi.GPIO as GPIO
    import time
    
    # Set the GPIO mode to BCM and disable warnings
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    # Define pins
    led = 16
    
    GPIO.setup(led,GPIO.OUT)
    
    # Make sure LED is off
    GPIO.output(led,False)
    
    # Begin Loop
    while True:
    
        # Turn LED on
        GPIO.output(led,True)
    
        # Wait 1 second
        time.sleep(1)
    
        # Turn LED off
        GPIO.output(led,False)
    
        # Wait 1 second
        time.sleep(1)
    
    
  6. 保存文件。

接下来,我们将使用终端使文件可执行,然后运行它。

  1. 打开一个新的终端窗口,并导航到您的项目文件夹。
  2. 类型chmod +x gpio_led.py。这使得文件可执行。
  3. 要运行代码,请键入sudo python3 gpio_led.py

这就是了:一个闪烁的 LED。你好世界。

脉冲宽度调制

尽管 Pi 的 GPIO 头上只有两个 PWM 引脚,并且您可能不会使用它们,但知道如何正确控制它们还是很有用的。板上的两个 PWM 引脚是 18 和 19。在本例中,我们将 LED 设置为使用引脚 18,并向 LED 发送脉冲。

连接电路

好吧,这是复杂的部分。要设置这个电路,你需要非常仔细地遵循这些指示。使用我们为 LED 练习构建的电路。

  1. 将跳线从引脚 16 移至引脚 18。

唷。现在我们已经完成了所有这些,让我们编码。

编写代码

创建一个新的 Python 3 文件。

如果使用 IDLE,请执行以下操作:

  1. Python 3 的 Open IDLE。
  2. 单击新建。
  3. 将文件另存为项目文件夹中的gpio_pwm_led.py

如果使用终端,请执行以下操作:

  1. 在“终端”窗口中,导航到您的项目文件夹。我的圆周率上,是$ cd ~/TRG-RasPi-Robot/code

  2. 类型touch gpio_pwm_led.py

  3. 类型idle3 gpio_pwm_led.py。这将在 Python 3 的空闲 IDE 中打开空文件。

  4. 一旦你的文件被创建并且你在空闲的编辑器中,输入下面的代码:

    # GPIO example blinking LED
    
    # Import the GPIO and time libraries
    import RPi.GPIO as GPIO
    import time
    
    # Set the GPIO mode to BCM and disable warnings
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    # Define pins
    pwmPin = 18
    
    GPIO.setup(pwmPin,GPIO.OUT)
    pwm = GPIO.PWM(pwmPin,100)
    
    # Make sure LED is off
    pwm.start(0)
    
    # Begin Loop
    while True:
    
        count = 1
        # begin while loop to brighten LED
        while count < 100:
    
            # set duty cycle
            pwm.ChangeDutyCycle(count)
    
            # delay 1/100 of a second
            time.sleep(0.01)
    
            # increment count
            count = count + 1
    
        # begin while loop to dim LED
        while count > 1:
    
            pwm.ChangeDutyCycle(count)
    
            time.sleep(0.01)
    
            # set duty cycle
            pwm.ChangeDutyCycle(count)
    
            # delay 1/100 of a second
            time.sleep(0.01)
    
            # decrement count
            count = count – 1
    
    
  5. 打开一个新的终端窗口,并导航到您的项目文件夹。

  6. 键入chmod +x gpio_pwm_led.py使文件可执行。

  7. 要运行代码,请键入sudo python3 gpio_pwm_led.py

您的 LED 现在应该在闪烁。要改变脉冲的频率,改变time.sleep()函数调用中的值。

简单输入

既然我们已经看到了发出信号是多么容易,那么是时候将一些信息返回到 Pi 中了。我们将通过两个例子来说明这一点。第一,按钮;在这个例子中,Pi 被设置为从一个简单的按钮获取输入,并在按钮被按下时在终端中进行指示。第二个例子使用声波测距仪读取物体的距离。输出将显示在终端中。

按钮示例

最简单的输入形式是按钮。你按下一个按钮,电路闭合,就会发生一些事情。对于我们的第一个输入示例,我们将把一个按钮连接到 GPIO 头。

基本上有两种连接按钮的方式。您可以将其设置为在低状态下启动,这意味着当按钮未被按下时,没有信号到达该引脚,该引脚上的电压被处理器读取为“低”。也可以在高状态下连接。在此配置中,当按钮未按下时,引脚读数为高电平或 on。按下按钮时,引脚变为低电平状态。

你经常会听到拉高或拉低这两个词。拉高或拉低引脚是迫使引脚进入高电平或低电平状态的方法。在许多应用中,这是通过在电路中增加一个电阻来实现的。

连接在逻辑引脚和电压之间的电阻使引脚处于高电平状态。引脚被拉高。然后,按钮接地。按下按钮时,电压通过按钮流到地,绕过引脚。如果引脚上没有电压,它将进入低电平状态。

反之,在逻辑引脚和地之间连接电阻,然后在引脚和电压源之间连接按钮,引脚被下拉。当按钮断开时,引脚中的任何残余电压都被拉至地,使引脚处于低电平状态。当按钮被按下时,电压被施加到引脚,引脚进入高电平状态。

引脚被拉高或拉低,以确保当按钮被按下时,它们处于预期状态。这是一种明确告诉电路预期行为的方式,通常是一种好的做法。

幸运的是,Raspberry Pi 具有内置电路,可以将引脚拉高或拉低。这意味着我们可以通过代码将引脚拉到适当的状态,而不必担心添加额外的组件。

在这个练习中,让我们把引脚拉高。按下按钮时,引脚变为低电平,一条消息打印到终端窗口。

连接电路

本练习需要以下零件:

  1. Attach the T-Cobbler as shown in Figure 4-5. One row of pins should be on either side of the split in the board. The placement is up to you; however, I generally attach it so that the ribbon cable header is off the board. This allows maximum access to the breadboard.

    A457480_1_En_4_Fig5_HTML.jpg

    图 4-5

    Push-button example circuit layout

  2. 连接一个触觉按钮,使引脚桥接试验板中心的间隙。

  3. 在 3.3V 引脚和电压轨之间连接一根跳线。

  4. 在接地引脚和接地轨之间连接另一根跨接导线。

  5. 使用另一根跨接导线将触摸开关的一侧连接到接地轨。

  6. 使用另一根跨接导线将另一个按钮触针连接到触针 17。

  • 触觉按钮开关
  • 4 个男性对男性跳线

这些触摸开关是双刀单掷的(DPST)。这意味着当按下按钮时,试验板间隙一侧的两个引脚连接。间隙另一侧的引脚形成一个独立的电路。确保跳线连接到分割线同一侧的引脚。

编写代码

创建一个新的 Python 3 文件。

如果使用 IDLE,请执行以下操作:

  1. Python 3 的 Open IDLE。
  2. 单击新建。
  3. 将文件另存为项目文件夹中的gpio_button.py

如果使用终端窗口,请执行以下操作:

  1. 导航到您的项目文件夹。在我的 Pi 上是$ cd ~/TRG-RasPi-Robot/code

  2. 类型touch gpio_button.py

  3. 类型idle3 gpio_button.py。这将在 Python 3 的空闲 IDE 中打开空文件。

  4. 输入以下代码:

    # GPIO example using an NC-SR04 ultrasonic rangefinder
    
    # import the GPIO and time libraries
    import RPi.GPIO as GPIO
    
    # Set the GPIO mode to BCM mode and disable warnings
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False) 
    
    # Define pin
    btnPin = 20
    GPIO.setup(btnPin, GPIO.IN, pull_up_down = GPIO.PUD_UP)
    
    # Begin while loop
    while True:
            btnVal = GPIO.input(btnPin)
    
            # If the pin is low, print to terminal
            if (btnVal == false):
                    print('Button pressed')
    
    
  5. 打开一个新的终端窗口,并导航到您的项目文件夹。

  6. 类型chmod +x gpio_button.py

  7. 要运行代码,请键入sudo python3 gpio_button.py

声波测距仪示例

在本例中,我们使用 HC-SR04 超声波传感器来确定到物体的距离。你将把呼叫放入一个循环中,让我们得到恒定的距离读数。您将使用前一个例子中使用的库来访问 GPIO 管脚。

本练习向您介绍了 Pi 和许多其他器件需要注意的一个关键因素:传感器和引脚之间的电压差。许多传感器被设计为在 5 伏下工作。然而,Pi 在其逻辑中使用 3.3 伏电压。这意味着所有 I/O 引脚都设计为在 3.3 伏电压下工作。向这些引脚中的任何一个施加 5V 信号都会对您的 Pi 造成严重损坏。Pi 确实提供了几个 5V 源引脚,但我们需要将返回信号降低到 3.3 伏。

连接电路

这一次,电路有点复杂。真的。请记住,传感器在 5 伏电压下工作。Pi 的 GPIO 引脚在 3.3 伏电压下工作。将 5V 返回信号馈入 3.3V 引脚可能会损坏 Pi。为了防止这种情况发生,我们在 echo 引脚上增加一个简单的分压器。

让我们做一些数学。

我们有 5 伏输入电压,想要 3.3 伏输出电压,我们使用 1kω电阻作为电路的一部分。所以…

)

)

以下是零件清单:

  • HC-SR04 战斗机
  • 1kω电阻
  • 2kω电阻您可以使用两个 1kω电阻串联,或者更受欢迎的类似电阻是 2.2k 电阻。我们就用这个。
  • 4 个男性对女性跳线
  • 4 个男性对男性跳线

这是设置。

  1. Attach the T-Cobbler, as shown in Figure 4-6. One row of pins should be on either side of the split in the board. The placement is up to you; however, I generally attach it so that the ribbon cable header is off the board. This allows maximum access to the breadboard.

    A457480_1_En_4_Fig6_HTML.jpg

    图 4-6

    Sonic rangefinder example circuit layout

  2. 确保接地跳线固定在接地引脚和接地轨之间。

  3. 在一个 5V 引脚和电源轨之间添加一根跳线。

  4. 使用一根阴阳跳线将 SR04 上的接地引脚连接到接地轨。

  5. 将 SR04 的 VCC 或 5V 引脚连接到电源轨。

  6. 将 SR04 上的 trig 引脚连接到 T 形补鞋匠的引脚 20。

  7. 将 2kω电阻从一个空的 5 引脚供电轨连接到接地供电轨。

  8. 将 1kω电阻从连接到 2kω电阻的供电轨连接到另一个空的 5 引脚供电轨。

  9. 在连接到 2kω电阻的供电轨和 T 形补钉上的引脚 21 之间连接另一根跳线。

  10. 将 SR04 echo 引脚连接到 1kω电阻另一端所连接的供电轨。

这就完成了布线。现在让我们设置代码。

编写代码

HC-SR04 超声波测距仪通过测量超声波脉冲返回传感器所需的时间来工作。我们将发出一个 10 微秒的脉冲,然后监听脉冲的返回。通过测量返回脉冲的长度,我们可以使用一点数学来计算厘米距离。

距离的计算方式为速度×时间。它是从公式速度=距离÷时间推导出来的。在海平面上,声音以每秒 343 米或每秒 34300 厘米的速度传播。由于我们实际上是在测量脉冲到达目标并返回所需的时间,所以我们实际上只需要该值的一半。让我们用下面的公式来计算:

距离= 17150×时间

代码只需发出一个 10μS 的脉冲,测量它返回所需的时间,计算以厘米为单位的估计距离,并在终端窗口中显示出来。

创建一个新的 Python 3 文件。

如果使用 IDLE,请执行以下操作:

  1. Python 3 的 Open IDLE。
  2. 单击新建。
  3. 将文件另存为项目文件夹中的gpio_sr04.py

如果使用终端窗口,请执行以下操作:

  1. 导航到您的项目文件夹。在我的 Pi 上是$ cd ~/TRG-RasPi-Robot/code

  2. 类型touch gpio_sr04.py

  3. 类型idle3 gpio_sr04.py。这将在 Python 3 的空闲 IDE 中打开空文件。

  4. 输入以下代码:

    # GPIO example using an NC-SR04 
    
    ultrasonic rangefinder
    
    # import the GPIO and time libraries
    import RPi.GPIO as GPIO
    import time
    
    # Set the GPIO mode to BCM mode and disable warnings
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    # Define pins
    trig = 20
    echo = 21
    
    GPIO.setup(trig,GPIO.OUT)
    GPIO.setup(echo,GPIO.IN)
    
    print("Measuring distance")
    
    # Begin while loop
    while True:
        # Set trigger pin low got 1/10 second
        GPIO.output(trig,False)
        time.sleep(0.1)
    
        # Send a 10uS pulse
        GPIO.output(trig,True)
        time.sleep(0.00001)
        GPIO.output(trig,False)
    
        # Get the start and end times of the return pulse
        while GPIO.input(echo)==0:
            pulse_start = time.time()
    
        while GPIO.input(echo)==1: 
    
            pulse_end = time.time()
    
        pulse_duration = pulse_end - pulse_start
    
        # Calculate the distance in centimeters
        distance = pulse_duration * 17150
        distance = round(distance, 2)
    
        # Display the results. end = '\r' forces the output to the same line
        print("Distance: " + str(distance) + "cm      ", end = '\r')
    
    
  5. 打开一个新的终端窗口,并导航到您的项目文件夹。

  6. 类型chmod +x gpio_sr04.py

  7. 要运行代码,请键入sudo python3 gpio_sr04.py

摘要

Raspberry Pi 的一大优点是 GPIO 头。40 针接头允许您直接与传感器和其他设备连接。除了我们用来连接 LED、按钮和超声波传感器的简单 GPIO 之外,还有具有其他特定功能的引脚。我建议探索一些其他的功能。标有 SCL、SDA、MISO 和 MOSI 的引脚是串行连接,允许您使用高级传感器,如加速度计和 GPS。

使用 GPIO 头时,有几件事需要记住。

  • 要运行您的脚本,首先使用chmod +x <filename>使文件可执行。
  • 无论何时运行使用 GPIO 管脚的脚本,都需要使用sudo
  • 请仔细注意传感器使用的电压。
  • 虽然接头可以为设备提供 5 伏电压,但逻辑引脚是 3.3 伏。如果你不减弱来自传感器的信号,你会损坏你的树莓派。
  • 分压电路——类似于为超声波传感器构建的分压电路——可用于将来自传感器的 5V 信号降至 3.3。
  • 被称为逻辑电平转换器的预制电路板降低了电压。
  • Raspberry Pi 没有实用的模拟引脚。
  • 它只有两个 PWM 通道。每个引脚都连接到两个引脚,因此看起来有四个可用的 PWM 引脚,但实际上没有。

在下一章中,我们将 Arduino 板连接到我们的 Raspberry Pi。Arduino 是为 IO 设计的微控制器。这就是它所做的一切,但它做得很好。通过结合这两种板,我们不仅克服了 Pi 的缺点,还增加了其他好处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值