树莓派超算和科学计算教程-全-

树莓派超算和科学计算教程(全)

原文:Raspberry Pi Supercomputing and Scientific Programming

协议:CC BY-NC-SA 4.0

一、单板计算机和树莓派简介

我们将用 Raspberry Pi 开始我们探索超级计算和科学编程科学领域的激动人心的旅程。但是对于我们开始这个旅程,我们必须熟悉单板计算机和 Raspberry Pi 的基础知识。在这一章中,我们将学习单板计算机的定义、历史和原理。我们首先将它与普通计算机进行比较。然后,我们将继续讨论有史以来最受欢迎和最畅销的单板计算机 Raspberry Pi。到本章结束时,读者将有足够的知识来独立设置自己的树莓派。本章旨在让读者熟悉单板计算机的基本概念和 Raspberry Pi 的设置。

单板计算机

单板计算机(在整本书中,从下文开始将被称为 SBCs)是一个围绕单个印刷电路板构建的全功能计算机系统。SBC 具有微处理器、存储器、输入/输出和最小功能计算机所需的其它特征。与台式个人电脑(PC)不同,大多数 SBC 没有用于外围功能或扩展的扩展槽。因为所有组件,例如处理器、RAM、GPU 等。集成在一个 PCB 上,我们无法升级 SBC。

很少有单板机是为了系统扩展而插入底板的。SBC 有许多种类、大小、形状、外形和功能。由于电子和半导体技术的进步,大多数单板机的价格非常低。SBCs 最重要的特征之一是成本效益。每个价格约为 50 美元,我们手中有一个开发工具,适用于新的应用程序、黑客攻击、调试、测试、硬件开发和自动化系统。

单板机通常以下列形式制造:

  • Pico-ITX
  • 虚拟仪器
  • Qseven
  • 世界杯足球赛
  • 总线技术
  • 总线
  • 先进电信运算架构
  • 总线
  • 嵌入式紧凑型扩展(ECX)
  • 迷你 ITX
  • PC/104
  • 工业计算机制造协会

单板机和普通计算机的区别

下面是 SBCs 和普通电脑的区别表(表 1-1 )。

表 1-1。

Differences Between SBCs and Regular Computers

| 单板计算机 | 普通计算机 | | --- | --- | | 它不是模块化的。 | 它是模块化的。 | | 其组件不能升级或更换。 | 它的组件可以升级或更换。 | | 这是一个片上系统。 | 它不是片上系统。 | | 它的外形尺寸很小。 | 它具有大的形状因子。 | | 它是便携式的。 | 它大多是非便携式或半便携式的。 | | 它消耗的能量更少。 | 它消耗更多的能量。 | | 它比普通电脑便宜。 | 它比单板机贵。 |

片上系统

所有 SBC 主要是 SOC。片上系统或片上系统(SOC 或 SoC)是一种集成电路(IC ),它将计算机的所有组件都集成在一个芯片上。SOC 在移动电子设备中非常常见,因为它们具有低功耗和多功能性。SOC 广泛应用于手机、SBC 和嵌入式硬件中。SoC 拥有其运行所需的所有硬件和软件。

SoC 与常规 CPU

使用 SoC 的最大优势是它的尺寸。如果我们使用中央处理器,就很难制造出一台紧凑的计算机,这仅仅是因为我们需要在一块板上排列大量的单个芯片和其他组件。然而,使用 SOC,我们可以在智能手机和平板电脑中放置完整的特定应用计算系统,并且仍然有足够的空间来放置电池、天线和远程电话和数据通信所需的其他附件。

由于非常高的集成度和紧凑的尺寸,SoC 比常规 CPU 消耗的功率要少得多。对于移动和便携式系统,这是 SOC 的一大优势。此外,通过消除计算机电路板上的冗余 IC 来减少芯片数量,可实现紧凑的电路板尺寸。

SBCs 的历史

Dyna-Micro 是第一款真正的 SBC。它基于英特尔 C8080A,并使用英特尔的第一个 EPROM,C1702A。dyna-micro 于 1976 年由康涅狄格州德比的 E&L Instruments 公司更名为 MMD 1 号(Mini-Micro Designer 1)。它作为微型计算机的主要例子而出名。在计算的早期,单板机非常流行,因为许多家用计算机实际上都是单板机。然而,随着个人电脑的兴起,单板机的受欢迎程度下降了。自 2010 年以来,由于 SBC 的生产成本较低,SBC 再次受到欢迎。

除了 MMD-1,以下是一些受欢迎的历史 SBC:

  • BBC Micro 是围绕运行在 2 MHz 的 MOS 技术 6502A 处理器构建的。
  • Ferguson Big Board II 是一台基于 Zilog Z80 的计算机,运行频率为 4MHz。
  • Nascom 是另一台基于 Zilog Z80 的计算机。

流行的 SBC 系列

基于制造商和设计师,SBC 分为系列、型号和代。一些受欢迎的 SBC 系列有

  • 覆盆子酱基金会的覆盆子酱
  • 香蕉派和香蕉派
  • 英特尔爱迪生和伽利略
  • 方块板
  • 喙骨和喙板

树莓派

Raspberry Pi 是由 Raspberry Pi 基金会在英国开发的一系列信用卡大小的 SBC。树莓派基金会成立于 2009 年。开发 Raspberry Pi 的目的是通过提供一个低成本的计算平台来促进学校和发展中国家的基础计算机科学教学。

树莓派基金会的树莓派于 2012 年发布。这是一次巨大的成功,两年内售出了 200 多万台。随后,树莓派基金会修订了树莓派的版本。他们还发布了 Pi 的其他配件。

你可以在树莓派基金会的网站( www.raspberrypi.org 找到更多关于树莓派基金会的信息。

树莓派目前生产型号和其他配件的产品页面为 www.raspberrypi.org/products

我已经在 Raspberry Pi 模型 B+、2B 和 3B 上编写、执行和测试了本书的所有代码示例。树莓派 3 型号 B(也称为 3B)是树莓派 的最新型号。让我们看看树莓派 3 Model B 的规格(参考表 1-2 )。

表 1-2。

Specifications of Raspberry Pi 3 Model B

| 出厂日期 | 2016 年 2 月 | | 体系结构 | ARMv8 战斗机 | | SoC Broadcom | BCM2837 | | 中央处理器 | 1.2 GHz 64 位四核 ARM Cortex-A53 | | 国家政治保卫局。参见 OGPU | Broadcom video core IV(300 MHz GPU 的 3D 部分,400 MHz GPU 的视频部分) | | 记忆 | 1 GB(与 GPU 共享) | | 通用串行总线 | 2.0 端口 4 | | 视频输出 | HDMI 版本 1.3 和复合视频 RCA 插孔 | | 车载存储 | 微型 SDHC 槽 | | 车载网络 | 10/100 Mbps 以太网、蓝牙和 WiFi | | 电源 | 5V 微型 USB 端口 | | 额定功率 | 800 毫安(4W) |

下面(图 1-1 )是树莓派 3 Model B 的俯视图。

A447085_1_En_1_Fig1_HTML.jpg

图 1-1。

Raspberry Pi 3 Model B top view

下面(图 1-2 )是树莓派 3 Model B 的仰视图。

A447085_1_En_1_Fig2_HTML.jpg

图 1-2。

Raspberry Pi 3 Model B bottom view

我们可以通过访问其产品页面( www.raspberrypi.org/products/raspberry-pi-3-model-b )获得更多关于树莓派 3 Model B 的信息。

下表(表 1-3 )列出了树莓派 2 Model B 的规格。

表 1-3。

Specifications of Raspberry Pi 2 Model B

| 出厂日期 | 2015 年 2 月 | | 体系结构 | ARMv7 战斗机 | | SoC Broadcom | BCM2836 | | 中央处理器 | 900 MHz 32 位四核 ARM Cortex-A7 | | 国家政治保卫局。参见 OGPU | Broadcom 视频会议 IV @ 250 MHz | | 记忆 | 1 GB(与 GPU 共享) | | 通用串行总线 | 2.0 端口 4 | | 视频输出 | HDMI 版本 1.3 和复合视频 RCA 插孔 | | 车载存储 | 微型 SDHC 槽 | | 车载网络 | 10/100 Mbps 以太网、蓝牙和 WiFi | | 电源 | 5V 微型 USB 端口 | | 额定功率 | 800 毫安(4W) |

我们可以通过访问其产品页面( www.raspberrypi.org/products/raspberry-pi-2-model-b/ )获得更多关于树莓派 2 Model B 的信息。

下表(表 1-4 )列出了树莓派 1Model B+的规格。

表 1-4。

Specifications of Raspberry Pi 1 Model B+

| 出厂日期 | 2014 年 7 月 | | 体系结构 | ARMv6 战斗机 | | SoC Broadcom | BCM2835 | | 中央处理器 | 700 MHz 单核 ARM1176JZF-S | | 国家政治保卫局。参见 OGPU | Broadcom 视频会议 IV @ 250 MHz | | 记忆 | 512 MB(与 GPU 共享) | | 通用串行总线 | 2.0 端口 4 | | 视频输出 | HDMI 版本 1.3 和复合视频 RCA 插孔 | | 车载存储 | 微型 SDHC 槽 | | 车载网络 | 10/100 Mbps 以太网、蓝牙和 WiFi | | 电源 | 5V 微型 USB 端口 | | 额定功率 | 800 毫安(4W) |

我们可以通过访问其产品页面( www.raspberrypi.org/products/model-b-plus/ )获得更多关于树莓派 2 Model B 的信息。

Raspberry Pi 设置

我们必须先设置树莓派,然后才能开始使用它进行探索和冒险。下面我们来详细看看如何设置。正如我前面提到的,我使用 Raspberry Pi 3 Model B 进行设置。树莓派 2 型号 B 和树莓派 1 型号 B+的设置过程完全相同。让我们来看看安装所需的硬件材料清单。

Raspberry Pi 设置所需的硬件

安装需要下列硬件。

树莓派

我们需要使用树莓派 3 模型 B 或树莓派 2 模型 B 或树莓派 1 模型 B+进行设置。

计算机

需要一台带互联网连接的 Windows 电脑或笔记本电脑。我们需要使用计算机为 Pi 准备一个带有 Raspbian 操作系统映像的 microSD 卡。

输入输出设备

需要一个标准 USB 键盘和一个 USB 鼠标。

微型 sd 卡

需要一个存储容量至少为 8 GB 的 microSD 卡(例如参见图 1-3 )。我们将使用该卡作为 Pi 的二级存储。建议使用等级 10 的卡,因为等级 10 的数据传输速度非常快。安全起见,我建议至少使用 8 GB 的卡。大多数情况下,选择 16 GB 的卡就足够了。

A447085_1_En_1_Fig3_HTML.jpg

图 1-3。

Class 10 microSD card (image from www.flickr.com/photos/ssoosay/ ) Note

在购买该卡之前,请务必访问链接 http://elinux.org/RPi_SD_cards 以检查该卡与树莓派的兼容性。

电源

所有 Raspberry Pi 型号都需要 5V 微型 USB 电源装置(PSU)。树莓派 3 型 PSU 的推荐电流容量为 2.5 安培。对于所有其他型号 2 安培 PSU 是绰绰有余。

你可以在 https://thepihut.com/products/official-raspberry-pi-universal-power-supply 找到树莓派的官方电源。

SD/microSD 读卡器

我们还需要一个读卡器。许多笔记本电脑都有内置的 SD 读卡器。

如果笔记本电脑或读卡器只能使用 SD 卡,那么我们需要一个额外的 microSD-to-SD 卡适配器。下图(图 1-4 )显示了一个适配器。

A447085_1_En_1_Fig4_HTML.jpg

图 1-4。

Card reader and microSD-to-SD adapter (image from www.flickr.com/photos/sparkfun/ )

班长

我们需要一个 HDMI 显示器或 VGA 显示器。

对于 HDMI 显示器,我们需要一根 HDMI 电缆(参见图 1-5 )。它通常与 HDMI 显示器一起包装。

A447085_1_En_1_Fig5_HTML.jpg

图 1-5。

HDMI male-to-male cable (image from www.flickr.com/photos/sparkfun/ )

对于 VGA 显示器,我们需要一根 VGA 电缆(参见图 1-6 )。这通常也与 VGA 显示器一起包装。

A447085_1_En_1_Fig6_HTML.jpg

图 1-6。

VGA cable (image from www.flickr.com/photos/124242273@N07/ )

如果我们使用 VGA 显示器,我们将需要一个 HDMI-VGA 适配器,因为 Raspberry Pi 只有一个 HDMI 端口用于视频输出(图 1-7 )。

A447085_1_En_1_Fig7_HTML.jpg

图 1-7。

HDMI-to-VGA adapter (image from www.flickr.com/photos/sparkfun/ )

树莓派 MicroSD 卡的手工制作

手动准备用于 Pi 的 microSD 卡是将操作系统安装到单板计算机的 microSD 卡中的最佳方式。许多用户(包括我)更喜欢它,因为它允许在用于启动之前手动修改 microSD 卡的内容(如果需要)。准备 microSD 的另一种方法是使用 NOOBS(新的开箱即用软件),我在本书中没有使用过。

手动准备允许我们在启动前访问/boot/config.txt等配置文件。在启动 Pi 之前,我们可能必须在少数情况下修改配置文件(我们将很快讨论这一点)。默认的 Raspbian 映像有两个分区,bootsystem。请为 Pi 使用至少 16 GB 的 microSD 卡,考虑到将来可能对操作系统进行的任何升级。

下载所需的免费软件

让我们下载所需的软件。

下载加速器增强版

从其下载页面( www.speedbit.com/dap/download/downloading.asp )下载下载加速器加安装程序。这个免费软件用于管理下载。这对于大型下载非常有用,因为我们可以暂停和继续下载。如果您的电脑突然关机或互联网中断,它会从最后一个检查点继续下载。下载并安装后,使用它来管理以下下载。

win32 diski manager

从其下载页面( https://sourceforge.net/projects/win32diskimager/files/latest/download )下载 Win32DiskImager 安装程序。安装。

WinZip 或 WinRaR

我们需要一个文件提取工具。下载 WinZip ( http://www.winzip.com/win/en/index.htm )或者 WinRaR ( http://www.win-rar.com/download.html )。安装其中任何一个。

下载并解压缩 Raspbian 操作系统映像

我们将使用 Raspbian 操作系统作为 Pi。我们将在本章的后半部分详细讨论 Raspbian。现在,从 www.raspberrypi.org/downloads/raspbian 下载 Raspbian 操作系统的最新压缩文件。用 WinZip 或 WinRaR 提取图像 zip 文件。

将 Raspbian 操作系统映像写入 MicroSD 卡

将 microSD 卡插入读卡器。如果您的电脑或笔记本电脑有内置读卡器,请将其插入。如果读卡器或您的电脑只有一个 SD 卡插槽,您可能必须使用 microSD-to-SD 卡适配器。

打开 Win32DiskImager。选择图像文件的位置,然后单击写入按钮。见下图 1-8 。

A447085_1_En_1_Fig8_HTML.jpg

图 1-8。

Win32 Disk Imager

如果您看到以下警告消息(图 1-9 ,则切换读卡器或 SD 卡适配器(或两者)的写保护槽。然后再次点按“写入”按钮。

A447085_1_En_1_Fig9_HTML.jpg

图 1-9。

Write protection error message

将显示以下(图 1-10 )警告信息。单击“是”按钮继续。

A447085_1_En_1_Fig10_HTML.jpg

图 1-10。

Overwrite warning message

一旦操作系统映像被写入 SD 卡,将显示以下消息(图 1-11 )。单击确定按钮。

A447085_1_En_1_Fig11_HTML.jpg

图 1-11。

Write successful message

Raspbian 操作系统已经刷新到 microSD 卡。

更改 VGA 监视器的 config.txt 文件的内容

Note

如果你打算使用 VGA 显示器,这一步是必须的。如果您使用的是 HDMI 显示器,请跳过这一步。

对于 VGA 显示器,我们必须使用 HDMI 转 VGA 电缆。我们还需要更改config.txt的内容,以使 Pi 与 VGA 监视器一起工作。我们将在本章的后半部分了解更多关于config.txt的内容。

将 microSD 卡再次插入读卡器,并在 Windows 资源管理器中浏览。在 Windows 资源管理器中,它将被表示为标记为boot的可移动介质驱动器。

打开文件config.txt,对文件进行如下更改:

  • #disable_overscan=1改为disable_overscan=1
  • #hdmi_force_hotplug=1改为hdmi_force_hotplug=1
  • #hdmi_group=1改为hdmi_group=2
  • #hdmi_mode=1改为hdmi_mode=16
  • #hdmi_drive=2改为hdmi_drive=2
  • #config_hdmi_boost=4改为config_hdmi_boost=4

完成上述更改后,保存文件。

microSD 卡现在已准备好用于 Pi 和 VGA 监视器。

启动 Pi

让我们用准备好的 microSD 卡启动 Pi。其步骤如下:

  1. 如果您使用的是 HDMI 显示器,请使用 HDMI 公对公电缆将显示器直接连接到 Pi 的 HDMI 端口。如果您使用的是 VGA 显示器,请使用 HDMI 转 VGA 适配器将 HDMI 信号转换为 VGA。
  2. 将 microSD 卡插入 Pi 的 microSD 卡插槽。
  3. 连接 USB 鼠标和 USB 键盘。
  4. 此时,请确保电源已关闭。使用我们之前讨论过的微型 USB 电源线将 Pi 连接到电源。
  5. 将显示器连接到电源。
  6. 检查所有连接。打开 Pi 和监视器的电源。

此时,树莓派将启动。

对于所有配备单核处理器的 Raspberry Pi 机型,开机画面如下(图 1-12 )。

A447085_1_En_1_Fig12_HTML.jpg

图 1-12。

Single-core CPU RPi model boot screen

树莓派所有搭载四核处理器的机型,开机画面都会如下图(图 1-13 )。

A447085_1_En_1_Fig13_HTML.jpg

图 1-13。

Quad-core CPU RPi Model boot screen

一旦 Pi 启动,监视器显示如下的桌面(图 1-14 )。

A447085_1_En_1_Fig14_HTML.jpg

图 1-14。

Raspbian Desktop (as of February 2017)

配置 Pi

我们现在需要配置 Pi 以备将来使用。就这么办吧。

在桌面上,有一个任务栏。在任务栏中,我们发现了下面的图标(图 1-15 )。

A447085_1_En_1_Fig15_HTML.jpg

图 1-15。

LXTerminal icon

点击图标,LXTerminal 窗口(图 1-16 )将会打开。

A447085_1_En_1_Fig16_HTML.jpg

图 1-16。

LXTerminal Window

该终端是一个独立于桌面的基于 VTE 的 LXDE 终端模拟器,没有任何不必要的依赖。在提示符下键入sudoraspi-config并按回车键。raspi-config是树莓派 的配置工具。

导航至引导选项(在图 1-17 中突出显示)。

A447085_1_En_1_Fig17_HTML.jpg

图 1-17。

raspi-config with boot options highlighted

将引导选项设置为桌面自动登录,如下图 1-18 所示。

A447085_1_En_1_Fig18_HTML.jpg

图 1-18。

Desktop Autologin highlighted

在国际化选项中,更改时区和 Wi-Fi 国家(参见图 1-19 )。把键盘布局改成美国。

A447085_1_En_1_Fig19_HTML.jpg

图 1-19。

raspi-config internationalization options

完成后,返回主屏幕并选择完成,如下图所示(图 1-20 )。

A447085_1_En_1_Fig20_HTML.jpg

图 1-20。

Finish

它将要求重启(图 1-21 )。选择是。

A447085_1_En_1_Fig21_HTML.jpg

图 1-21。

Reboot Prompt

它将重新启动 Pi。

我们的工作还没有完成。我们需要学习如何将 Pi 连接到互联网,以及如何更新它。

Raspbian

操作系统是使计算机工作的一组基本程序和实用程序。它是用户和计算机之间的接口。Raspbian 是一个基于流行的 Linux 发行版 Debian 的免费操作系统。Raspbian 针对 SBCs 的 Raspberry Pi 系列进行了优化。它甚至被移植到其他类似的单板机,如香蕉亲。

Raspbian 捆绑了超过 35,000 个软件包和预编译软件,便于在 Raspberry Pi 上安装和使用。Raspbian 的第一个版本于 2012 年 6 月完成。Raspbian 仍在积极开发中,并经常更新。访问 Raspbian 主页( www.raspbian.org )和 Raspbian 文档页面( www.raspbian.org/RaspbianDocumentation )了解更多关于 Raspbian 的信息。

配置文件

Raspberry Pi 没有常规的 BIOS。BIOS(基本输入/输出系统)是计算机的微处理器用来启动计算机系统并在计算机通电后将操作系统加载到内存中的程序。它还管理计算机操作系统和连接的外围设备(如硬盘、视频适配器、键盘、鼠标和打印机)之间的数据流。

由于 Raspberry Pi 没有 BIOS,通常使用 BIOS 存储和修改的各种系统配置参数现在存储在名为config.txt的文本文件中。

树莓派 config.txt文件是树莓派 的boot分区上的文件。在 Linux 中,它通常可以作为/boot/config.txt访问。但是,在 Windows 和 Mac OS 中,它被视为 microSD 卡可访问部分中的文件。卡的可访问部分标记为boot。正如我们在本章前面已经了解到的,如果我们想将/boot/config.txt文件连接到 VGA 显示器,我们必须编辑它。

在 Raspberry Pi 上,我们可以在 LXTerminal 中使用以下命令编辑该文件:

sudo nano /boot/config.txt

Note

nano 是一个简单易学的基于文本的 Linux 文本编辑器。访问其主页( www.nano-editor.org )了解更多信息。我发现它比vivim编辑器更容易使用。

要了解更多关于config.txt的信息,请访问 http://elinux.org/RPiconfig 页面。此外,可以在 http://elinux.org/R-Pi_configuration_file 找到一个示例配置。

将 Raspberry Pi 连接到网络和互联网

要将 Pi 连接到任何网络,我们必须编辑/etc/network/interfaces文件。如果 Pi 连接的网络连接到 Internet,则 Pi 可以访问 Internet。

无线局域网(wireless fidelity 的缩写)

树莓派 3 型号 B 内置 WiFi。对于所有其他型号的 Pi,我们需要使用 USB WiFi 适配器。

将 USB WiFi 适配器连接到 Pi 后,使用以下命令备份/ etc/network/interfaces文件:

sudo mv /etc/network/interfaces /etc/network/interfaces.bkp

原始的/etc/network/interfaces文件以这种方式是安全的,并且如果出现问题可以恢复。

现在创建一个新的/etc/network/interfaces文件。

sudo nano /etc/network/interfaces

在其中键入以下行(列表 1-1 ):

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ssid "ASHWIN"
wpa-psk "internet"

Listing 1-1./etc/network/interfaces

在上面的文件中(列表 1-1 )用你的 WiFi 网络的 ssid 替换"ASHWIN",用你的 WiFi 网络的密码替换"internet"。通过按 CTRL+X 和 y 保存文件。

运行以下命令重新启动网络服务:

sudo service networking restart

如果您正确执行了这些步骤,Pi 应该连接到 WiFi 网络和互联网(前提是 WiFi 网络已连接到互联网)。

要验证与互联网的连接,请使用以下命令:

ping -c4 www.google.com

它应该显示类似下面的输出。

PING www.google.com (216.58.197.68) 56(84) bytes of data.
64 bytes from maa03s21-in-f4.1e100.net (216.58.197.68): icmp_seq=1 ttl=55 time=755 ms
64 bytes from maa03s21-in-f4.1e100.net (216.58.197.68): icmp_seq=2 ttl=55 time=394 ms
64 bytes from maa03s21-in-f4.1e100.net (216.58.197.68): icmp_seq=3 ttl=55 time=391 ms
64 bytes from maa03s21-in-f4.1e100.net (216.58.197.68): icmp_seq=4 ttl=55 time=401 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 391.729/485.695/755.701/155.925 ms

像上面这样的输出意味着 Pi 连接到了互联网。

要查找 Pi 的 IP 地址,请使用 ifconfig 命令。在其输出中,检查wlan0部分。具体如下:

wlan0     Link encap:Ethernet  HWaddr 7c:dd:90:00:e2:1e
          inet addr:192.168.0.122  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::7edd:90ff:fe00:e21e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1974 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1275 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:195049 (190.4 KiB)  TX bytes:1204336 (1.1 MiB)

在上面的输出中,192.168.0.122是 Pi 的 IP 地址。由于 IP 地址是使用 DHCP 协议分配的,因此根据 WiFi 网络设置的不同,IP 地址也会有所不同。

以太网

我们还可以将 Pi 连接到局域网。根据 LAN 交换机的设置,我们可以静态或动态地为 Pi 分配 IP 地址。

静态 IP 地址

如果局域网静态分配 IP 地址,则按如下方式配置/etc/network/interfaces(列表 1-2 ):

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet static
# Your static IP
address 192.168.0.2
# Your gateway IP
gateway 192.168.0.1
netmask 255.255.255.0
# Your network address family
network 192.168.0.0
broadcast 192.168.0.255

Listing 1-2./etc/network/interfaces

在上面的文件中,参数addressgatewaynetmasknetworkbroadcast基于局域网的配置。请查看局域网交换机或路由器的手册。如果您在组织中工作,请向网络管理员咨询这些参数。

动态 IP 地址

这是一个简单的问题。如果局域网具有 DHCP 功能,则按如下方式配置/etc/network/interfaces(列表 1-3 ):

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

Listing 1-3./etc/network/interfaces

这将配置 Pi 通过 DHCP 自动获取 IP 地址。

Note

在 Debian 及其衍生产品上设置网络所需的所有信息都可以在 https://wiki.debian.org/NetworkConfiguration 上找到。

更新 Pi

为此,Pi 必须连接到互联网。

更新固件

固件是嵌入电子设备 ROM 芯片的软件。它提供对设备的控制和监控。要更新 Pi 的固件,运行sudo rpi-update。它将更新固件。

更新和升级 Raspbian

我们将为此使用 APT。APT 代表高级打包工具。这是一个处理 Debian 和其他 Debian 衍生软件的安装和删除的程序。APT 通过自动获取、配置和安装软件包,简化了在 Debian 系统上管理软件的过程。我们也需要一个互联网连接。

首先,通过在 LXTerminal 中输入以下命令来更新系统的程序包列表:

sudo apt-get update

apt-get update从各自的远程存储库下载软件包列表,并在本地计算机上更新它们,以获取可用于安装和更新的最新版本软件包及其依赖项的信息。它应该在运行installupgrade命令之前运行。

接下来,使用以下命令将所有已安装的软件包升级到最新版本:

sudo apt-get dist-upgrade -y

apt-get dist-upgrade获取机器上标记为要在本地机器上升级的软件包的新版本。它还检测和安装依赖关系。它可能会删除过时的软件包。

定期这样做将使 Pi 上安装的 RaspbianOS 保持最新。输入这些命令后,需要一段时间来更新操作系统,因为这些命令会从远程存储库中获取数据和包。

Note

sudo apt-get --help将列出与apt-get相关的所有选项

更新 raspi-config

raspi-config中,转到高级选项(参见图 1-22 )并选择更新。

A447085_1_En_1_Fig22_HTML.jpg

图 1-22。

Updating raspi-config

关闭和重新启动 Pi

我们可以用sudo shutdown -h now安全关闭 Pi。

我们可以用sudo reboot -h now重启 Pi。

结论

在本章中,我们介绍了 SBCs 的概念和原理。我们也开始使用 SBC 的一个流行系列,Raspberry Pi。现在我们可以满怀信心地进行进一步的探索。在下一章,我们将学习一些重要的 Linux 命令以及如何远程连接到 Pi。

二、重要的 Linux 命令和远程连接

在上一章中,我们学习了单板计算机的基础知识以及如何设置 Pi。我们还学会了将 Pi 连接到互联网。我希望所有读者现在对这些基础知识都很熟悉了。完成这些后,我们将会更深入地挖掘一些基础知识。在这一章中,我们将学习一些对我们有用的重要的 Linux 命令。我们还将研究如何远程连接到 Pi。

重要而有用的 Linux 命令

在这一节中,我们将研究几个重要的 Linux 命令,这些命令将有助于我们理解将用于超级计算和并行编程的硬件环境(Pi)。

获得 Linux 命令的帮助

我们可以使用 man 命令或- help 选项来获取有关命令的更多信息。例如,如果我们想更多地了解 cat 命令的用法,那么我们可以发出命令 man cat 或 cat - help。

网络相关命令

以下与网络相关的命令有助于理解网络基础设施。

命令

ifconfig 用于检查网络状态。我们可以使用 ifconfig eth0 或 ifconfig wlan0 分别检查 WiFi 或以太网的状态。

尤克里里琴

我们可以使用 iwconfig 来检查无线网络状态。其输出如下:

wlan0 IEEE 802.11bg ESSID:"ASHWIN"
Mode:Managed Frequency:2.412 GHz Access Point: A2:EC:80:FB:E2:66
Bit Rate=6 Mb/s Tx-Power=20 dBm
Retry short limit:7 RTS thr:off Fragment thr:off
Power Management:off
Link Quality=45/70 Signal level=-65 dBm
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:24 Invalid misc:6 Missed beacon:0

lo no wireless extensions.

eth0 no wireless extensions.

iwlist wlan0 扫描

iwlist wlan0 扫描显示所有可用无线网络的列表。

ping 测试两台设备之间的网络连通性。我们已经在第一章中看到了它检查互联网连接的用法。

系统信息命令

这些命令帮助我们更多地了解 Pi 上系统和硬件的状态。

CPU 相关信息

我们可以使用 cat /proc/cpuinfo 来查看关于 CPU 的信息。

要检查其他细节(即 CPU 的运行速度),可以使用 lscpu 命令。lscpu 的输出如下:

Architecture: armv7l
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
Model name: ARMv7 Processor rev 5 (v7l)
CPU max MHz: 900.0000
CPU min MHz: 600.0000

记忆相关信息

我们可以使用 cat /proc/meminfo 来获得关于内存的细节。我们还可以使用 free 命令来查看有多少可用内存,如下所示:

        total   used     free     shared   buffers  cached
Mem:    996476  210612   785864   7208     15152    113668
-/+ buffers/
cache:  81792   914684
Swap:   102396  0        102396

系统和操作系统版本信息

uname -a 提供关于当前系统的信息如下:

Linux raspberrypi 4.4.11-v7+ #888 SMP Mon May 23 20:10:33 BST 2016 armv7l GNU/Linux

要识别 Linux 版本,请运行命令 cat /proc/version。

分区相关信息

df -h 以可读格式显示 microSD 卡分区相关信息,如下所示:

Filesystem              Size    Used    Avail   Use%    Mounted on
/dev/root               15G     3.6G    11G     26%     /
devtmpfs                483M    0       483M    0%      /dev
tmpfs                   487M    0       487M    0%      /dev/shm
tmpfs                   487M    6.6M    480M    2%      /run
tmpfs                   5.0M    4.0K    5.0M    1%      /run/lock
tmpfs                   487M    0       487M    0%      /sys/fs/cgroup
/dev/mmcblk0p1          63M     21M     43M     33%     /boot
tmpfs                   98M     0       98M     0%      /run/user/1000

cat /proc/partitions 提供了分区块分配信息。

其他有用的命令

hostname -I 显示了 IP 地址。

lsusb 显示连接到 Pi 的所有 usb 设备的列表。

vcgencmd measure_temp 显示 CPU 的温度。

vcgencmd get _ mem arm & & vcgencmd get _ mem gpu 显示 CPU 和 GPU 之间的内存分割。

从 raspi-config 为 SSH 启用 Pi

要远程连接到 Pi,我们需要从 raspi- config 启用 SSH 服务器。打开 LXTerminal 并运行命令 sudo raspi-config。

在 raspi-config 的主菜单中选择高级选项。在高级选项屏幕中,选择 A4 SSH,将出现以下屏幕(图 2-1 )。

A447085_1_En_2_Fig1_HTML.jpg

图 2-1。

Enabling the SSH server

选择是,将显示以下信息(图 2-2 )。

A447085_1_En_2_Fig2_HTML.jpg

图 2-2。

SSH server enable confirmation

按回车键。从 raspi-config 的主菜单中选择 Finish,并在提示重新启动时选择 Yes。一旦 Pi 重启,我们就可以远程访问了。

从 Windows 远程连接到 Raspberry Pi

如果我们用来连接 Raspberry Pi 的计算机也在同一个网络中(无论是物理上还是通过 VPN),远程连接到 Pi 是可能的。为此,我们必须使用各种工具和实用程序。在本章中,我们将学习远程连接到 Pi。我们还将学习如何在 Pi 之间来回传输文件。当我们想在无头模式下使用 Pi 时,这一点很重要(“无头”只是指我们使用的 Pi 没有附加可视显示)。在我们没有显示器或者由于资源/空间限制而不想备用显示器的情况下,这很有用。例如,当我们创建一个 Pi 集群时,我们不能为集群中的每个 Pi 留出一个显示器。在这种情况下,无头模式非常有用。在本章的这一部分,我们将探索远程使用 Pi 的多种方法。

从另一台计算机检查与 Pi 的连接

我们计划连接到 Pi 的计算机必须在同一个网络中,这一点很重要。在其他计算机和 Pi 之间不应该有任何代理和防火墙限制。这种设置的最佳示例是 Pi 和另一台计算机连接在同一个路由器/网络交换机下。要检查连通性,我们可以使用 ping 实用程序。

  1. 在计算机上打开 Windows 命令行 cmd。
  2. 打开 Pi 并记下其 IP 地址。使用 ifconfig 命令查找其 IP 地址。假设是 192.168.0.2(不管是以太网还是 WiFi)。
  3. 在 cmd 中,运行 ping 192.168.0.2 来检查连通性。

我们可以在任何装有 Linux 发行版或 macOS 的计算机上使用相同的 ping 命令,只要它们与 Pi 在同一个网络中。

油灰

PuTTY 是用于 Windows 和 Unix 平台的 SSH 和 Telnet 的一个免费实现,附带一个 xterm 终端仿真器。它主要由 Simon Tatham 编写和维护。你可以探索 PuTTY 主页( www.chiark.greenend.org.uk/~sgtatham/putty/ )了解更多信息。

PuTTY 是开源软件,有源代码,由一个志愿者团队开发和支持。

现在让我们下载并安装 PuTTY。PuTTY 可以从其下载页面( www.chiark.greenend.org.uk/~sgtatham/putty/latest.html )下载。下载文件 PuTTY.exe。下载完成后,将其放在您选择的目录中,并为您创建一个桌面快捷方式(图 2-3 )。

A447085_1_En_2_Fig3_HTML.jpg

图 2-3。

PuTTY desktop shortcut

双击 putty 快捷方式,将会打开 PuTTY(图 2-4 )窗口。

A447085_1_En_2_Fig4_HTML.jpg

图 2-4。

PuTTY Window

在主机名(或 IP 地址)文本框中键入 IP 地址或主机名。确保选择 SSH 作为连接类型。为了将来使用,你可能想要保存设置(我通常这样做)。现在,单击打开按钮。它会打开一个终端风格的窗口。它会要求你输入用户名和密码,分别是 pi 和 raspberry。首次登录时,显示如下(图 2-5 )消息对话框。单击是。

A447085_1_En_2_Fig5_HTML.jpg

图 2-5。

PuTTY security alert

一旦我们登录,它会显示如下提示(图 2-6 )。

A447085_1_En_2_Fig6_HTML.jpg

图 2-6。

PuTTY remote connection window

我们现在可以远程使用 Pi 的命令提示符了。

远程访问 Raspberry Pi 桌面

树莓派 桌面是 LXDE(轻量级 X11 桌面环境)。我们可以使用 RDP(远程桌面协议)客户端从 Windows 计算机远程访问它。为此,我们需要在 Pi 上安装 xrdp。通过运行 sudo apt-get install xrdp 来安装它。安装后重新启动 Pi。

我们现在必须使用 Windows 远程桌面客户端来连接到 Pi。可以使用 Windows 中的搜索选项找到客户端。单击 Remote Desktop 客户端图标将其打开。

在下面的对话框中(图 2-7 ,点击选项将其展开。

A447085_1_En_2_Fig7_HTML.jpg

图 2-7。

Remote Desktop Connection

它展开并显示各种选项(图 2-8 )。输入 pi 的 IP 地址和 Pi 作为用户名。单击复选框和保存按钮,保存相同的配置供将来使用。

A447085_1_En_2_Fig8_HTML.jpg

图 2-8。

Remote Desktop Connection options

登录时会提示输入密码。输入密码。如果我们第一次使用 Windows 计算机连接到 Pi,那么它将显示以下对话框(图 2-9 )。选中复选框,这样它就不会再询问我们,然后单击是。

A447085_1_En_2_Fig9_HTML.jpg

图 2-9。

First time remote login

它将显示树莓派桌面。由于 Pi 通过网络传输桌面,操作速度会稍慢。然而,它工作得很好。

这就是我们如何使用 Windows 电脑/笔记本电脑访问 Pi 的桌面。然而,我们所学的方法都不允许我们在 Windows 计算机和 Pi 之间传输文件。我们将在本章的下一节学习如何去做。

温斯 CP

对于 Windows 计算机和 Raspberry Pi 之间的文件传输,我们将使用 WinSCP ( https://winscp.net/eng/index.php )。WinSCP 是一个免费的 Windows 开源 SFTP 和 FTP 客户端。它的主要功能是本地和远程计算机之间的安全文件传输。

下载其设置( https://winscp.net/eng/download.php )并安装。在 Windows 桌面上创建它的快捷方式。双击图标打开 WinSCP(图 2-10 )。

A447085_1_En_2_Fig10_HTML.jpg

图 2-10。

WinSCP window

在主机名文本框中输入 Pi 的 IP 地址。另外,输入 pi 作为用户名,输入 raspberry 作为密码。

我们还可以保存设置以备将来使用。保存会话(图 2-11 )对话框如下。

A447085_1_En_2_Fig11_HTML.jpg

图 2-11。

Save session

登录后,如果我们是第一次连接,则会显示以下对话框(图 2-12 )。单击“添加”按钮继续。

A447085_1_En_2_Fig12_HTML.jpg

图 2-12。

First time login dialog box

登录后,将显示以下窗口(图 2-13 )。

A447085_1_En_2_Fig13_HTML.jpg

图 2-13。

WinSCP file transfer window

本地 Windows 计算机的文件系统在左侧面板中,而 Raspberry Pi 的 Pi 用户的主目录即/home/pi 在右侧面板中。我们现在可以在两台计算机之间传输文件了。

使用 Linux 或 macOS 连接到 Raspberry Pi

让我们学习使用 Linux 计算机或 macOS 连接到 Pi。

使用 SSH 远程登录

SSH 内置于 Linux 发行版和 macOS 中。我们可以使用 SSH 从 Linux 计算机(也可能是另一个 Raspberry Pi)或 Mac 终端连接到 Pi,而无需安装额外的软件。

在 Linux 计算机或 Mac 中打开终端,并键入以下命令:

ssh pi@192.168.0.2

192.168.0.2 是我 Pi 的 IP 地址。用您的 Pi 的 IP 地址替换它。一旦我们按下回车键,它将显示一个安全/真实性警告提示。键入 yes 继续。此警告仅在我们第一次连接时显示。

现在它会提示输入密码。输入 pi 用户的默认密码 raspberry。我们将看到 Raspberry Pi 提示符,它与 Raspberry Pi 上的提示符完全相同。

使用 SSH 转发

我们还可以通过 ssh 转发 X11 会话,以允许使用图形应用程序,方法是在 SSH 命令中使用-Y 标志,如下所示:

ssh -Y pi@192.168.0.2

让我们远程访问像 scratch 这样的图形程序。运行以下命令,

scratch &

它将在 Linux 计算机或 Mac 上的新窗口中启动 Pi 程序scratch的新 X11 会话。&使命令在后台运行。

用于文件传输的 SCP

在 Windows 中,我们使用 WinSCP 在 Windows 计算机和 Pi 之间进行文件传输。同样,我们可以在 Linux 计算机/Mac 和 Pi 之间传输文件。为此,我们需要使用 scp 实用程序。它内置于所有的 Linux 发行版和 macOS 中。

要将文件从 Pi 复制到 Linux 计算机或 Mac 上,我们必须在 Linux 计算机或 Mac 上的终端中运行以下命令:

scp pi@192.168.0.2:/home/pi/test.txt /home/ashwin

上面的命令将 test.txt 从 pi 的/home/pi 目录复制到我们的 Linux 电脑或 Mac 的/home/ashwin 目录。

同样,我们可能希望将文件从 Linux 计算机或 Mac 计算机复制到 Pi。为此,在 Linux 计算机或 Mac 的终端中运行以下命令:

scp /home/ashwin/test_again.txt pi@192.168.0.2:/home/pi

你可以在 www.computerhope.com/unix/scp.htm 上阅读更多关于 scp 命令的详细内容。

EXERCISE

完成下面的练习,更好地理解本章。

  • 练习 man 命令。
  • 练习各种 Linux 命令的- help 选项。
  • 运行 iwlist wlan0 scan 命令。
  • 运行 ping www.AshwinPajankar.com 命令来检查与互联网的连接。
  • 尝试查看本章中演示的所有系统相关命令的输出。
  • 尝试用 VNC 远程访问树莓派桌面。

结论

在本章中,我们探讨了远程连接到 Pi 的各种方法。我们还学习了如何在 Pi 和其他使用 Linux、Windows 和 Mac 作为操作系统的计算机之间传输文件。

三、Python 简介

在上一章中,我们学习了重要的 Linux 命令,以及如何从其他计算机远程连接到 Raspberry Pi。我们还学习了如何在 Raspberry Pi 之间传输文件。

在这一章中,我们将开始学习 Python。

让我们从 Python 的介绍开始这一章。我个人觉得 Python 很神奇,已经被它迷住了。Python 是一种简单而强大的编程语言。当我们使用 Python 时,很容易关注给定问题的解决方案的实现,因为程序员不必担心编程语言的语法。Python 完美契合了 Raspberry Pi 的理念,即“为所有人编程”。这就是为什么 Python 是 Raspberry Pi 和许多其他 SBC 的首选编程平台。

Python 的历史

Python 是在 20 世纪 80 年代末设计和构思的。1989 年底,荷兰国家数学和计算机科学研究所的吉多·范·罗苏姆开始了它的实际实施。Python 是 ABC 编程语言的继承者。ABC 编程语言本身受到了 SETL 的启发。1991 年 2 月,Van Rossum 向 alt.sources 新闻组公开发布了 Python 源代码。Python 这个名字的灵感来自电视节目《巨蟒剧团的飞行马戏团》。范·罗森是巨蟒剧团的忠实粉丝。

Van Rossum 是 Python 编程语言的主要作者。他在指导 Python 编程语言的开发、错误修复、增强和进一步发展方面起着核心作用。他拥有 Python 终身仁慈独裁者的称号。他目前(截至 2017 年 2 月)为 Dropbox 工作,并将几乎一半的时间用于 Python 编程语言的进一步开发。

Python 编程语言的核心哲学,Python 的禅,在 PEP-20 (PEP 代表 Python 增强提案)中有解释,可以在 www.python.org/dev/peps/pep-0020 找到。

它是 20 个软件原则的集合,其中 19 个已经被文档化。这些原则如下:

  1. 漂亮总比难看好。
  2. 显性比隐性好。
  3. 简单比复杂好。
  4. 复杂总比复杂好。
  5. 扁平的比嵌套的好。
  6. 疏比密好。
  7. 可读性很重要。
  8. 特例不足以特殊到打破规则。
  9. 虽然实用性战胜了纯粹性。
  10. 错误永远不会无声无息地过去。
  11. 除非明确沉默。
  12. 面对暧昧,拒绝猜测的诱惑。
  13. 应该有一种——最好只有一种——显而易见的方法来做这件事。
  14. 尽管这种方式一开始可能并不明显,除非你是荷兰人。
  15. 现在总比没有好。
  16. 虽然从来没有比现在更好。
  17. 如果实现很难解释,这是一个坏主意。
  18. 如果实现很容易解释,这可能是一个好主意。
  19. 名称空间是一个非常棒的想法——让我们多做一些吧!

Python 的特性

下面是 Python 的一些特性,正是这些特性让它在编程社区中变得流行和受欢迎。

简单的

Python 是一种简单的极简主义语言。阅读一个好的、写得好的 Python 程序会让我们觉得好像在阅读英文文本。

简单易学

由于其简单的、类似英语的语法,Python 对于初学者来说非常容易学习。这就是现在它作为第一编程语言被教授给学习编程入门和编程 101 课程的高中生和大学生的主要原因。新一代的程序员正在学习 Python 作为他们的第一门编程语言。

易于阅读

与其他高级编程语言不同,Python 没有提供太多混淆代码和使其不可读的措施。与用其他编程语言编写的代码相比,Python 代码类似英语的结构更容易阅读。这使得它比其他高级语言如 C 和 C++更容易理解和学习。

易于维护

由于 Python 代码易于阅读、理解和学习,任何维护代码的人都可以在相当短的时间内熟悉代码库。我可以从我维护和增强大型遗留代码库的个人经验中证明这一点,这些代码库是用 bash 和 Python 2 的组合编写的。

开放源码

Python 是一个开源项目。它的源代码是免费的。我们可以根据需要对它进行修改,并在应用程序中使用原始代码和修改后的代码。

高级语言

在编写 Python 程序时,我们不必管理低级别的细节,如内存管理、CPU 计时和调度过程。所有这些任务都由 Python 解释器管理。我们可以直接用易于理解的类似英语的语法编写代码。

轻便的

Python 解释器已经被移植到许多操作系统平台上。Python 代码也是可移植的。如果我们足够小心地避免任何依赖于系统的编码,所有的 Python 程序都可以在任何支持的平台上工作,而不需要做很多改变。

我们可以在 GNU/Linux、Windows、Android、FreeBSD、Mac OS、iOS、Solaris、OS/2、Amiga、Palm OS、QNX、VMS、AROS、AS/400、BeOS、OS/390、z/OS、psion、Acorn、PlayStation、sharp suzanrus、RISC OS、VxWorks、Windows CE 和 PocketPC 上使用 Python。

解释

Python 是一种解释型语言。让我们理解这是什么意思。用 C、C++和 Java 等高级编程语言编写的程序首先被编译。这意味着它们首先被转换成中间格式。当我们运行程序时,这个中间格式由链接器/加载器从辅助存储器(即硬盘)加载到内存(ram)中。所以 C,C++和 Java 有独立的编译器和链接器/加载器。Python 就不是这样了。Python 直接从源代码运行程序。我们不必费心编译和链接到适当的库。这使得 Python 程序具有真正的可移植性,因为我们可以将程序从一台计算机复制到另一台计算机,只要在目标计算机上安装了必要的库,程序就可以正常运行。

面向对象

Python 支持面向过程的编程以及面向对象的编程范例。

Python 支持面向对象的编程范例。所有面向对象的编程范例都是用 Python 实现的。在面向对象的编程语言中,程序是围绕结合数据和相关功能的对象构建的。Python 是一种非常简单但功能强大的面向对象编程语言。

可扩张的

Python 的一个特点就是我们可以从 Python 程序中调用 C 和 C++例程。如果我们希望应用程序的核心功能运行得更快,那么我们可以用 C/C++编写这部分代码,并在 Python 程序中调用它(C/C++程序通常比 Python 运行得更快)。

丰富的图书馆

Python 有一个广泛的标准库,它预装在 Python 中。标准库拥有现代编程语言的所有基本特性。它提供了数据库、单元测试(我们将在本书中探讨)、正则表达式、多线程、网络编程、计算机图形、图像处理、GUI 和其他实用程序。这是 Python“包含电池”哲学的一部分。

除了标准库之外,Python 还有大量且不断增长的第三方库。这些库的列表可以在 Python 包索引中找到。

粗野的

Python 通过处理错误的能力来提供健壮性。遇到的错误的完整栈跟踪是可用的,并使程序员的生活更可忍受。运行时错误被称为异常。允许处理这些错误的特性被称为异常处理机制。

快速原型

Python 被用作快速原型制作工具。正如我们前面看到的,Python 的特性是它有丰富的库,并且容易学习,所以许多软件架构师越来越多地使用它作为工具,在很短的时间内将他们的想法快速原型化为工作模型。

内存管理

在汇编语言和像 C 和 C++这样的编程语言中,内存管理是程序员的责任。这是手头任务之外的工作,给程序员带来了不必要的负担。在 Python 中,Python 解释器负责内存管理。这有助于程序员避开内存问题,专注于手头的任务。

强大的

Python 拥有现代编程语言所需的一切。它用于计算机视觉、超级计算、药物发现、科学计算、模拟和生物信息学等应用。全世界数百万程序员使用 Python。很多大品牌像 NASA,Google,SpaceX,思科(我在那里工作过!)将 Python 用于他们的应用程序和基础设施。

社区支持

我个人认为这是 Python 最吸引人的特性。正如我们所看到的,由于 Python 是开源的,并且在全世界拥有近百万程序员的社区(可能更多,因为今天的高中生也在学习 Python),互联网上有大量的论坛来支持遇到任何障碍的程序员。我提出的与 Python 相关的问题没有一个是没有答案的。

python3

Python 3 发布于 2008 年。Python 开发团队决定去掉 Python 语言的一些冗余特性,简化一些其他特性,纠正一些设计缺陷,并添加一些更急需的特性。

我们决定为此需要一个主要的修订版本号,并且最终发布的版本不会向后兼容。Python 2.x 和 3.x 应该并行共存,以便程序员社区有足够的时间将他们的代码和第三方库从 2.x 迁移到 3.x。在许多情况下,Python 2.x 代码不能按原样运行,因为 2.x 和 3.x 之间存在显著差异。

Python 2 和 Python 3 的区别

以下是 Python 2 和 Python 3 之间几个最值得注意的差异,值得了解。我们将使用 Python 3 中与这些差异相关的许多特性。让我们简单地看一下它们:

  • print()函数这是 Python 2 和 Python 3 之间最明显的区别。Python 2 的 print 语句在 Python 3 中被 print()函数所取代。
  • 整数除法产生浮点值为了数学正确性,整数除法的性质在 Python 3 中已经改变。在 Python 2 中,两个整数相除的结果是一个整数。然而,在 Python 3 中,它是一个浮点值,在数学上是正确的,对初学者来说更有意义。在大多数编程语言中,整数除法是一个整数。
  • 移除 xrange()在 Python 2 中,为了创建可迭代对象,使用了 xrange()函数。在 Python 3 中,range()的实现类似于 xrange()。因此在 Python 3 中不再需要单独的 xrange()。在 Python 3 中使用 xrange()会引发 nameError 异常。
  • 抛出异常在 Python 3 中,必须将异常参数(如果有的话)括在括号中,而在 Python 2 中,这是可选的。
  • 处理异常在 Python 3 中,处理异常时,参数前的 as 关键字处理参数是必须的。在 Python 2 中,不需要。
  • 新样式类 Python 2 支持旧样式类和新样式类,而 Python 3 只支持新样式类。Python 3 根本不支持旧的样式类。默认情况下,Python 3 中创建的所有类都是新的样式类。
  • Python 3 的新特性 Python 3 的以下新特性尚未移植到 Python 2。
    • 默认情况下,字符串是 Unicode 的
    • 清除 Unicode/字节分隔
    • 异常链接
    • 函数注释
    • 仅关键字参数的语法
    • 扩展元组解包
    • 非局部变量声明

根据上面的列表,我们将在本书的代码示例中广泛使用 print()方法、新型类、异常和异常处理机制。

Note

参见 Python Wiki 页面了解 Python 2 和 Python 3 的区别: https://wiki.python.org/moin/Python2orPython3

为什么要用 Python 3?

根据上面的列表,我们将在本书的代码示例中频繁使用新的样式类和异常。

虽然许多 Python 专家仍在鼓吹 Python 2,但我完全不同意他们的观点。

Python Wiki ( https://wiki.python.org/moin/Python2orPython3 )上说:

Python 2.x is the heritage, and Python 3.x is the present and future of language.

支持 Python 2 的一个主要理由是大量的文档、书籍和第三方库。然而,大多数开发人员已经在将他们的定制库移植到 Python 3 上了。几乎所有主要的第三方库都被移植并完全支持 Python 3。就书籍和文档而言,像我这样的作者正在大量编写 Python 3。久而久之,肯定会有更多 Python 3 的文档可用。

新一代程序员正在学习 Python 3 作为他们的第一门编程语言。当他们熟悉 Python 编程的概念和哲学时,他们会逐渐了解 Python 2。

大多数组织已经开始将代码库从 Python 2 迁移到 Python 3。Python 中几乎所有的新项目都在大量使用 Python 3。

我个人认为这些是使用 Python 3 的很好的理由。

Raspbian 上的 Python 2 和 Python 3

Raspbian 是 Debian 的变体。Python 2 和 Python 3 解释器预装在 Raspbian 中。可以通过在 lxterminal 中运行命令 Python 来调用 Python 2 解释器。可以通过在 lxterminal 中运行 python3 命令来调用 Python 3 解释器。我们可以通过运行 python3 -V 或 python - version 来检查 Python 3 解释器的版本。我们可以通过在 lxterminal 运行 which python3 来检查 Python 3 二进制文件的位置。

运行 Python 程序和 Python 模式

我们现在已经为 Python 编程建立了环境。让我们从 Python 的一个简单概念开始。Python 有两种模式,普通模式和交互模式。让我们详细看看这些模式。

对话方式

Python 的交互模式是命令行 shell。它为每个执行的语句提供即时输出。它还将以前执行的语句的输出存储在活动内存中。当 Python 解释器执行新语句时,在评估当前输出时,会考虑之前执行的整个语句序列。我们必须在 lxterminal 中键入 python3 来调用 Python 3 解释器进入交互模式,如下所示:

pi@raspberrypi:∼ $
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

我们可以在这种交互模式下直接执行 Python 语句,就像从操作系统外壳/控制台运行命令一样,如下所示:

>>>print ('Hello World!')
Hello World!
>>>

我们不会在本书中使用交互模式。然而,这是检查小代码片段(5 到 10 行)最快的方法。我们可以使用 exit()语句退出交互模式,如下所示:

>>> exit()
pi@raspberrypi:∼ $

自然振荡

正常模式是 Python 脚本文件(。py)由 Python 解释器执行。

创建一个文件名为 test.py 的文件,并添加语句 print ('Hello World!')到文件。保存文件并使用 Python 3 解释器运行它,如下所示:

pi@raspberrypi:∼ $ python3 test.py
HelloWorld!
pi@raspberrypi:∼ $

在上面的例子中,python3 是解释器,test.py 是文件名。如果 python test.py 文件不在调用 python3 解释器的同一个目录中,我们必须提供 python 文件的绝对路径。

Python 的 ide

集成开发环境(IDE)是一个软件套件,它拥有编写和测试程序的所有基本工具。典型的 IDE 有一个编译器、一个调试器、一个代码编辑器和一个构建自动化工具。大多数编程语言都有各种 ide 让程序员的生活更美好。Python 也有许多 ide。让我们来看看 Python 的几个 ide。

闲置的

IDLE 代表集成开发环境。它与 Python 安装捆绑在一起。IDLE3 是针对 Python 3 的。很受 Python 初学者的欢迎。在 lxterminal 中运行 idle3 即可。下面是一个 IDLE3 代码编辑器的截图(图 3-1 )和交互提示。

A447085_1_En_3_Fig1_HTML.jpg

图 3-1。

IDLE3

盖尼

Geany 是一个使用 GTK+工具包的文本编辑器,具有集成开发环境的基本特性。它支持许多文件类型,并有一些不错的功能。详见 www.geany.org 。下面(图 3-2 )是 Geany 文本编辑器的截图。

A447085_1_En_3_Fig2_HTML.jpg

图 3-2。

Geany

Geany 预装在最新版本的 Raspbian 中。如果您的 Raspbian 安装没有 Geany,那么可以在 lxterminal 中运行 sudo apt-get install geany 来安装它。安装后,可以在下面的截图(图 3-3 )中找到 Raspbian 菜单➤编程➤安歌 y 程序员编辑器。

A447085_1_En_3_Fig3_HTML.jpg

图 3-3。

Raspbian Menu

键入 print("Hello World!")并在/home/pi 目录下将文件另存为 test.py,点击菜单栏中的 Build,然后执行。我们也可以使用键盘快捷键 F5 来执行程序。程序将在 lxterminal 窗口中执行。我们必须按回车键来关闭执行窗口。Geany 的默认 Python 解释器是 Python 2。我们需要把它改成 Python 3。为此,请转到构建➤设置构建命令。将出现以下(图 3-4 )窗口。

A447085_1_En_3_Fig4_HTML.jpg

图 3-4。

Set Build Commands

在此窗口中,在“执行命令”部分下,将 Python“% f”(在上图中红色框中高亮显示)更改为 python3“% f”,以将 Python 3 解释器设置为默认解释器。之后,再次运行程序,以验证一切都做得正确。

EXERCISE

为了更好地理解 Python 3 背景,请完成以下练习。

结论

在这一章中,我们学习了 Python 的背景、历史和特性。我们还学习了 Python 2.x 和 Python 3.x 之间的重要区别。我们学习了在脚本和解释器模式下使用 Python 3。我们看了一些流行的 Python ide,并在 Pi 上为 Python 3 配置了 geany。在本书的后面部分,我们将使用 Python 3 和 mpi4py 为我们即将构建的小型超级计算机进行并行编程。在下一章,我们将学习超级计算的基础知识。

四、超级计算简介

在上一章中,我们学习了 Python 编程语言的历史和哲学。

在这短短的一章中,我们将学习超级计算的概念和历史。

超级计算机的概念

超级计算机是一种具有强大处理能力的特殊计算机。这是超级计算机这个术语最简单的定义。超级计算机区别于其他类型计算机的关键特征是它们巨大的处理能力。

超级计算机用于计算密集型应用。这些大多是科学应用。以下是几个例子:

  • 天气预报
  • 气候研究
  • 分子建模
  • 物理模拟
  • 量子力学
  • 石油和天然气勘探

超级计算机简史

控制数据公司(CDC)是超级计算机的摇篮。在这里,1964 年,西摩·克雷建造了 CDC 6600。它被称为第一台超级计算机,因为它的性能超过了同时代的所有其他计算机。它的处理速度约为 10 兆赫。1968 年,西摩·克雷建造了 CDC 7600。它的处理速度是 35 兆赫。同样,它是最快的计算机,在计算能力方面超过了所有其他计算机。

这就是超级计算机的起源。最终,克雷离开了 CDC,并成立了自己的公司来设计和开发超级计算机。克雷创造了历史上一些最成功的超级计算机,即克雷 1、克雷 X-MP、克雷 2 和克雷 Y-MP。20 世纪 90 年代见证了大规模并行超级计算机的时代,数千个处理器以各种配置相互连接。一个显著的例子是 Intel Paragon,它可能有许多 Intel i860 处理器。

超级计算机的速度是以每秒浮点运算(FLOPS)而不是 MIPS(每秒百万条指令)来衡量的。英特尔 ASCI Red 是第一台 TFLOPS (Tera FLOPS)超级计算机。2008 年,IBM Roadrunner 成为第一台速度为 PFLOPS (Peta FLOPS)的超级计算机。

超级计算领域的下一个突破将是以 Exa-FLOPS 计算处理速度的 Exascale 超级计算机。

我在这里不提供前 10 名超级计算机或最快的超级计算机的列表。这是因为名单每年都在变化。此外,超级计算机基于各种参数进行排名,因此基于不同参数的不同来源的排名不会相同。

在设计大规模并行计算系统时,通常遵循两种方法。第一种是让分布在广阔地理区域的成千上万台计算机通过互联网来解决一个特定的问题。这在像互联网这样的广域网上工作得很好。这些类型的系统被称为分布式系统。另一种方法是将数千个处理节点彼此靠近放置。这节省了大量通信时间,并且大部分处理能力用于解决计算量巨大的问题。这种方法被称为聚类。所有的超级计算机都属于这一类。

计算机集群被定义为一组松散耦合或紧密耦合在一起工作的计算机。群集中的计算机称为节点。集群中的所有节点执行完全相同类型的任务。

我们将要开发的微型超级计算机将是一个 pi 集群。所有的超级计算机都是集群,但不是所有的集群都是超级计算机。正如我们在超级计算机的定义中所了解到的,超级计算机拥有巨大的处理能力。这就是为什么每个集群都没有资格被称为超级计算机。我们将在这里构建的集群不是一台超级计算机,因为它在处理能力方面比现实世界的超级计算机逊色,但它的工作原理与现实世界的超级计算机相同。因此我们称它为小型超级计算机。自从引入大规模并行系统以来,大规模集群和功能较弱的计算机之间的界限开始变得模糊。今天,很少有自制的集群像 20 世纪 80 年代的超级计算机那样强大。根据其配置,商品集群分为以下两类。

异质集群

当集群的所有节点不具有完全相同的硬件配置时,该集群被称为异构集群。在制作我的集群时,我使用了两个单位的 Pi B+,一个单位的 Pi 2,一个单位的 Pi 3,所以我的集群是一个异构集群。

贝奥武夫星团

与异构集群不同,Beowulf 集群中的所有节点具有完全相同的配置。我们可以用商品级硬件和 SBC(如 Raspberry Pi)制作同构集群和 Beowulf 集群。几乎所有集群都使用 Linux 的某个发行版作为其节点的操作系统。

根据您附近 Pi 模型的可用性,您可以创建异构集群或 Beowulf 集群。

并行和并发

让我们探讨一下超级计算领域的几个重要术语。

平行

并行性意味着计算任务是并行执行的。这意味着这些任务是同时执行的。并行通常用于计算问题非常大的情况。大问题通常被分成更小的子问题,然后由计算机并行解决。随着多核处理器的引入,硬件本身支持并行程序的执行。运行并行程序的另一种方式是通过使用多台不同的计算机来创建并行系统。Parallel 是 serial 的反义词,意思是一个接一个地串联。并行性与另一个术语“并发性”密切相关。

让我用简单的话解释一下排比。假设有两项工作要完成,有两个人可以承担这两项工作。两个人都被分配了一项工作,他们开始各自独立地工作。这就是所谓的并行。

并发

并发意味着许多计算任务同时进行。任务不必同时进行。在并行中,所有的任务同时执行。在并发中,它们不需要。在并发系统中,一个计算可以在不等待所有其他计算完成的情况下进行,并且可以同时进行多个计算。并发性的最好例子是操作系统中的进程调度。

让我用简单的语言解释一下并发性。假设要完成两项工作,而只有一个人可以完成所有的工作。这个人决定从第一份工作开始。他做了 30%,然后转到第二份工作。他完成了第二份工作的 40%,并切换回第一份工作。这种类型的切换会发生多次。我们可以说这两项工作都在进行中。虽然这些工作不是同时完成的,但它们正在朝着完成的方向前进。最后,两个工作都完成了。并发是顺序的反义词。

并行编程

所有集群和超级计算机都使用并行性来将计算量巨大的任务分解成较小的块,然后收集结果作为最终输出。支持这种类型操作的编程范例被称为并行编程。消息传递接口(MPI)是工业界和学术界最常用的并行编程标准之一。我们将在下一章研究如何在 Python 3 的 Pi 上安装它。

结论

在这短短的一章中,我们学习了一些与超级计算相关的重要概念,我们还学习了超级计算机的历史。

在下一章中,我们将学习如何建立一个树莓派集群的节点。

五、消息传递接口

在上一章中,我们学习了超级计算机的历史和哲学。我们还学习了与超级计算相关的重要概念。

在这简短的一章中,我们将开始在 Raspberry Pi 上安装必要的包和库。我们将安装 MPI4PY,这是一个用于 MPI 的 Python 库。最后,我们将安装用于节点发现的实用程序 nmap。

消息传递接口

消息传递接口标准(MPI)是基于 MPI 论坛建议的消息传递库标准。MPI 论坛在美国和欧洲有超过 40 个参与组织。消息传递接口的目标是为消息传递定义一个可移植的、有效的、灵活的标准,它将被广泛用于编写各种各样的消息传递程序。MPI 是第一个独立于供应商的消息传递库标准。使用 MPI 标准开发消息传递程序的优点是可移植性、效率和灵活性。尽管 MPI 不是 IEEE 或 ISO 标准,但它已经成为为高性能计算(HPC)、并行计算机、集群和分布式系统等各种平台编写消息传递程序的行业标准。MPI 标准定义了库例程的语法和语义,用于用 C、C++和 Fortran 编写可移植的消息传递程序。

与 MPI 相关的一些重要事实如下:

  • MPI 是库的规范。MPI 本身不是一个库。
  • MPI 的目标是消息传递标准应该实用、可移植、高效和灵活。
  • 根据 MPI 标准的实现方式,实际的 MPI 库略有不同。
  • MPI 标准已经经历了几次修订。最新版本是 MPI-3.2。

Note

有关 MPI 论坛和标准的更多信息,请访问 MPI 论坛的主页( www.mpi-forum.org )和 MPI 标准文档页面( www.mpi-forum.org/docs/docs.html )。

MPI 标准的历史和发展

1992 年 4 月 29-30 日,一个关于分布式内存环境中消息传递标准的研讨会在弗吉尼亚州的威廉斯堡召开。讨论了标准消息传递接口的基本特征,并成立了一个工作组来继续标准化进程。从那以后,MPI 的工作继续进行,工作组定期开会。MPI 标准草案在 1993 年 11 月的超级计算’93 会议上提出。经过一段时间的公众评议,MPI 标准发生了一些变化,1994 年 6 月发布了 MPI 1.0 版。这些会议和电子邮件讨论一起导致了 MPI 论坛的形成。来自美国和欧洲 40 个组织的大约 80 人参与了 MPI 标准化工作。到目前为止,MPI 的最新版本是 MPI-3.2,我们将使用它来构建集群。

MPI 的特性

MPI 针对具有分布式内存和连接所有节点的网络的分布式系统进行了优化,如图 5-1 所示。

A447085_1_En_5_Fig1_HTML.gif

图 5-1。

Distributed memory system

以下是消息传递接口的功能:

  • 简单性:MPI 范式的基础是传统的通信操作。
  • 通用性:它可以在大多数基于并行架构的系统上实现。
  • 性能:实现可以匹配底层硬件的速度。
  • 可伸缩性:相同的程序可以部署在更大的系统上,而无需对其进行任何更改。

当我们开始学习如何用 MPI4PY 编码时,我们将研究 MPI 范例的更多细节。

MPI 的实现

正如我们已经看到的,MPI 不是一个库,而是开发消息传递库的标准,有几种 MPI 实现。以下是最受欢迎的实现:

  • 奥林匹克运动会
  • MP-MPICH (MP 代表多平台)
  • 温姆皮奇
  • MPI BIP
  • 惠普的 MPI
  • IBM 的 MPI
  • SGI 的 MPI
  • 邮票
  • OpenMPI(开放原始码)

MPI4PY

MPI4PY 代表 Python 的 MPI。MPI for Python 为 Python 提供了 MPI 绑定。这允许任何 Python 程序使用多处理器配置计算机。这个包是建立在 MPI-1/2/3 规范之上的。它为 Python 中的并行编程提供了面向对象的接口。它支持任何 Python 对象的点对点(发送和接收)和集体(广播、分散和收集)通信。

图 5-2 描绘了 MPI4PY 的概况。

A447085_1_En_5_Fig2_HTML.gif

图 5-2。

Philosophy of MPI4PY

为什么要使用 Python、MPI 和 MPI4PY 的组合?

Python 是 HPC(高性能计算)中三种最常用的编程语言之一。另外两个是 C 和 FORTRAN。正如我们前面看到的,Python 语法很容易理解和学习。MPI 是 HPC 和并行编程的事实上的标准。自 1994 年左右(20 多年)以来,它就一直存在。MPI4PY 是一个广受好评的、干净的、高效的 Python MPI 实现。它涵盖了大部分 MPI-2 标准。这就是为什么我们要在树莓派 上使用 Python 3 配合 MPI4PY 进行并行编程。

在 Raspbian 上为 Python3 安装 MPI4PY

在 Raspbian 上安装 Python 3 的 MPI4PY 非常简单。用户只需在 lxterminal 中运行以下命令:

sudo apt-get install python3-mpi4py -y

为 Python 3 安装 MPI4PY 需要几分钟时间。

要检查它是否已安装,请运行以下命令:

mpirun hostname

它应该输出主机名 raspberrypi。

运行以下命令启动多个进程:

mpirun -np 3 hostanme

输出如下所示:

raspberrypi
raspberrypi
raspberrypi

我们还可以运行以下命令来检查系统上安装的 MPI 版本:

mpirun -V

这样我们就可以安装、运行和验证 MPI4PY。

Note

有关更多详细信息,请访问 mpirun 手册页( www.open-mpi.org/doc/v1.8/man1/mpirun.1.php )。在本书的后半部分,我们将在 Python 3 中广泛使用 mpirun,在那里我们将详细研究它。

正在安装 nmap

nmap 是一个网络安全扫描器。我们将使用它来发现我们的 Pi 的 IP 地址。我们将在下一章使用它。现在,只需通过运行以下命令来安装 nmap:

sudo apt-get install nmap

结论

在本章中,我们学习了通过安装 MPI4PY 为超级计算准备一个 Pi。在下一章中,我们将通过连接多个 pi 来构建一台超级计算机。

六、构建超级计算机

在前一章中,我们通过安装必要的库、框架和工具为超级计算准备了 Pi。在本章中,我们将学习创建一个由多个 pi 组成的网络,并使用它来并行运行各种命令。

制作 MicroSD 卡的备份

一旦我们配置了 Pi,更新了它,并安装了必要的软件包和实用程序,我们就应该备份 microSD 卡。这是必要的,因为(上帝禁止!)如果 microSD 卡或 Pi 被损坏或丢失,那么我们可以用备份来继续我们的工作。建议始终备份一个安装并更新了 Raspbian 操作系统的 microSD 卡。此外,在安装了项目所需的必要包、工具和实用程序之后进行备份也是一个好主意,我总是遵循这个好主意。

要备份 microSD 卡,首先将其从 Pi 中取出,并将其插入 SD 读卡器。将 SD 读卡器连接到安装了 Win32DiskImager 的 Windows 计算机。打开 Win32DiskImager 并选择一个位置。为备份文件选择一个合适的名称。追加扩展名。img 或者。文件名后的 IMG。的。img 或者。IMG 扩展用于原始存储磁盘映像。见下图(图 6-1 )截图举例。

A447085_1_En_6_Fig1_HTML.jpg

图 6-1。

Taking the microSD card backup

然后点击阅读按钮。一旦完成,将显示如下(图 6-2 )对话框。

A447085_1_En_6_Fig2_HTML.jpg

图 6-2。

Backup completed

Raspbian OS、MPICH 和 MPI4PY 安装的更新版本的映像文件现在保存在硬盘上。我们可以使用这个图像为超级计算机的其余节点准备其他 microSD 卡。要做到这一点,我们只需要用 Win32DiskImager 把这个镜像写到其他 microSD 卡上。

准备超级计算机的节点

使用我们在上一节中准备的操作系统映像,我们将准备超级计算机的其他节点。使用 Win32DiskImager 将此操作系统映像写入其他 microSD 卡。一旦所有的 microSD 卡都准备好了,就把它们插入 Pis。每个 Pi 应该有一个唯一的主机名。我们可以用 raspi-config 更改 Pi 的主机名。转到高级选项并选择 A2 主机名。更改主机名,如下图截图所示(图 6-3 )。

A447085_1_En_6_Fig3_HTML.jpg

图 6-3。

Changing the hostname

主机名更改后,lxterminal 中的提示会出现在下面的截图中(图 6-4 )。

A447085_1_En_6_Fig4_HTML.jpg

图 6-4。

The lxterminal prompt after changing the hostname

如您所见,提示符和 lxterminal 窗口标题中的主机名raspberrypi被替换为pi001

对所有 pi 执行相同的步骤,并将它们的主机名更改为pi002pi003等等。

Pis 联网

这是一个有点棘手的部分。我们可以通过许多方式将 pi 连接在一起。选择取决于基础设施和可用的预算。让我们探索几个简单易行的方法。

使用 DHCP 的局域网

此选项适用于受管理的网络交换机和 WiFi 路由器。访问受控交换机或 WiFi 路由器的管理控制台。登录到管理控制台后,将有选项来设置 DHCP 的地址范围。修改该范围,因为我们将向其中添加更多设备。

对于 WiFi 路由器来说,管理控制台通常是一个网页。可以通过连接到 WiFi,然后在浏览器中键入其 IP 地址来访问它。它通常以用户名和密码的形式进行验证,通常在 WiFi 路由器手册中列出。每个 WiFi 路由器都有用于有线局域网的以太网端口,如下图所示(图 6-5 )。

A447085_1_En_6_Fig5_HTML.jpg

图 6-5。

Rear side of a WiFi router (image from https://www.flickr.com/photos/smemon/ )

更新/etc/network/interfaces中的网络设置,作为 IP 地址分配方案自动连接到 LAN 和 DHCP。以下是一个示例/etc/network/interfaces文件:

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

WiFi 网络

我们知道 Pi 3 之前的所有型号的 Pi 都需要 USB WiFi 适配器。最好的选择是如果你有一个 WiFi 路由器和许多 Pi 3s。如果 Pis 的型号早于 Pi 3,您将不得不投资购买 USB WiFi 适配器。在为相关型号的 Pi 配备 USB WiFi 适配器后,更新/etc/network/interfaces中的网络设置,以便自动连接到 WiFi。

下面是一个示例/etc/network/interfaces文件。

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto wlan0
iface wlan0 inet dhcp
wpa-ssid "ASHWIN"
wpa-psk "internet"

ASHWIN替换为您 WiFi 网络的 ssid,将internet替换为您 WiFi 网络的密码。

具有静态 IP 地址的局域网

这是我的首选方法。所有被管理的网络交换机和 WiFi 路由器都有一个静态 IP 地址范围。为集群中的节点选择几个地址,然后更新/etc/network/interfaces文件。使用受管网络交换机或 WiFi 路由器的 IP 地址作为网关的值。

使用低成本的非托管网络交换机可能是最便宜的选择。“未被管理”意味着它们没有管理控制台。只需用以太网电缆将 pi 连接到交换机,并按如下方式更新/etc/network/interfaces:

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static

# Your static IP
address 192.168.0.2

# Your gateway IP
gateway 192.168.0.1
netmask 255.255.255.0

# Your network address family
network 192.168.0.0
broadcast 192.168.0.255

对于所有 pi,网络设置(除了 IP 地址)将与上述相同。IP 地址将是唯一的。此外,由于交换机不受管理,我们通过使用与网关相同的值(在上例中为 192.168.0.1)为其手动分配 IP 地址。所有 pi 的网关值必须相同。

更改网络设置后重新启动 Pis。

以下是几款低成本非托管交换机及其各自的产品页面。

使用 nmap 查找 Pis 的 IP 地址

不管网络类型(以太网或 WiFi)和 IP 地址分配方案(静态或动态),我们都需要知道网络中所有 pi 的 IP 地址,以便将 pi 的网络用作集群。在前一章中,我们安装了nmap实用程序。我们现在将使用它来查找网络中 pi 的 IP 地址。

将显示器、键盘和鼠标连接到pi001。我们将使用pi001作为主节点。我们将在pi001使用 lxterminal 为超级计算机运行命令和并行程序。

将所有 pi 连接到网络。我们不需要将任何显示器或 I/O 设备连接到其他 pi。

启动所有 Pis。一旦所有 pi 启动,用nmap扫描网络。

以下是扫描网络的命令:

sudo nmap -sn 192.168.0.*

在上面的命令中,192.168.0.*中的前三个字节对应于我的网络的 IP 地址。用网络标识符的前三个字节替换它们,并在pi001上的 lxterminal 中运行命令。

输出如下所示:

Starting Nmap 6.47 ( http://nmap.org ) at 2016-09-15 18:02 IST
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled.\
Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for 192.168.0.2
Host is up (0.0020s latency).
Nmap scan report for 192.168.0.3
Host is up (0.0018s latency).
Nmap scan report for 192.168.0.4
Host is up (0.0016s latency).
Nmap scan report for 192.168.0.5
Host is up (0.0014s latency).
Nmap done: 256 IP addresses (4 hosts up) scanned in 2.70 seconds

记下网络中所有覆盆子 pi 的 IP 地址。在这种情况下,IP 地址是 192.168.0.2、192.168.0.3、192.168.0.4 和 192.168.0.5。

使用 mpirun 在多个 pi 上运行 hostname 命令

pi001上,通过运行以下命令导航到/home/pi:

cd ∼

创建一个名为myhostfile的新文件。将之前记下的所有 IP 地址添加到myhostfile中,如下所示:

192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5

现在运行以下命令:

mpirun -hostfile myhostfile -np 4 hostname

它将显示一个带有错误的输出,并且命令hostname不会在文件myhostfile中列出的所有主机上运行。

这是因为我们试图在pi002pi003pi004上远程运行来自pi001的命令。我们没有这方面的认证。

为自动认证交换 ssh-keygen 密钥

ssh-keygen实用程序用于生成认证密钥。要在任意两台 Linux 计算机之间建立身份验证,请执行以下步骤:

  1. 使用ssh-keygen.为两台主机生成密钥
  2. 通过远程复制密钥,在主机之间交换密钥。
  3. 将密钥添加到授权主机列表中。

完成后,我们可以在没有密码的情况下执行以下操作,因为在密钥交换后,不会再次提示输入密码。

  1. 登录到远程主机。
  2. 在远程主机上执行 shell 命令。

我们还可以在 shell 脚本中使用ssh命令来自动化远程主机中的任务。我们使用pi001作为主节点,所以我们希望从其他节点上的pi001远程运行命令,反之亦然。我已经建立了一个包含四个节点的集群,因此用于密钥交换的主机对是(pi001,pi002)、(pi001,pi003)和(pi001,pi004)。

让我们交换钥匙。打开 lxterminal 并转到pi001的主目录。

cd ∼

运行ssh-keygen命令生成密钥。只要在每次提示输入时按下回车键。以下是 lxterminal 上的输出:

Generating public/private rsa key pair.
Enter file in which to save the key (/home/pi/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/pi/.ssh/id_rsa.
Your public key has been saved in /home/pi/.ssh/id_rsa.pub.
The key fingerprint is:
03:bc:3f:5a:28:88:b7:ac:6c:50:f0:81:5e:f9:6d:5f pi@pi001
The key's randomart image is:
+---[RSA 2048]----+
| . .             |
|o .o .           |
|.o... +          |
| .o . = E        |
| . o S .         |
|.. . o o         |
|o o . . +        |
|.+ . . o .       |
|ooo .            |
+-----------------+

请注意,由于每次执行生成的密钥不同,命令提示符上显示的图像每次都会不同。

上面的执行在 Pi 的主目录中创建了一个隐藏目录.ssh。转到.ssh目录。

cd .ssh

通过运行ls命令检查.ssh目录的内容,如下所示:

pi@pi001:∼/.ssh $ ls
id_rsa id_rsa.pub

在上面的输出中,id_rsa是私钥,id_rsa.pub是主机pi001的公钥。我们必须将公钥复制到我们想要远程登录和执行命令的主机上。

为了保持有序,我们将公钥id_rsa.pub复制到一个新文件pi01

cp id_rsa.pub pi01

我们需要将这个pi01文件的内容添加到其他主机的authorized_keys文件中,以便在没有身份验证的情况下实现远程访问。

现在使用以下命令登录到pi002:

ssh pi@192.168.0.3

系统会提示我们输入pi002的密码。输入密码。

像我们在pi001上做的那样,在pi002上运行以下命令来生成公钥:

ssh-keygen
cd .ssh
cp id_rsa.pub pi02

现在我们需要使用scppi001的公钥文件复制到pi002

scp 192.168.0.2:/home/pi/.ssh/pi01 .

通过运行以下命令,将pi01的内容添加到authorized_keys中:

cat pi01>>authorized_keys

最后,使用注销命令从pi002注销。

对于pi003,我们又要按照同样的步骤。

登录到pi003

ssh pi@192.168.0.4

pi003上运行以下命令序列:

ssh-keygen
cd .ssh
cp id_rsa.pub pi03
scp 192.168.0.2:/home/pi/.ssh/pi01 .
cat pi01>>authorized_keys
logout

对于pi004,我们又要按照同样的步骤。

登录到pi004

ssh pi@192.168.0.5

pi004上运行以下命令序列:

ssh-keygen
cd .ssh
cp id_rsa.pub pi04
scp 192.168.0.2:/home/pi/.ssh/pi01 .
cat pi01>>authorized_keys
logout

pi001中,运行以下命令序列,将pi002pi003pi004的公钥复制到pi001

cd /home/pi/.ssh
scp 192.168.0.3:/home/pi/.ssh/pi02 .
scp 192.168.0.4:/home/pi/.ssh/pi03 .
scp 192.168.0.5:/home/pi/.ssh/pi04 .

然后运行以下命令,将这些公钥添加到pi001的授权密钥列表中:

cat pi02>>authorized_keys
cat pi03>>authorized_keys
cat pi04>>authorized_keys

这就完成了集群的设置。要测试设置,在pi001上运行以下命令:

mpirun -hostfile myhostfile -np 4 hostname

上面命令的输出应该如下所示:

pi001
pi002
pi003
pi004

恭喜你。我们已经建立了自己的小型超级计算机集群。在下一节中,我们将学习如何在一个漂亮的栈中组织我们的集群。

在集群中组织 pi

当我建立我的第一个集群时,我想到了创建一个定制的丙烯酸案件;然而,估计的费用超出了我的预算。我还想到通过 3D 打印为集群创建一个定制案例,但 3D 打印承包商也为此报出了一笔天文数字。所以我决定尝试一种更具成本效益的方法来组织集群中的 pi。我使用 M3 六角间隔垫片来创建一个 Pis 栈。我们需要两种类型的对峙,男性对女性和女性对女性。我们现在将使用这些来创建一个栈。重要的是,支架的长度必须至少为 25 毫米,以避免 Raspberry Pi PCBs 相互接触。

Note

在谷歌上搜索 M3 六角支架垫片。

如下图所示(图 6-6 ),取四个凸对凹支架,并将它们连接到一个 Pi 上。

A447085_1_En_6_Fig6_HTML.jpg

图 6-6。

Attaching male-to-male standoffs to the Pi

之后,取四个凹对凹支座,将它们连接到 Pi 的底部,如下图所示(图 6-7 )。

A447085_1_En_6_Fig7_HTML.jpg

图 6-7。

Attaching female-to-female standoffs to the cluster base

现在将第二个 Pi 连接到此处,如下图所示(图 6-8 )。

A447085_1_En_6_Fig8_HTML.jpg

图 6-8。

Adding second Pi to the stack

最后,将剩余的两个 pi 添加到此之后,集群栈看起来如下图所示(图 6-9 )。

A447085_1_En_6_Fig9_HTML.jpg

图 6-9。

Raspberry Pi Supercomputer stack

结论

在本章中,我们学习了如何将几个 pi 连接在一起,构建一个超低成本的迷你超级计算机集群。我们还了解了如何在一个方便的栈中组织集群。在下一章中,我们将学习如何超频 Pi 的各种模型,以增加集群中 Pi 的计算能力,而无需额外的成本。

七、超频树莓派

在这一章中,我们将学习如何通过超频这台神奇的小计算机的各种组件来增加各种型号的 Raspberry Pi 的计算能力。我们将通过raspi-config和通过改变config.txt的内容来研究如何超频。

超频意味着配置计算机的硬件部件以比原始制造商认证的速度更快的速度运行。器件的工作速率通常用时钟频率来表示,如 MHz、GHz 等。通常超频组件的工作电压也会增加,这有助于在加速时保持组件的工作稳定性。然而,超频的缺点是,给定的半导体器件在比常规设置更高的频率和电压下工作时会产生和散发更多的热量,因此大多数超频尝试也会增加功耗和散热。为了减轻超频组件中增加的散热,我们通常必须安装散热器和冷却系统。

让我们从超频 Raspberry Pi 的基础开始。在本章中,我们将详细探讨如何安装被动散热器以及如何超频各种型号的 Raspberry Pi。

树莓派 超频的风险

超频 Raspberry Pi 使我们能够充分利用它。然而,我们不应该在不了解风险的情况下这样做。非常重要的一点是,我们要明白我们将要面对的是什么。

这里有一些我们必须注意的超频风险:

  • 寿命缩短:部件可能会很快失效。
  • 发热:以更高的速度运行会产生和散发更多的热量。
  • 文件损坏:许多超频者在未优化的超频设置下观察到文件损坏。
  • 保修失效:强制过电压将使保修失效。

在 Pi 上安装散热器

散热器是用于吸收多余热量的装置或物质。如果您计划超频您的 Pi,那么建议在处理器、内存和图形处理器上安装散热器。

大多数散热器上都粘有不干胶。用它们在 Pi 的芯片上安装散热片。大多数人只在使用 Raspberry Pi 时使用被动散热器,因为像液体冷却和/或散热器这样的主动散热机制对 Raspberry Pi 来说太过了。

采购散热器

许多经销商在网上销售散热器。你可以谷歌一下关键词树莓派散热器。

以下是销售散热器的各个经销商的网站链接:

使用 raspi-config 超频 Pi

我们可以使用raspi-config工具中的超频选项对 Pi 进行超频。

下面(图 7-1 )是 Pi B 和 Pi B+中的超频选项截图。

A447085_1_En_7_Fig1_HTML.jpg

图 7-1。

Overclock options in Pi B and Pi B+

下面(图 7-2 )是 Pi 2 中超频选项的截图。

A447085_1_En_7_Fig2_HTML.jpg

图 7-2。

Overclock options in Pi 2

使用/boot/config.txt 对 Pi 进行超频

我们已经学会了如何用raspi-config超频;但是,它不允许对将要超频的 Pi 进行微调。还有一种超频 Pi 的方法,我们可以通过改变/boot/config.txt中的几个参数来手动实现。

/boot/config.txt 中的选项

我们可以在/boot/config.txt文件中添加或更改选项。如果你已经使用了raspi-config工具进行超频和/或内存分割,那么你会发现这些选项中的许多已经出现在了/boot/config.txt文件中。

  • arm_freq:ARM 内核的频率,单位为 MHz。
  • core_freq:GPU 处理器内核的频率,单位为 MHz。
  • h264_freq:硬件视频块的频率,单位为 MHz。
  • isp_freq:图像传感器流水线块的频率,单位为 MHz。
  • v3d_freq:3D 块的频率,单位为 MHz。
  • avoid_pwm_pll:不要将 pll 专用于 PWM 音频。这将略微降低模拟音频质量。备用 PLL 允许 core_freq 独立于 GPU 的其余部分设置,从而对超频进行更多控制。
  • dram_freq:SDRAM 的频率,单位为 MHz。
  • over_voltage : ARM/GPU 核心电压调整。
  • over_voltage_sdram_c : SDRAM 控制器电压调整。
  • over_voltage_sdram_i : SDRAM I/O 电压调整。
  • over_voltage_sdram_p : SDRAM phy 电压调整。
  • force_turbo:禁用动态 cpufreq 驱动程序和以下最低设置。启用 H.264/V3D/ISP 超频选项。可以设置保修位。
  • temp_limit:过热保护。当 SoC 达到该摄氏度值时,将时钟和电压设置为默认值。设置高于默认值会使保修失效。
  • gpu_mem : GPU 内存,单位兆字节。设置 ARM 和 GPU 之间的内存分配。ARM 获得剩余的内存。如果已经使用 raspiconfig 设置了该值,则无需进行设置。

各种型号 Pi 的/boot/config.txt 选项

每个圆周率都是唯一的。必须为单个 Pi 定制/boot/config.txt中超频 Pi 的选项值。这些选项没有一套最适合所有 pi 的值。我通过反复试验学到了这一点。如果您为超频选项设置的值不是最合适的,Pi 将会不稳定或根本无法启动。在这些情况下,您可以尝试使用其他计算机更改 microSD 卡上config.txt中的超频选项值,然后使用同一张卡启动 Pi。这样做,直到你得到一组稳定的和最优的 Pi 值。一般来说,对于任何类型的硬件(如 CPU 和 RAM),超频设置取决于单个 IC。

我们可以在 eLinux RPi 配置页面( http://elinux.org/RPiconfig )找到上述选项的详细解释。

Pi B 和 Pi B+的选项

以下是 Pi B 和 Pi B+的超频选项值:

arm_freq=950
core_freq=450
sdram_freq=500
gpu_mem=16

Pi 2 的选项

以下是 Pi 2 的超频选项值:

arm_freq=1100
over_voltage=4
core_freq=550
sdram_freq=483
over_voltage_sdram_p=0
over_voltage_sdram_i=0
over_voltage_sdram_c=0
gpu_mem=16
force_turbo=1
avoid_pwm_pll=1
v3d_freq=450
h264_freq=0
isp_freq=0
avoid_safe_mode=1

Pi 3 的选项

以下是 Pi 3 的超频选项值:

arm_freq=1350
over_voltage=6
temp_limit=80
core_freq=500
h264_freq=333
avoid_pwm_pll=1
gpu_mem=450
v3d_freq=550
sdram_freq=588
sdram_schmoo=0x02000020
over_voltage_sdram_p=6
over_voltage_sdram_i=4
over_voltage_sdram_c=4
force_turbo=1

如果您的 Pi 无法启动或变得不稳定,请尝试调整这些值以获得最佳设置。

我们无法涵盖稳定超频的所有可能的工作设置组合。浏览以下网站链接,了解超频相关信息:

http://linuxonflash.blogspot.in/2015/02/a-look-at-raspberry-pi-2-performance.html

https://github.com/retropie/retropie-setup/wiki/Overclocking

超频后,使用cat /proc/cpuinfo命令检查所有型号的处理器速度。

Note

不要将超频 Pi 的 microSD 卡插入另一个 Pi。记住!每件硬件都有独特的超频设置。如果要将一个超频 Pi 的 microSD 卡用于另一个 Pi,请禁用/boot/config.txt 中的超频设置,然后将其用于另一个 Pi。

Exercise

超频所有你打算用来构建小型超级计算机集群的 Raspberry Pis。这将在运行时方面提高个人和整体的性能。

结论

在这一章中,我们学习了如何超频各种型号的树莓派。从下一章开始,我们将开始研究如何利用我们构建的集群的计算能力。我们将使用 MPI4PY 用 Python 3 编写代码,演示 MPI 和并行编程中的概念。

八、Python 3 中的并行编程

在上一章中,我们学习了如何对各种型号的 Raspberry Pi 进行超频,以增加其计算能力。在本章中,我们将学习如何用 Python 和 MPI4PY 编写并行程序。我更喜欢 Python,因为它简单,而且 Python 中的代码不那么吓人。我们将探索 MPI 概念,并用 MPI4PY 在 Python 中实现这些概念。

我们将研究和实现的 MPI 概念如下:

  • MPI 等级和流程
  • 发送和接收数据
  • 数据标记
  • 广播数据
  • 分散和收集数据

MPI4PY 的基础知识

在本书的前面部分,我们学习了一些 MPI 概念。这一章我们再研究几个。

MPI 使用单程序多数据( SPMD )的概念。以下是 SPMD 建筑的要点:

  • 所有进程(称为等级)运行相同的代码,每个进程访问不同的数据部分。
  • 所有进程同时启动。

并行程序被分解成单独的进程,称为等级。每个列都有自己的地址空间,这需要跨列划分数据。每个等级在自己的私有内存中保存一部分程序数据。等级按从 0 到 n-1 的顺序编号。下图(图 8-1 )描述了同时运行的多个队列。

A447085_1_En_8_Fig1_HTML.gif

图 8-1。

Multiple ranks running simeltaneously

在下一节中,我们将看到一个带有等级的基本程序。

MPI4PY 入门

让我们从简单的 Hello World 开始吧!用 MPI4PY 用 Python 编程。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
name = MPI.Get_processor_name()

sys.stdout.write("Hello World!")
sys.stdout.write(" Name: %s, My rank is %d\n" % (name, comm.rank))

Listing 8-1.prog01.py

在上面的代码(清单 8-1 )中,语句from mpi4py import MPI导入所需的 MPI4PY 库。在第六章中,我们学习了 MPI 中通信者的概念。MPI.COMM_WORLD是传播者。它用于运行在集群进程上的进程之间的所有 MPI 通信。Get_processor_name()返回当前进程运行的主机名。comm.rank是当前流程的等级。下图(图 8-2 )描绘了COMM_WORLD

A447085_1_En_8_Fig2_HTML.gif

图 8-2。

COMM_WORLD

你可能已经注意到我们正在控制台上使用sys.stdout.write()进行打印。这是因为我希望代码兼容 Python 编程语言的两种解释器,python(Python 2 的解释器)和python3。在本书中,我们不会使用任何特定于任一解释器的特性或编程结构。因此,代码可以使用两种解释器运行。

这一章我们已经开始编码了,后面的章节有很多代码样例和练习。将代码和数据组织在单独的目录中是一个好主意。在 lxterminal 中逐一运行以下命令:

mpirun -hostfile myhostfile -np 4 mkdir /home/pi/book
mpirun -hostfile myhostfile -np 4 mkdir /home/pi/book/code
mpirun -hostfile myhostfile -np 4 mkdir /home/pi/book/code/chapter08

这将在小型超级计算机的所有节点上创建相同的目录结构。现在将上面的代码保存在∼/book/code/chapter08目录下一个名为prog01.py的文件中。使用scp as将代码文件复制到所有节点上的那个目录,如下所示:

scp book/code/chapter08/prog01.py 192.168.0.2:/home/pi/book/code/chapter08/
scp book/code/chapter08/prog01.py 192.168.0.3:/home/pi/book/code/chapter08/
scp book/code/chapter08/prog01.py 192.168.0.4:/home/pi/book/code/chapter08/

最后,在pi001上运行mpirun,如下所示:

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog01.py

以下是输出:

Hello World! Name: pi001, My rank is 0
Hello World! Name: pi002, My rank is 1
Hello World! Name: pi004, My rank is 3
Hello World! Name: pi003, My rank is 2

对于我们将在本章剩余部分讨论的所有其他代码示例,我们必须遵循相同的步骤。让我简单重复一遍:在chapter08目录下创建一个 Python 代码文件,将该文件复制到集群所有节点的chapter08目录下,最后使用mpirun和 Python 解释器来执行代码。

条件语句

我们可以在 MPI4PY 代码中使用如下条件语句(清单 8-2 ):

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
sys.stdout.write("My rank is: %d\n" % (comm.rank))

if comm.rank == 0:
    sys.stdout.write("Doing the task of Rank 0\n")

if comm.rank == 1:
    sys.stdout.write("Doing the task of Rank 1\n")

Listing 8-2.prog02.py

在这段代码中,我们检查进程等级是 0 还是 1,然后将更多消息打印到控制台。用mpiexec运行程序如下:

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog02.py

上面的程序(清单 8-2 )的输出如下:

My rank is: 0
Doing the task of Rank 0
My rank is: 1
Doing the task of Rank 1
My rank is: 3
My rank is: 2

检查流程的数量

让我们编写代码(清单 8-3 )来显示 MPI 进程的等级和数量。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
rank = comm.rank
size = comm.size

sys.stdout.write("Rank: %d," % rank)
sys.stdout.write(" Process Count: %d\n" % size)

Listing 8-3.prog03.py

在上面的代码中,comm.size给出了在集群中运行的 MPI 进程的数量。使用mpiexec运行上面的代码,如下所示:

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog03.py

输出如下所示:

Rank: 0, Process Count: 4
Rank: 1, Process Count: 4
Rank: 2, Process Count: 4
Rank: 3, Process Count: 4

发送和接收数据

使用send()receive()进行进程间的数据传输是进程间最简单的通信形式。我们可以用这个实现一对一的交流。下图(图 8-3 )清楚地解释了这一点。

A447085_1_En_8_Fig3_HTML.gif

图 8-3。

One-to-one communication

让我们来看看代码示例(清单 8-4 )的相同之处。

from mpi4py import MPI
import time
import sys

comm = MPI.COMM_WORLD

rank = comm.rank
size = comm.size
name = MPI.Get_processor_name()

shared = 3.14

if rank == 0:
    data = shared
    comm.send(data, dest=1)
    comm.send(data, dest=2)
    sys.stdout.write("From rank %s, we sent %f\n" % (name, data))
    time.sleep(5)

elif rank == 1:
    data = comm.recv(source=0)
    sys.stdout.write("On rank %s, we received %f\n" % (name, data))

elif rank == 2:
    data = comm.recv(source=0)
    sys.stdout.write("On rank %s, we received %f\n" % (name, data))

Listing 8-4.prog04.py

在上面的代码示例中,我们从等级为 0 的流程中发送数据。等级为 1 和 2 的进程正在接收数据。

让我们运行程序。

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog04.py

上面的程序(清单 8-4 )的输出如下:

On rank pi002, we received 3.140000
On rank pi003, we received 3.140000
From rank pi001, we sent 3.140000

动态发送和接收数据

到目前为止,我们已经为发送和接收数据的过程编写了条件语句。然而,在大型分布式网络中,由于进程数量的不断变化,这种类型的数据传输并不总是可能的。此外,用户可能不想手工编写条件语句。

下面的例子(清单 8-5 )展示了动态数据传输的概念。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
rank = comm.rank
size = comm.size
name = MPI.Get_processor_name()

shared = (rank+1)*(rank+1)

comm.send(shared, dest=(rank+1) % size)
data = comm.recv(source=(rank-1) % size)

sys.stdout.write("Name: %s\n" % name)
sys.stdout.write("Rank: %d\n" % rank)
sys.stdout.write("Data %d came from rank: %d\n" % (data, (rank-1) % size))

Listing 8-5.prog05.py

在上面的代码(清单 8-5 )中,每个进程都从前面的进程接收数据。这种情况一直持续到结束并循环,这样第一个进程从最后一个进程接收数据。

让我们运行代码。

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog05.py

代码的输出如下所示:

Name: pi001
Rank: 0
Data 16 came from rank: 3
Name: pi002
Rank: 1
Data 1 came from rank: 0
Name: pi003
Rank: 2
Data 4 came from rank: 1
Name: pi004
Rank: 3
Data 9 came from rank: 2

如前所述,等级为 0 的进程(第一个进程)从等级为 3 的进程(最后一个进程)接收数据。

数据标记

在前面的例子(清单 8-5 )中,我们研究了如何用 MPI 发送和接收数据。这给好奇的程序员提出了一个基本问题:我们如何在进程间交换多个数据项?我们可以将多个数据项从一个进程发送到另一个进程。然而,在接收端,我们会遇到区分不同数据项的问题。解决这个问题的方法是标记。看看下面的代码示例(清单 8-6 )。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
rank = comm.rank
size = comm.size
name = MPI.Get_processor_name()

if rank == 0:
    shared1 = {'d1': 55, 'd2': 42}
    comm.send(shared1, dest=1, tag=1)

    shared2 = {'d3': 25, 'd4': 22}
    comm.send(shared2, dest=1, tag=2)

if rank == 1:
    receive1 = comm.recv(source=0, tag=1)
    sys.stdout.write("d1: %d, d2: %d\n" % (receive1['d1'], receive1['d2']))
    receive2 = comm.recv(source=0, tag=2)
    sys.stdout.write("d3: %d, d4: %d\n" % (receive2['d3'], receive2['d4']))

Listing 8-6.prog06.py

在上面的例子中,我们将两个不同的字典shared1shared2从等级为 0 的进程发送到等级为 1 的进程。在源端,shared1被标记为 1,shared2被标记为 2。在目的地,我们可以从与它们相关联的标签中区分不同的数据项。

用下面的命令运行上面的代码(清单 8-6 ):

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog06.py

输出如下所示:

d1: 55, d2: 42
d3: 25, d4: 22

数据标记使程序员能够更好地控制数据流。当多个数据在流程之间交换时,数据标记是必须的。

数据广播

当数据从一个进程发送到所有其他进程时,就称为广播。考虑下面的代码(清单 8-7 ):

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
rank = comm.rank

if rank == 0:
    data = {'a': 1, 'b': 2, 'c': 3}
else:
    data = None

data = comm.bcast(data, root=0)
sys.stdout.write("Rank: %d, Data: %d, %d, %d\n"
                 % (rank, data['a'], data['b'], data['c']))

Listing 8-7.prog07.py

在上面的代码(清单 8-7 )中,在if语句中,只有当进程的等级为 0 时,我们才给数据分配一个字典。bcast()向所有进程广播数据。

运行程序。

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog07.py

输出如下所示:

Rank: 0, Data: 1, 2, 3
Rank: 1, Data: 1, 2, 3
Rank: 2, Data: 1, 2, 3
Rank: 3, Data: 1, 2, 3

数据分散

在广播中,我们向所有进程发送相同的数据。在分散中,我们将不同的数据块发送给所有进程。例如,我们有一个包含四个项目的列表。在广播中,我们将所有这四个项目发送给所有进程,而在分散中,我们将列表中的项目发送给进程,这样每个进程都从列表中接收一个项目。下面的程序(清单 8-8 )演示了这一点。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

if rank == 0:
    data = [x for x in range(0,size)]
    sys.stdout.write("We will be scattering: ")
    sys.stdout.write(" ".join(str(x) for x in data))
    sys.stdout.write("\n")
else:
    data = None

data = comm.scatter(data, root=0)
sys.stdout.write("Rank: %d has data: %d\n" % (rank, data))

Listing 8-8.prog08.py

在上面的代码(清单 8-8 )中,我们创建了一个名为 data 的列表,它的元素数量等于集群中的进程数。scatter()用于将数据分散到所有进程中。

运行代码。

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog08.py

以下是输出:

Rank: 1 has data: 1
We will be scattering: 0 1 2 3
Rank: 0 has data: 0
Rank: 2 has data: 2
Rank: 3 has data: 3

正如我们所看到的,每个进程从列表中接收一个项目。scatter()的限制是我们分散的数据列表的大小不能超过进程的数量。

数据采集

收集数据的想法与分散相反。主进程收集由其他进程处理的所有数据。

下面的程序(列表 8-9 )演示了gather()方法。

from mpi4py import MPI
import sys

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

if rank == 0:
    data = [x for x in range(0,size)]
    sys.stdout.write("We will be scattering: ")
    sys.stdout.write(" ".join(str(x) for x in data))
    sys.stdout.write("\n")
else:
    data = None

data = comm.scatter(data, root=0)
sys.stdout.write("Rank: %d has data: %d\n" % (rank, data))
data *= data

newData = comm.gather(data, root=0)

if rank == 0:
    sys.stdout.write("We have gathered: ")
    sys.stdout.write(" ".join(str(x) for x in newData))
    sys.stdout.write("\n")

Listing 8-9.prog09.py

在上面的程序(列表 8-9 )中,主进程分散数字列表。所有 MPI 进程都从列表中接收一个元素(列表的大小等于 MPI 进程的数量)。每个进程执行它接收的元素的一个操作。在我们的例子中,它是数字平方的计算。然而,在现实世界的超级计算中,操作可能相当复杂。

一旦操作完成,主进程将所有已处理的元素收集到一个新列表中。

运行代码。

mpirun -hostfile myhostfile -np 4 python3 ∼/book/code/chapter08/prog09.py

输出如下所示:

Rank: 1 has data: 1
Rank: 3 has data: 3
We will be scattering: 0 1 2 3
Rank: 0 has data: 0
We have gathered: 0 1 4 9
Rank: 2 has data: 2

结论

在本章中,我们介绍了 Python 的 MPI4PY 库。我们用 MPI4PY 学习并实验了并行编程中各种有趣的概念。在下一章中,我们将开始使用 Python 3 中带有 Raspberry Pi 的 SciPy 栈。

九、SciPy 栈和符号编程简介

在上一章中,我们学习了如何使用我们为 MPI4PY 和 Python 3 并行编程而构建的 Raspberry Pi 集群。在本章中,我们将介绍 SciPy 栈并将其安装在 Pi 上。我们还将开始使用 SymPy 进行符号编程。

科学 Python 栈

SciPy(Scientific Python 的缩写)是一个用 Python 编写的科学和技术计算的开源库。

SciPy 具有用于数值运算、常微分方程解算器、快速傅立叶变换、最优化、线性代数、积分、插值、信号处理和图像处理的模块。SciPy 被全世界的科学、数学和工程社区广泛使用。还有许多其他库使用 SciPy 和 NumPy 的核心模块进行操作。OpenCV 和 SciKit 是其他主要库使用 NumPy 和/或 SciPy 的最好例子。

SciPy 栈包含以下组件:

  • NumPy 是一个用于数值计算的库。它提供了数字和科学计算所需的所有基本数据类型。
  • SciPy 库有许多用于科学编程的模块。
  • Matplotlib 用于数据可视化。
  • SymPy 用于符号编程。
  • IPython 是一个高级 Python 解释器,增加了一些特性。
  • Pandas 用于数据分析。
  • Nose 用于自动化测试用例。

下图(图 9-1 )总结了 Python SciPy 栈在科学计算世界中的作用。

A447085_1_En_9_Fig1_HTML.gif

图 9-1。

The components of the SciPy stack

在本书中,我们将学习 NymPy、SciPy 库、Matplotlib 和 SymPy。

SciPy 栈的安装

在 Raspberry Pi 上安装 SciPy 栈的最佳方式是使用apt-getpip

首先,使用以下命令升级pip:

sudo python3 -m pip install --upgrade pip

使用以下命令安装 SymPy:

sudo pip3 install sympy

SciPy 栈的其余组件可以使用apt-get实用程序方便地安装,如下所示:

sudo apt-get install python3-matplotlib -y
sudo apt-get install python3-scipy -y
sudo apt-get install python3-numpy -y

这将安装所需的 SciPy 栈组件。

-很好

SymPy 网站上说:

SymPy is a Python library of symbolic mathematics. Its goal is to become a fully functional computer algebra system (CAS), while keeping the code as simple as possible so as to be easy to understand and expand. SymPy is written entirely in Python.

SymPy 是 BSD 许可的,是免费的。它完全是用 Python 编写的。它依赖于mpmath并且本质上是轻量级的,因为没有其他依赖项。

入门指南

让我们从 SymPy 开始。运行以下命令创建并导航到该章节的目录:

cd ∼
cd book
cd code
mkdir chapter09
cd chapter09

我们将把这一章的代码保存在目录chapter09中,并将在本书的其余部分继续这一做法,即按章创建目录来组织代码。

我假设您有一些数学和微积分的基础知识,所以我在解释代码时不必解释这些基础知识。让我们看一个符号计算概念的简单例子(列表 9-1 )。

import math
import sympy
print(math.sqrt(9))
print(math.sqrt(8))
print(sympy.sqrt(9))
print(sympy.sqrt(8))
Listing 9-1.prog01.py

运行上面的代码(清单 9-1 )。以下是输出:

3.0
2.8284271247461903
3
2*sqrt(2)

在上面的输出中,我们可以看到math.sqrt()方法直接产生数字格式的结果,而sympy.sqrt()方法只在结果是整数的情况下才产生数字格式的结果。它不会产生一个小数值,而是保持sqrt(2)不变。这样,我们就可以象征性地计算很多数学方程。有了符号数学概念的这些知识,让我们用 Python 3 更深入地研究 SymPy。

标志

我们来研究一下符号的概念。符号类似于数学中的变量。我们可以用它们来计算方程式和表达式。它们也可以用来解方程。sympy.symbols()方法将一个字符串转换成符号变量,如下所示(清单 9-2 ):

from sympy import *

x = symbols('x')
sigma, y = symbols('sigma y')
print(x, y, sigma)

Listing 9-2.prog02.py

输出如下所示:

x y sigma

上面的代码(清单 9-2 )展示了symbols()方法可以接受一个字符串作为参数,其中的标记由空格分隔。

让我们再看一个例子(清单 9-3 ),它演示了用符号对表达式求值。

from sympy import *

x = symbols('x')
expr = x + 1
print(expr.subs(x, 3))

Listing 9-3.prog03.py

这将在表达式中用 3 代替x,并对其求值。代码(清单 9-3 )产生4作为输出。

我们可以替换多个符号如下(列表 9-4 ):

from sympy import *

x, y = symbols('x y')
expr = x + y
print(expr.subs({x:3, y:2}))

Listing 9-4.prog04.py

这里,我们一次替换多个符号。运行代码并检查输出。

将字符串转换为 SymPy 表达式

我们可以将字符串转换成 SymPy 表达式。就像在 Python 中一样,我们可以对指数使用**操作符。下面的代码(清单 9-5 )显示了这一点:

from sympy import *

str = "x**3 + 4*y - 1/5"
expr = sympify(str)
print(expr)

Listing 9-5.prog05.py

方法将一个字符串转换成一个 SymPy 表达式。

我们还可以使用evalf()方法将表达式求值为浮点数。它的默认精度是小数点后 15 位数字;但是,我们可以将精度作为参数传递。下面(列表 9-6 显示了evalf()方法的用例示例:

from sympy import *

expr = sqrt(10)
print(expr.evalf())

print(pi.evalf(20))

x = symbols('x')
expr = sin(2*x)
print(expr.evalf(subs={x: 2.4}))
The output is as follows,
3.16227766016838
3.1415926535897932385
-0.996164608835841
Printing in SymPy

Listing 9-6.prog06.py

Sympy 的打印功能

SymPy 有很多打印机。在任何环境中,在命令提示符下使用init_session()方法都会启动一个交互式会话。下面是一个交互式会话的示例。我在控制台中输入的命令以粗体突出显示。

pi@pi001:∼/book/code/chapter09 $ python3

Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from sympy import *

>>> init_session()

Python console for SymPy 1.0 (Python 3.4.2-32-bit) (ground types: python)

这些命令被执行:

>>> from __future__ import division
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = symbols('f g h', cls=Function)
>>> init_printing()

Documentation can be found at http://docs.sympy.org/1.0/

>>> Integral(sqrt(1/x), x)

⌠
⎮        ___
⎮      ╱ 1
⎮    ╱   ─  dx
⎮╲╱     x
⌡
>>> sqrt(x)

√x
>>> (sqrt(x) + sqrt(y))**2

         2
(√x + √y)
>>>

这就是我们如何在交互式控制台中以良好的格式打印表达式。

症状的简化

我们可以使用simplify()方法尽可能简化数学表达式。这下涵盖了大量的表达式。以下(清单 9-8 )就是一个例子:

from sympy import *
x = symbols('x')
print(simplify(sin(x)**2 + cos(x)**2))
print(simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1)))
print(simplify(gamma(x)/gamma(x - 2)))
Listing 9-8.prog08.py

简化的输出如下:

1
x - 1
(x - 2)*(x - 1)

SymPy 中有更多的简化方法。我们可以使用expand()来展开一个多项式表达式,如下所示(列表 9-9 )。

from sympy import *
x, y = symbols('x y')
print(expand((x + y)**2))
print(expand((x + 3)*(y + 5)))
Listing 9-9.prog09.py

以下是扩展后的输出:

x**2 + 2*x*y + y**2
x*y + 5*x + 3*y + 15

类似地,我们可以使用factor()方法(列表 9-10 )来寻找多项式的不可约因子。

from sympy import *
x = symbols('x')
print(factor(x**3 - x**2 + x))
Listing 9-10.prog10.py

输出如下所示:

x*(x**2 - x + 1)

结石

我们甚至可以用 SymPy 来做微积分。我们可以使用diff()方法计算导数如下(列表 9-11 ):

from sympy import *
x = symbols('x')
print(diff(x**3 - x**2 + x, x))
print(diff(x**5, x))
print(diff(sin(x), x))
Listing 9-11.prog11.py

输出是:

3*x**2 - 2*x + 1
5*x**4
cos(x)

我们还可以找到如下表达式的高阶导数(列表 9-12 ):

from sympy import *
x = symbols('x')
print(diff(10*x**4, x, x, x))
print(diff(10*x**4, x, 3))
Listing 9-12.prog12.py

输出如下所示:

240*x
240*x

我们也可以使用integrate()方法计算带有 SymPy 的积分。下面的代码(清单 9-13 )演示了这一点:

from sympy import *
x = symbols('x')
print(integrate(sin(x), x))
Listing 9-13.prog13.py

输出如下所示:

-cos(x)

我们还可以集成如下限制(列表 9-14 ):

from sympy import *
x = symbols('x')
print(integrate(exp(-x), (x, 0, oo)))
Listing 9-14.prog14.py

这里,我们对-x的指数从零到无穷大进行积分(用oo表示)。运行这个并检查输出。我们也可以计算多重极限的多重积分如下(列表 9-15 ):

from sympy import *
x, y = symbols('x y')
print(integrate(exp(-x)*exp(-y), (x, 0, oo), (y, 0, oo)))
Listing 9-15.prog15.py

运行代码(列表 9-15 )并通过手动执行集成来验证输出。

Note

SymPy 真是个大话题。这不可能在一章中完全涵盖。我建议读者在 http://docs.sympy.orgwww.sympy.org 网站上多探索。

结论

在这一章中,我们从 SymPy 开始,学习了如何在 Python 中执行符号计算。在下一章,我们将从 NumPy 和 Matplotlib 开始。

十、NumPy 简介

在上一章中,我们学习了如何安装 SciPy 栈,以及如何在 Python 3 中使用 SymPy 进行符号计算。在这一章中,我们将被介绍到 NumPy 库,我们将学习 NumPy 的基础知识。我们还将学习使用 Matplotlib 绘制和可视化数据的基础知识。因此,让我们通过学习 NumPy 的基础知识,开始进入科学计算世界的激动人心的旅程。

NumPy 基础

NumPy 是数字(al) Python 的缩写。它的网站( http://www.numpy.org )上说:

努比切望 python(python 语言)

进行科学计算的基础包

其特点如下:

它有一个强大的自定义 N 维数组对象,可以高效方便地表示数据。

它拥有与其他用于科学编程的编程语言(如 C/C++和 FORTRAN)集成的工具。

它用于数学运算,如线性代数、矩阵运算、图像处理和信号处理。

朱皮特

到目前为止,我们一直将代码保存在.py文件中,并用 Python 3 解释器运行它。在这一章中,我们将使用一个叫做 Jupyter 的工具,这是一个基于网络的高级工具,用于在编程语言 Julia、Python 和 R .

Ju lia + pyt hon + r = Jupyter

它将 Python 3(或任何其他支持的语言,如 R 和 Julia)代码和结果保存在一种叫做笔记本的交互格式中。Jupyter 使用 Python 2 和 Python 3 的 IPython 内核。IPython 是 Python 的高级交互式 shell,具有可视化功能。Jupyter 项目是 IPython 的副产品。

Jupyter 和 IPython 具有以下特性:

  • 基于终端和 Qt 的交互式 shells
  • 基于浏览器的笔记本,支持代码和交互式可视化
  • 支持并行计算

可以使用以下命令轻松安装它:

sudo pip3 install --upgrade pip
sudo pip3 install jupyter

这将安装 Jupyter 及其对 Raspberry Pi 的所有依赖。

jupyter 笔记型电脑

Jupyter Notebook 是由 Jupyter Notebook 应用程序生成的文档,其中包含 Python 代码和丰富的文本元素,如段落、等式、图形、链接和交互式可视化。笔记本有人类可读组件和机器可读(可执行)组件。

让我们现在就开始使用 NumPy 和 Jupyter 笔记本吧。打开 lxterminal 并运行以下命令序列:

cd ∼
cd book
cd code
mkdir chapter10
cd chapter10

这将创建并导航到对应于当前章节chapter10的目录。

现在,这是我们的笔记本启动文件夹,让我们从这里使用以下命令启动笔记本:

jupyter notebook

它将启动 Jupyter 笔记本应用程序,并打开一个浏览器窗口(在 Raspbian 的最新版本中为 Chromium 浏览器)。

下面(图 10-1 )是 Jupyter 笔记本启动时的控制台截图:

A447085_1_En_10_Fig1_HTML.jpg

图 10-1。

Jupyter Notebook App console

下面(图 10-2 )是 Chromium 浏览器窗口标签运行笔记本 app 的截图:

A447085_1_En_10_Fig2_HTML.jpg

图 10-2。

Jupyter Notebook App running in Chromium

在浏览器窗口的右上方,单击 New,然后在随后的下拉菜单中选择 Python 3。见下图(图 10-3 )截图:

A447085_1_En_10_Fig3_HTML.jpg

图 10-3。

New Python 3 notebook

它将在同一浏览器窗口中打开一个新的笔记本标签(图 10-4 )。

A447085_1_En_10_Fig4_HTML.jpg

图 10-4。

Python 3 notebook tab

将 Jupyter 笔记本的名称改为Chapter10_Practice,如下图截图所示(图 10-5 )。

A447085_1_En_10_Fig5_HTML.jpg

图 10-5。

Renaming the notebook

笔记本应用程序将显示一个新笔记本的实例,其名称已更新,状态为“正在运行”,如下图截图所示(图 10-6 )。

A447085_1_En_10_Fig6_HTML.jpg

图 10-6。

Notebook running

现在,如果你查看Chapter10目录,你会发现一个与笔记本对应的文件Chapter10_Practice.ipynb

在窗口顶部的菜单栏中,有您在任何其他 IDE 中都有的选项,例如保存、复制、粘贴和运行。

在第一个单元格中键入import numpy as np,然后单击 Run 按钮。该控件将自动创建下一个文本单元格,并将光标置于其上,如下所示(图 10-7 )。

A447085_1_En_10_Fig7_HTML.jpg

图 10-7。

Working with Python 3 code

我们刚刚将 NumPy 导入到我们的笔记本中,因此我们不必再次导入。此外,我们也可以编辑笔记本的前一个单元格。在执行类型中,如果解释器突出了语法中的错误,我们可以通过编辑任何单元格来修复它。随着科学计算的发展,我们将对 Jupyter 有更多的了解。

N 维数组(ndarray)

NumPy 最强大的构造是 N 维数组(ndarray)。ndarray为多维同质数据提供通用容器。同构意味着ndarray中的数据项是相同的数据类型。让我们看一个 Python 中各种ndarray类型变量的例子。在笔记本中键入以下代码:

x = np.array([1, 2, 3], np.int16)
y = np.array([[0, 1, 2], [3, 4, 5]], np.int32)
z = np.array([[[0, 1, 2], [2, 3, 4], [4, 5, 6]],[[1, 1, 1], [0, 0, 0], [1, 1, 1]]], np.float16)

恭喜,我们刚刚创建了一维、二维和三维的ndarray对象。我们可以通过在 Jupyter notebook 中运行以下代码来验证这一点:

print(x.shape)
print(y.shape)
print(z.shape)

输出是:

(3,)
(2, 3)
(3, 3, 3)

NumPy 中的ndarray的索引方案与 C 语言中的相同,即第一个元素从 0 开始索引。下面一行打印了一个ndarray中第二行第三列的元素值。

我们可以按如下方式对数组进行切片:

print(z[:,1])

以下是输出:

[[ 2\.  3\.  4.]
 [ 0\.  0\.  0.]]

n 数组属性

以下是对ndarray重要属性的演示:

print(z.shape)
print(z.ndim)
print(z.size)
print(z.itemsize)
print(z.nbytes)
print(z.dtype)

以下是输出:

(2, 3, 3)
3
18
2
36
float16

下面是正在发生的事情:

  • ndarray.shape返回数组维数的元组。
  • ndarray.ndim返回数组维数。
  • ndarray.size返回数组中元素的个数。
  • ndarray.itemsize以字节为单位返回一个数组元素的长度。
  • ndarray.nbytes返回数组元素消耗的总字节数。它是属性ndarray.sizendarray.itemsize相乘的结果。
  • ndarray.dtype返回数组中数据项的数据类型。

数据类型

数据类型用于表示数据的类型和大小。例如,int16表示 16 位有符号整数,uint8表示 8 位无符号整数。NumPy 支持广泛的数据类型。有关数据类型的更多详细信息,请浏览数据类型对象网页( https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html )。

Note

要查看 NumPy 支持的各种数据类型的详细列表,请访问 https://docs.scipy.org/doc/numpy/user/basics.types.html

具备了 NumPy 中的ndarray构造的基础,我们将在下一节开始数组创建例程。

数组创建例程

让我们用内置数组创建例程来创建ndarrays。我们还将使用 matplotlib 来可视化创建的数组。我们将使用 matplotlib 在需要时可视化数据。这本书的最后一章是专门介绍 matplotlib 的,在这里我们会学到更多的内容。

创建ndarrays的第一种方法是ones()。您一定已经猜到了,它创建了所有值都为 1 的数组。在笔记本中运行以下语句,查看该方法的演示:

a = np.ones(5)
print(a)
b = np.ones((3,3))
print(b)
c = np.ones((5, 5), dtype=np.int16)
print(c)

类似地,我们有生成多维零数组的np.zeros()方法。只需通过向方法np.zeros()传递相同的一组参数来运行上面的例子。np.eye()方法用于创建对角矩阵。我们也可以指定对角线的指数。在笔记本中运行以下示例进行演示:

a = np.eye(5, dtype=np.int16)
print(a)
b = np.eye(5, k=1)
print(b)
c = np.eye(5, k=-1)
print(c)

np.arange()返回指定范围内等距数字的列表。尝试笔记本中的以下示例:

a = np.arange(5)
print(a)
b = np.arange(3, 25, 2)
print(b)

输出如下所示:

[0 1 2 3 4]
[ 3  5  7  9 11 13 15 17 19 21 23]

现在让我们用 matplotlib 找点乐子吧。在笔记本中运行以下示例:

import matplotlib.pyplot as plt
x = np.arange(10)
y = np.arange(10)
print(x)
print(y)
plt.plot(x, y, 'o')
plt.show()

在上面的例子中,我们用图形表示了ndarrays。文本结果如下:

[0 1 2 3 4 5 6 7 8 9]
[0 1 2 3 4 5 6 7 8 9]

图形输出(图 10-8 )如下:

A447085_1_En_10_Fig8_HTML.jpg

图 10-8。

Graphical Output of arange()

通过语句import matplotlib.pyplot as plt,我们正在导入myplotlib库的 pyplot 模块。plt.plot()准备显示的标绘图,plt.show()在屏幕上显示。

我们有一个类似的方法,np.linspace(),来生成一个线性数组。linspace()arange()的主要区别在于linspace()接受要生成的样本数作为参数,而不是步长。下面的代码片段展示了linspace()生成数据的方式:

N = 8
y = np.zeros(N)
x1 = np.linspace(0, 10, N)
x2 = np.linspace(0, 10, N)
plt.plot(x1, y - 0.5, 'o')
plt.plot(x2, y + 0.5, 'o')
plt.ylim([-1, 1])
plt.show()

plt.ylim()指定图形上 Y 坐标的极限。以下(图 10-9 )为输出:

A447085_1_En_10_Fig9_HTML.jpg

图 10-9。

linspace() graph

类似地,我们有logspace()方法,它生成对数标度的数组值。下面的代码演示了:

N = 64
x = np.linspace(1, N, N)
y = np.logspace(0.1, 1, N)
plt.plot(x, y, 'o')
plt.show()

运行笔记本中的语句,它会生成以下(图 10-10 )输出:

A447085_1_En_10_Fig10_HTML.jpg

图 10-10。

linspace() versus logspace() graph

矩阵和线性代数

NumPy 有创建矩阵的例程。我们来看几个例子。np.matrix()将给定数据解释为矩阵。以下是一些例子:

a = np.matrix('1 2; 3 4')
b = np.matrix([[1, 2], [3, 4]])
print(a)
print(b)

运行笔记本中的代码,您会看到两个示例都返回了矩阵,即使输入的格式不同。

[[1 2]
 [3 4]]
[[1 2]
 [3 4]]

从数组或序列构建一个矩阵。

A = np.mat('1 1; 1 1')
B = np.mat('2 2; 2 2')
C = np.mat('3 4; 5 6')
D = np.mat('7 8; 9 0')
a = np.bmat([[A, B], [C, D]])
print(a)

上面的代码返回一个由所有序列组合而成的矩阵。运行笔记本中的代码,查看如下输出:

[[1 1 2 2]
 [1 1 2 2]
 [3 4 7 8]
 [5 6 9 0]]

np.matlib.zeros()np.matlib.ones()分别返回 0 和 1 的矩阵。np.matlib.eye()返回一个对角矩阵。np.matlib.identity()返回给定大小的正方形单位矩阵。下面的代码示例演示了这些方法:

from numpy.matlib import *
a = zeros((3, 3))
print(a)
b = ones((3, 3))
print(b)
c = eye(3)
print(c)
d = eye(5, k=1)
print(d)
e = eye(5, k=-1)
print(e)
f = identity(4)
print(f)

rand()randn()方法返回带有随机数的矩阵。

a = rand((3, 3))
b = randn((4, 4))
print(a)
print(b)

我们来学习几个与线性代数(矩阵运算)相关的方法。我们有dot()方法,它计算两个数组的点积,而vdot()计算两个向量的点积。inner()计算两个数组的内积。outer()计算两个向量的外积。下面的代码示例演示了所有这些方法:

a = [[1, 0], [0, 1]]
b = [[4, 1], [2, 2]]
print(np.dot(a, b))
print(np.vdot(a, b))
print(np.inner(a, b))
print(np.outer(a, b))

输出如下所示:

[[4 1]
 [2 2]]
6
[[4 2]
 [1 2]]
[[4 1 2 2]
 [0 0 0 0]
 [0 0 0 0]
 [4 1 2 2]]

三角测量法

让我们把三角方法形象化。我们将使用 matplotlib 可视化sin()cos()tan()sinh()cosh()方法。下面的示例演示了这些方法的用法:

x = np.linspace(-np.pi, np.pi, 201)
plt.plot(x, np.sin(x))
plt.xlabel('Angle in radians')
plt.ylabel('sin(x)')
plt.show()

输出(图 10-11 )如下:

A447085_1_En_10_Fig11_HTML.jpg

图 10-11。

Graph for sin(x)

以下代码示例演示了cos()的用法:

x = np.linspace(-np.pi, 2*np.pi, 401)
plt.plot(x, np.cos(x))
plt.xlabel('Angle in radians')
plt.ylabel('cos(x)')
plt.show()

以下(图 10-12 )为输出:

A447085_1_En_10_Fig12_HTML.jpg

图 10-12。

graph for cos(x)

让我们继续讨论双曲余弦和正弦波,如下所示:

x = np.linspace(-5, 5, 2000)
plt.plot(x, np.cosh(x))
plt.show()
plt.plot(x, np.sinh(x))
plt.show()

以下(图 10-13 )是cosh()的输出:

A447085_1_En_10_Fig13_HTML.jpg

图 10-13。

Graph of cosh(x)

下面(图 10-14 )是sinh(x)的曲线图:

A447085_1_En_10_Fig14_HTML.jpg

图 10-14。

Graph of sinh(x)

随机数和统计

rand()方法生成给定维数的随机矩阵。randn()方法使用从正态分布中抽取的数字生成一个矩阵。randint()在给定的界限内生成一个数,不包括这个界限。random_integers()生成包含给定限值的随机整数。下面的代码演示了上述方法的前三种:

import numpy as np
a = np.random.rand(3, 3)
b = np.random.randn(3, 3)
c = np.random.randint(4, 15)
print(a)
print(b)
print(c)

我们可以计算 a ndarray的中值、平均值、均值、标准差和方差,如下所示:

a = np.array([[10, 7, 4], [1, 2, 3], [1, 1, 1]])
print(median(a))
print(average(a))
print(mean(a))
print(std(a))
print(var(a))

傅立叶变换

NumPy 有一个用于基本信号处理的模块。fft模块具有fft()方法,用于计算一维离散傅立叶变换,如下所示:

t = np.arange(256)
sp = np.fft.fft(np.sin(t))
freq = np.fft.fftfreq(t.shape[-1])
plt.plot(freq, sp.real, freq, sp.imag)
plt.show()

输出(图 10-15 )如下:

A447085_1_En_10_Fig15_HTML.jpg

图 10-15。

Fast Fourier Transform

fft2()fftn()分别用于计算二维和 n 维离散傅立叶变换。试着为这些写代码。

结论

在本章中,我们从 NumPy、matplotlib 和 Jupyter 开始,并学习了如何在 Python 中执行数值计算和基本的可视化。在下一章,我们将从 SciPy 库开始。

十一、SciPy 简介

在上一章中,我们学习了如何使用 NumPy 进行数值计算。我们还学习了如何方便地使用 Jupyter,以及如何使用 matplotlib 进行可视化。在本章中,我们将介绍 SciPy 库。然而,我们与 NumPy 和 matplotlib 的旅程还远未结束。在本书的剩余部分,我们将学习 NumPy 和 matplotlib 的新功能。因此,让我们和 SciPy 一起踏上科学计算的激动人心的旅程。

SciPy 中的科学和数学常数

在我们开始之前,让我们完成为本章创建一个新目录的仪式。请为本章创建一个目录chapter 11

cd ∼
cd book
cd code
mkdir chapter11
cd chapter11

现在,使用以下命令启动 Jupyter 笔记本应用程序:

jupyter notebook

打开一个新笔记本,重命名为Chapter11_Practice。这个笔记本将保存本章的代码。

SciPy 库有一个名为scipy.constants的模块,其中有许多重要的数学和科学常数的值。让我们试几个。在笔记本中运行以下代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.constants import *
print("Pi = " + str(pi))
print("The golden ratio = " + str(golden))
print("The speed of light = " + str(c))
print("The Planck constant = " + str(h))
print("The standard acceleration of gravity = " + str(g))
print("The Universal constant of gravity = " + str(G))

输出如下所示:

Pi = 3.141592653589793
The golden ratio = 1.618033988749895
The speed of light = 299792458.0
The Planck constant = 6.62606957e-34,
The standard acceleration of gravity = 9.80665
The Universal constant of gravity = 6.67384e-11

请注意,SciPy 常量不包括度量单位,仅包括常量的数值。这些在数值计算中非常有用。

Note

还有更多这样的常数。请访问 https://docs.scipy.org/doc/scipy/reference/constants.html 查看更多。

线性代数

现在我们将学习几个与线性代数相关的方法。让我们从矩阵的逆矩阵开始:

import numpy as np
from scipy import linalg
a = np.array([[1, 4], [9, 16]])
b = linalg.inv(a)
print(b)

以下是输出:

[[-0.8   0.2 ]
 [ 0.45 -0.05]]

我们也可以求解矩阵方程ax = b如下:

a = np.array([[3, 2, 0], [1, -1, 0], [0, 5, 1]])
b = np.array([2, 4, -1])
from scipy import linalg
x = linalg.solve(a, b)
print(x)
print(np.dot(a, x))

以下是输出:

[ 2\. -2\.  9.]
[ 2\.  4\. -1.]

我们可以如下计算矩阵的行列式:

a = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(linalg.det(a))

在笔记本中运行上面的代码,自己看看结果。

我们还可以计算矩阵的范数,如下所示:

a = np.arange(16).reshape((4, 4))
print(linalg.norm(a))
print(linalg.norm(a, np.inf))
print(linalg.norm(a, 1))
print(linalg.norm(a, -1))

以下是结果:

35.2136337233
54
36
24

我们也可以计算 QR 和 RQ 分解如下:

from numpy import random
from scipy import linalg
a = random.randn(3, 3)
q, r = linalg.qr(a)
print(a)
print(q)
print(r)
r, q = linalg.rq(a)
print(r)
print(q)

综合

SciPy 有用于各种集成操作的integrate模块,所以让我们看看它的一些方法。第一个是quad()。它接受要积分的函数以及积分的极限作为参数,然后返回数值和近似误差。以下是几个例子:

from scipy import integrate
f1 = lambda x: x**4
print(integrate.quad(f1, 0, 3))
import numpy as np
f2 = lambda x: np.exp(-x)
print(integrate.quad(f2, 0, np.inf))

以下是输出结果:

(48.599999999999994, 5.39568389967826e-13)
(1.0000000000000002, 5.842606742906004e-11)

trapz()使用梯形法则沿给定轴积分:

print(integrate.trapz([1, 2, 3, 4, 5]))

以下是输出:

12.0

让我们看一个使用梯形法则的累积积分的例子。

import matplotlib.pyplot as plt
x = np.linspace(-2, 2, num=30)
y = x
y_int = integrate.cumtrapz(y, x, initial=0)
plt.plot(x, y_int, 'ro', x, y[0] + 0.5 * x**2, 'b-')
plt.show()

以下(图 11-1 )为输出:

A447085_1_En_11_Fig1_HTML.jpg

图 11-1。

Cumulative integration using the trapezoidal rule

插入文字

这个模块有插值的方法。让我们借助 matplotlib 的图形表示来研究其中的一些。interp1d()用于一维插值,如下所示。

from scipy import interpolate
x = np.arange(0, 15)
y = np.exp(x/3.0)
f = interpolate.interp1d(x, y)
xnew = np.arange(0, 14, 0.1)
ynew = f(xnew)
plt.plot(x, y, 'o', xnew, ynew, '-')
plt.show()

结果如下(图 11-2 ):

A447085_1_En_11_Fig2_HTML.jpg

图 11-2。

1-D interpolation

interp1d()适用于y=f(x)类型的功能。类似地,interp2d()处理z=f(x, y)类型的函数。它用于二维插值。

x = np.arange(-5.01, 5.01, 0.25)
y = np.arange(-5.01, 5.01, 0.25)
xx, yy = np.meshgrid(x, y)
z = np.sin(xx**3 + yy**3)
f = interpolate.interp2d(x, y, z, kind='cubic')
xnew = np.arange(-5.01, 5.01, 1e-2)
ynew = np.arange(-5.01, 5.01, 1e-2)
znew = f(xnew, ynew)
plt.plot(x, z[0, :], 'ro-', xnew, znew[0, :], 'b-')
plt.show()

结果如下(图 11-3 ):

A447085_1_En_11_Fig3_HTML.jpg

图 11-3。

2-D interpolation

接下来,我们要学习splev()splprep()splrep()splev()方法用于计算 B 样条或其导数。我们将把这种方法与用于表示 B 样条的splprep()splrep(),一起使用。

splrep()用于表示一维曲线,如下所示:

from scipy.interpolate import splev, splrep
x = np.linspace(-10, 10, 50)
y = np.sinh(x)
spl = splrep(x, y)
x2 = np.linspace(-10, 10, 50)
y2 = splev(x2, spl)
plt.plot(x, y, 'o', x2, y2)
plt.show()

结果如下(图 11-4 ):

A447085_1_En_11_Fig4_HTML.jpg

图 11-4。

Representation of a 1-D curve

splprep()用于表示一条 N 维曲线。

from scipy.interpolate import splprep
theta = np.linspace(0, 2*np.pi, 80)
r = 0.5 + np.cosh(theta)
x = r * np.cos(theta)
y = r * np.sin(theta)
tck, u = splprep([x, y], s=0)
new_points = splev(u, tck)
plt.plot(x, y, 'ro')
plt.plot(new_points[0], new_points[1], 'r-')
plt.show()

下面(图 11-5 )是结果。

A447085_1_En_11_Fig5_HTML.jpg

图 11-5。

Representation of an N-D curve

结论

在本章中,我们介绍了 SciPy 库中几个重要且常用的模块。接下来的两章将着重向读者介绍信号和图像处理的专业科学领域。在下一章,我们将学习几个与信号处理相关的模块。

十二、SciPy 信号处理

在上一章中,我们学习了如何用 SciPy 进行科学计算。我们了解了 SciPy 库的几个模块。在本章中,我们将探索一个重要的科学领域,即信号处理,并且我们将学习SciPy.signal模块中的方法。让我们从 SciPy 中的信号处理开始吧。这将是一个简短的章节,只有几个代码示例,让您对信号处理领域的一些基础知识有所了解。

波形

让我们从波形发生器功能开始。在∼/book/code目录下新建一个目录chapter 12。运行以下命令启动 Jupyter 笔记本应用程序:

jupyter notebook

将当前笔记本重命名为Chapter12_Practice。和前面的章节一样,在同一个笔记本中运行本章的所有代码。先导入 NumPy 和 matplotlib。

import numpy as np
import matplotlib.pyplot as plt

我们要研究的第一个例子是锯齿波发生器功能。

from scipy import signal
t = np.linspace(0, 2, 5000)
plt.plot(t, signal.sawtooth(2 * np.pi * 4 * t))
plt.show()

该函数接受信号的时序和宽度,并生成三角形或锯齿形连续信号。以下(图 12-1 )为输出,

A447085_1_En_12_Fig1_HTML.jpg

图 12-1。

Sawtooth wave signal

让我们来看一个方波发生器,它接受时间序列和占空比作为输入。

t = np.linspace(0, 1, 400)
plt.plot(t, signal.square(2 * np.pi * 4 * t))
plt.ylim(-2, 2)
plt.title('Square Wave')
plt.show()

输出如下所示:

A447085_1_En_12_Fig2_HTML.jpg

图 12-2。

Square wave signal

脉宽调制方波正弦波可以如下所示:

sig = np.sin(2 * np.pi * t)
pwm = signal.square(2 * np.pi * 30 * t, duty=(sig +1)/2)
plt.subplot(2, 1, 1)
plt.plot(t, sig)
plt.title('Sine Wave')
plt.subplot(2, 1, 2)
plt.plot(t, pwm)
plt.title('PWM')
plt.ylim(-1.5, 1.5)
plt.show()

输出(图 12-3 )如下:

A447085_1_En_12_Fig3_HTML.jpg

图 12-3。

Modulated wave

窗口功能

窗口函数是一种数学函数,在特定区间外为零。我们现在来看看三种不同的窗口函数。第一个是汉明窗函数。我们必须将输出窗口中的点数作为参数传递给所有函数。

window = signal.hamming(101)
plt.plot(window)
plt.title('Hamming Window Function')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.show()

输出(图 12-4 )如下:

A447085_1_En_12_Fig4_HTML.jpg

图 12-4。

Hamming window demo

汉宁窗函数如下:

window = signal.hanning(101)
plt.plot(window)
plt.title('Hanning Window Function')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.show()

输出(图 12-5 )如下:

A447085_1_En_12_Fig5_HTML.jpg

图 12-5。

Hanning window demo

凯泽窗函数如下:

window = signal.kaiser(51, beta=20)
plt.plot(window)
plt.title('Kaiser Window Function Beta = 20')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.show()

输出(图 12-6 )如下:

A447085_1_En_12_Fig6_HTML.jpg

图 12-6。

Kaiser window demo

墨西哥帽小波

通过将点数和振幅作为参数传递,我们可以使用 Ricker 函数生成一个墨西哥帽小波,如下所示:

plt.plot(signal.ricker(200, 6.0))
plt.show()

墨西哥帽小波是连续小波族中的一个特例。它用于过滤和平均光谱信号。输出如下所示:

A447085_1_En_12_Fig7_HTML.jpg

图 12-7。

Mexican hat wavelet

盘旋

我们可以用convolve()方法卷积两个 N 维数组,如下所示:

sig = np.repeat([0., 1., 0.], 100)
win = signal.hann(50)
filtered = signal.convolve(sig, win, mode='same') / sum(win)
plt.subplot(3, 1, 1)
plt.plot(sig)
plt.ylim(-0.2, 1.2)
plt.title('Original Pulse')
plt.subplot(3, 1, 2)
plt.plot(win)
plt.ylim(-0.2, 1.2)
plt.title('Filter Impulse Response')
plt.subplot(3, 1, 3)
plt.plot(filtered)
plt.ylim(-0.2, 1.2)
plt.title('Filtered Signal')
plt.show()

信号、窗口及其卷积如图 12-8 所示。两个信号的卷积提供了两个信号的组合以形成第三个信号。它是信号处理中一种非常重要的信号合并技术。如果信号代表图像或音频数据,我们可以基于卷积度获得改进的图像或音频。如果您还没有注意到,我们正在使用 NumPy 的repeat()方法,它把要重复的模式和重复次数作为参数。在本例中,我们生成一个样本大小为 300 的信号。

A447085_1_En_12_Fig8_HTML.jpg

图 12-8。

Convolution

结论

在这简短的一章中,我们介绍了 SciPy 的scipy.signal模块中的几个重要的方法类。在下一章,我们将探索图像处理领域。

十三、SciPy 图像处理

在上一章中,我们用 SciPy 研究了信号处理。我们研究了 SciPy 提供的几个主要函数类,合并在scipy.signal下。在这一章中,我们将学习一个 SciPy 包,scipy.misc,以及一些使用它进行图像处理的例子。

第一图像处理程序

scipy.misc模块用于基本的图像处理操作。创建一个名为Dataset的目录来存储我们将要使用的所有样本图像。

cd ∼/book
mkdir Dataset

样本图像数据集可以在本书的页面上的在线下载部分获得。同样,为图书代码创建一个目录chapter 13,如下所示:

cd ∼/book/code
mkdir chapter13
cd chapter13

让我们看一个读取和显示图像的基本例子(清单 13-1 )。

from scipy import misc

img = misc.imread('/home/pi/book/Dataset/4.2.01.tiff')

misc.imshow(img)

Listing 13-1.prog01.py

代码(清单 13-1 将从imread()方法提供的路径中读取一个图像,并且imshow()方法将使用xlib显示它。

scipy.misc有三个内置图像。这些可以如下使用(列表 13-2 ):

from scipy import misc

img1 = misc.face()
img2 = misc.lena()
img3 = misc.ascent()

misc.imshow(img1)
misc.imshow(img2)
misc.imshow(img3)

Listing 13-2.prog02.py

face()是浣熊的脸,lena()是标准测试图像,ascent()是灰度图像。

简单的图像处理

scipy.misc有三种简单操作的方法。scipy.imfilter()对图像应用各种滤镜。以下(清单 13-3 )是一个例子:

from scipy import misc

misc.imshow(misc.imfilter(misc.face(), 'edge_enhance_more'))

Listing 13-3.prog03.py

在上面的代码中(清单 13-3 ,我没有使用任何中间变量来存储图像。我通过传递给imshow()方法来直接显示它。方法imfilter()接受两个参数。

  • 第一个是要过滤的图像。
  • 第二个是要应用的预定义过滤器的类型。

过滤器类型的允许值为'blur', 'contour', 'detail', 'edge_enhance', 'edge_enhance_more', 'emboss', 'find_edges', 'smooth', 'smooth_more', 'sharpen'

我们可以将图像的大小调整为 50%,如下所示(清单 13-4 ):

from scipy import misc
misc.imshow(misc.imresize(misc.face(), 50))
Listing 13-4.prog04.py

我们也可以将图像旋转一定角度,如下所示(列表 13-5 ):

from scipy import misc
misc.imshow(misc.imrotate(misc.face(), 45))
Listing 13-5.prog05.py

用于图像处理的 NumPy 简介

让我们从使用 NumPy 库进行图像处理的基础开始。考虑以下(列表 13-6 )代码:

from scipy import misc

img = misc.face()

print(type(img))

Listing 13-6.prog06.py

上述程序(清单 13-6 )的输出如下:

<class 'numpy.ndarray'>

这意味着图像的数据类型是 NumPy 中的ndarray。我们需要理解一些重要的ndarray属性,这将帮助我们理解它所代表的图像的重要属性。

考虑下面的代码(清单 13-7 ):

from scipy import misc

img = misc.face()

print(img.dtype)
print(img.shape)
print(img.ndim)
print(img.size)

Listing 13-7.prog07.py

输出如下所示:

uint8
(768, 1024, 3)
3
2359296

让我们来了解一下其中每一项的含义。

  • dtype属性用于表示图像的元素的数据类型。在这种情况下,它是uint8,表示无符号的 8 位整数。这意味着它可以有 256 个不同的值。
  • shape表示图像的尺寸。在这种情况下,它是一个彩色图像。它的分辨率为 1024x768,有三个颜色通道,分别对应于红色、绿色和蓝色。每个像素的每个通道可以有 256 个可能值中的一个。因此,这些的组合可以为每个像素产生 256256256 种不同的颜色。您可以将彩色图像想象为三个二维平面的排列。灰度图像是灰度值的单一平面。
  • ndim代表尺寸。彩色图像具有三维,而灰度图像具有二维。
  • size代表数组中元素的总数。它可以通过乘以尺寸值来计算。在本例中,它是 76810243=2359296。

我们可以看到对应于单个像素的 RGB 值如下(列表 13-8 ):

from scipy import misc

img = misc.face()

print(img[10, 10]))

Listing 13-8.prog08.py

在上面的代码中(清单 13-8 ,我们正在访问位于(10,10)的像素值。输出为[172 169 188]

这些是 NumPy 用于图像处理的基础。在本章中,我们将在需要时学习更多关于 NumPy 的知识。

用于图像处理的 Matplotlib

我们使用了misc.imshow()方法来显示图像。虽然该方法对于简单的应用程序很有用,但它是原始的。我们需要使用更先进的科学应用框架。我们知道 matplotlib 就是为此服务的。这是一个用于 Python 的 MATLAB 风格的绘图和数据可视化库,我们在安装 SciPy 栈时安装了它。我们在前面的章节中也使用过它。在这一章和下一章,我们将使用它来显示图像。它是 SciPy 栈不可或缺的一部分。就像 NumPy 一样,matplotlib 对于一本书来说是一个太大的主题,值得再写一本书。我们将只使用 matplotlib 中的pyplot模块来满足我们的图像处理需求。让我们看一个简单的图像处理程序(列表 13-9 )如下:

import scipy.misc as misc
import matplotlib.pyplot as plt

img = misc.face()

plt.imshow(img)
plt.show()

Listing 13-9.prog09.py

在上面的代码中(清单 13-9 ,我们正在导入pyplot模块。imshow()方法用于将图像添加到绘图窗口。show()方法显示绘图窗口。输出(图 13-1 )如下:

A447085_1_En_13_Fig1_HTML.jpg

图 13-1。

pyplot imshow() demo for color image

我们还可以关闭轴(或标尺)并给图像添加一个标题,如下所示(清单 13-10 ):

import scipy.misc as misc
import matplotlib.pyplot as plt

img = misc.lena()

plt.imshow(img, cmap='gray')
plt.axis('off')
plt.title('face')
plt.show()

Listing 13-10.prog10.py

由于图像是灰度图像,我们必须在imshow()方法中选择一个gray色彩映射表,以便图像的色彩空间正确显示在绘图窗口中。axis('off')用于关闭斧子。title()方法用于指定图像的标题。输出(图 13-2 )如下:

A447085_1_En_13_Fig2_HTML.jpg

图 13-2。

Lena image with title and axis off

我们可以使用imshow()将多个图像推送到绘图窗口中的图像网格(参见清单 13-11 ),如下所示:

import scipy.misc as misc
import matplotlib.pyplot as plt

img1 = misc.face()
img2 = misc.ascent()
img3 = misc.lena()

titles = ['face', 'ascent', 'lena']
images = [img1, img2, img3]

plt.subplot(1, 3, 1)
plt.imshow(images[0])
plt.axis('off')
plt.title(titles[0])

plt.subplot(1, 3, 2)
plt.imshow(images[1], cmap='gray')
plt.axis('off')
plt.title(titles[1])

plt.subplot(1, 3, 3)
plt.imshow(images[2], cmap='gray')
plt.axis('off')
plt.title(titles[2])

plt.show()

Listing 13-11.prog11.py

imshow()之前我们已经用过subplot()的方法了。subplot()方法的前两个参数指定网格的尺寸,第三个参数指定图像在网格中的位置。网格中图像的位置编号从左上角开始。左上位置是第一个位置,下一个位置是第二个位置,依此类推。让我们看看输出(图 13-3 ):

A447085_1_En_13_Fig3_HTML.jpg

图 13-3。

Multiple image grid

图像通道

我们可以分离多通道图像的图像通道。代码(列表 13-12 )如下:

import scipy.misc as misc
import matplotlib.pyplot as plt

img = misc.face()

r = img[:, :, 0]
g = img[:, :, 1]
b = img[:, :, 2]

titles = ['face', 'Red', 'Green', 'Blue']
images = [img, r, g, b]

plt.subplot(2, 2, 1)
plt.imshow(images[0])
plt.axis('off')
plt.title(titles[0])

plt.subplot(2, 2, 2)
plt.imshow(images[1], cmap='gray')
plt.axis('off')
plt.title(titles[1])

plt.subplot(2, 2, 3)
plt.imshow(images[2], cmap='gray')
plt.axis('off')
plt.title(titles[2])

plt.subplot(2, 2, 4)
plt.imshow(images[3], cmap='gray')
plt.axis('off')
plt.title(titles[3])

plt.show()

Listing 13-12.Prog12.py

代码(清单 13-12 )的输出(图 13-4 )如下:

A447085_1_En_13_Fig4_HTML.jpg

图 13-4。

Separate image channels

我们可以使用np.dstack()方法合并所有通道来创建原始图像,如下所示(列表 13-13 ):

import scipy.misc as misc
import matplotlib.pyplot as plt
import numpy as np

img = misc.face()

r = img[:, :, 0]
g = img[:, :, 1]
b = img[:, :, 2]

output = np.dstack((r, g, b))

plt.imshow(output)
plt.axis('off')
plt.title('Combined')
plt.show()

Listing 13-13.prog13.py

运行上面的代码(清单 13-13 ,亲自看看np.dstack()是如何工作的。

Note

我写过一本关于用 Raspberry Pi 进行图像处理的详细书籍。可以在www . a press . com/us/book/9781484227305购买。

结论

在本章中,我们了解了使用 SciPy 进行图像处理的世界。我们还学习了如何使用 NumPy ndarray表示图像。我们学习了用scipy.misc包对图像执行一些基本操作。下一章,我们将学习 matplotlib 的数据表示和处理。

十四、Matplotlib

在最后一章中,我们用 SciPy 研究了数字图像处理。我们研究了 SciPy 为数字图像处理提供的几个主要功能类别,合并在scipy.misc下。在这一章中,我们将学习更多的使用 matplotlib 的图像处理和数据表示技术。我们已经在前面的章节中使用 matplotlib 来绘制和显示图像。如前几章所述,matplotlib 是一个 MATLAB 风格的数据可视化库。数据处理和挖掘是一个庞大的主题,超出了本书的范围;但是,我们可以使用图像作为方便的数据源来演示 matplotlib 的一些数据处理功能。所以让我们开始吧。

读取图像

为代码样本创建一个目录chapter 14。以下代码(清单 14-1 )演示了如何读取和显示图像:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
img = mpimg.imread('/home/pi/book/Dataset/Sample01.jpg')
plt.imshow(img)
plt.show()
Listing 14-1.prog01.py

输出(图 14-1 )如下:

A447085_1_En_14_Fig1_HTML.jpg

图 14-1。

Reading and displaying an image

颜色贴图

色彩映射表用于将颜色应用于数据集。灰度图像会自动应用默认色彩映射表。我们甚至可以为图像设置色彩映射表。为了正确显示灰度图像,我们需要像在前面章节中所做的那样,将色图设置为灰度值。在下面的例子中(清单 14-2 ,我们将使用默认的色彩映射表显示图像的一个通道。然后,我们将应用另一个色图到通道。

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

img = mpimg.imread('/home/pi/book/Dataset/4.2.01.tiff')
img_ch = img[:, :, 0]

plt.subplot(1, 2, 1)
plt.imshow(img_ch)
plt.title('Default Colormap')
plt.xticks([]), plt.yticks([])

plt.subplot(1, 2, 2)
plt.imshow(img_ch, cmap='hot')
plt.title('Hot Colormap')
plt.xticks([]), plt.yticks([])

plt.show()

Listing 14-2.prog02.py

输出(图 14-2 )如下:

A447085_1_En_14_Fig2_HTML.jpg

图 14-2。

Colormaps

彩色的

我们还可以显示彩条,让观众知道图像中的相对强度值。以下代码(清单 14-3 )演示了:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

img = mpimg.imread('/home/pi/book/Dataset/4.2.01.tiff')
img_ch = img[:, :, 0]

plt.imshow(img_ch, cmap='nipy_spectral')
plt.title('Colorbar Demo')
plt.colorbar()
plt.xticks([]), plt.yticks([])

plt.show()

Listing 14-3.prog03.py

输出(图 14-3 )如下:

A447085_1_En_14_Fig3_HTML.jpg

图 14-3。

Colorbar demo

用于图像处理的 Matplotlib

直方图是统计中频率表的图形表示。它是数据集中每个值出现次数的图表。我们还可以使用 matplotlib 中的plt.hist()方法来绘制单通道或灰度图像的直方图。以下(清单 14-4 )是一个例子:

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np

img = mpimg.imread('/home/pi/book/Dataset/Sample03.jpg')
img_ch = img[:, :, 0]

plt.hist(img_ch.ravel(), 256, [0, 256])
plt.title('Histogram Demo')
plt.xticks([]), plt.yticks([])

plt.show()

Listing 14-4.prog04.py

输出(图 14-4 )如下:

A447085_1_En_14_Fig4_HTML.jpg

图 14-4。

Histogram demo

插值方法

plt.imshow()中有多种插补类型。插值类型决定图像的显示方式。理解它们如何工作的最好方法是在渐变图像上使用它们。以下(清单 14-5 )代码示例很好地演示了这一点:

import matplotlib.pyplot as plt
import numpy as np

methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16',
           'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric',
           'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']

grid = np.arange(16).reshape(4, 4)

fig, axes = plt.subplots(3, 6, figsize=(12, 6),
subplot_kw={'xticks': [], 'yticks': []})

fig.subplots_adjust(hspace=0.3, wspace=0.05)

for ax, interp_method in zip(axes.flat, methods):
    ax.imshow(grid, interpolation=interp_method)
    ax.set_title(interp_method)

plt.show()

Listing 14-5.prog05.py

输出(图 14-5 )如下:

A447085_1_En_14_Fig5_HTML.jpg

图 14-5。

Interpolation demo

结论

在本章中,我们学习了如何用 matplotlib 表示数据。我们研究了色图、彩条和直方图。我们还学习了插值的概念。我们可以这样使用 matplotlib 来表示数据、图像和信号。

这本书的概要

在本书中,我们从 Raspberry Pi 和单板计算机的基础知识开始。我们学习了如何设置树莓派 并将其连接到互联网。我们还学会了通过网络访问它。

然后我们继续学习超级计算和并行编程的基础知识。我们准备了 Pis 的节点,并通过一个网络将它们连接在一起,作为一个集群。我们还利用 MPI4PY 开发了集群的功能。

然后我们用 Python 学习了符号计算。我们还研究了用于数值计算的 NumPy 库。然后,我们探索了科学计算库 SciPy 及其在信号和图像处理中的应用。

最后,我们研究了如何用 matplotlib 库表示图像数据和计算直方图。

然而,这并不是结束。这仅仅是你在科学计算的神奇世界中旅程的开始。您可以进一步探索 matplotlib、OpenCV 和 Scientific Kit (SciKit)库。对于想尝试 OS 编程的人来说,他们可以在 Raspberry Pi 上用 C 语言探索 pthread 和 POSIX 库。有了树莓派,可能性是无穷无尽的。我祝你在开始这个神奇的探索之旅时一切顺利。

posted @ 2024-10-05 17:14  绝不原创的飞龙  阅读(45)  评论(0编辑  收藏  举报