前面五节你都在一根一根地接线。这一节换个方式:一整套设备,直接叠上去。 Sense HAT 是一块官方扩展板——64 颗彩色小灯 + 一个摇杆 + 一堆环境传感器,插上去就能用。它还有个了不起的来历:它的兄弟真的在国际空间站上跑过学生的代码。
7-1 讲 40 针时提过 HAT 这个词和"BCM 0/1 两根针留给 HAT 身份识别";本节终于见到一块真 HAT,也明白那两根针为什么不能动。 7-3 到 7-5 里,LED、按钮、传感器全是你自己一根线一根线接的;Sense HAT 把这些都做进了一块板子——所以本节的重点不是"新原理",而是"同一批概念,换一种组织方式"。
一、Sense HAT 是什么:叠上去的一块板
HAT = Hardware Attached on Top,意思是"装在(树莓派)上面的硬件"。它不是随便一块扩展板,而是有规格的:
- 必须对准那 40 针排针,直接压上去(板上有对应的母座);
- 板上带一颗小芯片记录"我是什么板子"(就是 7-1 说的那两根"别动"的针——系统开机时会去读它,所以系统能自动认出"上面插了一块 Sense HAT");
- 尺寸和树莓派一样大,装上去正好。
Sense HAT 是为国际空间站(ISS)的 Astro Pi 项目设计的——那是树莓派基金会和欧洲空间局合作的一个教育项目:把两块装了 Sense HAT 的树莓派送上太空,让学生写的 Python 代码在天上真的跑起来。 为什么非得是"树莓派 + Sense HAT"这种组合?因为在空间站上你得用一块能开机、能联网、还不用焊线的板子:LED 矩阵用来给学生显示反馈,传感器用来测舱内环境,而代码就是普通的 Python——学生在教室里写的东西,可以原样送到 400 公里高空去运行。 这是本课最值得知道的一件事:你现在要玩的这块板子,和天上的那两块是同一款产品。
二、上面有些什么
三大部分,正好对应你前三节学的东西:
2.1 8×8 RGB LED 矩阵(= 64 颗灯)
64 颗灯排成 8 行 8 列,每颗都能单独调颜色。(普通 LED 只能亮/灭,这里的每颗灯相当于 7-4 讲的"三色 + 可调亮度"。)
它的坐标:x 从 0(最左)到 7(最右),y 从 0(最上)到 7(最下)——和 6-6 讲的列表下标一样从 0 开始。
2.2 五向摇杆(= 一个更高级的按钮)
上、下、左、右、按下(中)五个方向。它其实是 7-3 那个按钮的升级版:不只"按没按",还能告诉你"往哪个方向推"。
2.3 一堆环境与运动传感器
| 传感器 | 它测什么 | 大白话 |
|---|---|---|
| 加速度计 | 三个方向的加速度 | 感觉得到"倾斜、晃动、掉下去"(静止时读数就是重力) |
| 陀螺仪 | 转动的快慢 | 感觉得到"在转、转多快" |
| 磁力计 | 磁场方向 | 就是电子罗盘(能指北) |
| 气压计 | 空气压力 | 能算"大概在几米高",也能看天气趋势 |
| 湿度计 | 空气湿度 | 就是湿度 |
| 温度计 | 温度 | 注意:它会被 CPU 烤热(见第五节) |
九轴 IMU 是什么:加速度计(3 轴)+ 陀螺仪(3 轴)+ 磁力计(3 轴)= 9 轴。三个合起来,才能算出"板子在空间里的姿态"(手机能自动转屏,靠的就是这套)。
V2 版还多一个色彩传感器(能测环境光的颜色和亮度)。
因为它省掉了你 7-5 那种"接一颗芯片、配四根线、还要开 SPI"的全过程。
Sense HAT 和树莓派之间主要靠 I2C 通信(还记得 7-1 说的吗——I2C 只用两根线就能管一串设备)。板上有一颗小控制器替它管那 64 颗灯和摇杆,树莓派这边只要发指令过去。
这就是"扩展板"的价值:把复杂的接线,变成一句 apt install。
三、装上它:两步
3.1 硬件:断电、对准、压下去
- 先关机、拔掉电源(这一点没有例外);
- 把 Sense HAT 八 轻轻对准 40 针排针——注意方向:板子上的孔要和排针一一对齐,别硬按;
- 确认四周都平贴、没有歪斜,然后用配的螺丝/铜柱固定(避免它被碰歪)。
如果你的板子上已经有别的东西插在排针上,得先拔掉(或者用"堆叠排针"叠着插,但那是进阶话题)。
3.2 软件:一条命令
上电、连上网之后:
sudo apt update
sudo apt install sense-hat
sudo reboot
三条命令各干什么:
| 命令 | 作用 |
|---|---|
apt update |
刷新软件清单(4-5 讲过的规矩:先 update) |
apt install sense-hat |
装上官方 Python 库,它会顺便把 I2C 也打开 |
reboot |
重启(接口开关生效必须重启) |
验证 I2C 打开了:
ls /dev/i2c*
看到什么算成功:出现 /dev/i2c-1。
再看一眼板子在不在总线上:
sudo apt install -y i2c-tools
i2cdetect -y 1
你会看到什么:一张十六进制的格子表,其中某几个格子里印着地址(如 1c、5c、6a)。
读结果:有地址 = 板子被认出来了(每个传感器占一个 I2C 地址)。整张表全是 --(空格)= 它没接好——回去检查排针对齐没有,或者 I2C 没开。
四、用它:四个方向
4.1 点亮矩阵
from sense_hat import SenseHat
sense = SenseHat()
sense.show_message("Hello", text_colour=(0, 255, 0), scroll_speed=0.06) # 滚动显示
sense.show_letter("A") # 显示一个字符
sense.set_pixel(0, 0, 255, 0, 0) # 左上角点一颗红灯
sense.clear(0, 0, 255) # 整块变蓝
sense.clear() # 全灭
几个常用参数:
| 参数 | 作用 |
|---|---|
text_colour=(r, g, b) |
字色(每个分量 0~255) |
back_colour=(r, g, b) |
背景色 |
scroll_speed= |
滚动速度(越小越快,默认 0.1) |
还有更高级的两招:
sense.set_rotation(180) # 整块显示转 180 度(取决于你的板子朝哪边)
sense.set_pixels([...]) # 一次画 64 颗灯(列表必须正好 64 个元素)
Sense HAT 内部用的是一套很小的点阵英文字体,show_message("你好") 会显示成一堆问号或方块。
想在矩阵上表达中文只有一个办法:自己画——用 set_pixels() 手工拼出 8×8 的点阵图案(一个汉字要 8×8 甚至更大才看得清,实际很难)。
规矩:矩阵上的文字一律用英文/数字。
4.2 读传感器
print(sense.get_temperature()) # 温度(℃)
print(sense.get_humidity()) # 湿度(%)
print(sense.get_pressure()) # 气压(hPa)
print(sense.get_accelerometer_raw()) # 三轴加速度(原地原始值)
print(sense.get_gyroscope_raw()) # 三轴角速度
print(sense.get_compass()) # 罗盘方向(角度)
print(sense.get_orientation()) # 姿态:俯仰 pitch/横滚 roll/偏航 yaw
4.3 用摇杆
from sense_hat import SenseHat, ACTION_PRESSED
sense = SenseHat()
for event in sense.stick.get_events():
print(event.direction, event.action) # 例如 up pressed
摇杆的用法和 7-3 的按钮一模一样——只是"事件"多了个方向。这是本节最值得体会的一点:你已经学过的概念,换个设备还在用。
五、⚠️ 两个必须知道的坑
5.1 温度读数偏高:不是坏了
你会读到 40 多度甚至更高,吓一跳。原因:温度传感器就在 CPU 旁边几毫米的地方——它测的是"被 CPU 烤热的板子",不是"室温"。
两个解法:
| 办法 | 做法 | 效果 |
|---|---|---|
| 换一个传感器读 | sense.get_temperature_from_pressure() |
通常更接近室温(这颗离 CPU 远一点) |
| 把板子挪开 | 用一根 40 针排线把 Sense HAT 引到远处 | 最准(物理隔离热源) |
对比着看:sense.get_temperature() 和 sense.get_temperature_from_pressure() 两个数差好几度是正常的——这本身就是一个很好的实验:让你亲眼看到"传感器读数是"环境 + 它自己的处境"共同决定的。
5.2 显示方向不对:不是接线错
你装的方向和板子"以为"的方向可能差了 90° 或 180°,于是"上"变成了"下"。
sense.set_rotation(180) # 依次试 0 / 90 / 180 / 270,哪个正就用哪个
Sense HAT 的矩阵有时会被系统当作状态显示屏用(比如开机时显示彩虹、或者显示系统状态)。 如果发现自己的显示"被覆盖",或者程序跑完图案就没了——先确定没有别的进程在用矩阵(别同时跑两个会显示的脚本),必要时重启一次,再跑你的程序。 这和 7-2 讲的"一根针只能有一个主人"是同一条原理:同一块设备也只能有一个主人。
六、动手 Demo:三小步
第 1 步 · 打招呼
nano hat_hello.py
from sense_hat import SenseHat
sense = SenseHat()
sense.set_rotation(0) # 方向不对就改 0/90/180/270
sense.show_message("PI", text_colour=(0, 255, 0), scroll_speed=0.08)
sense.show_letter("7", text_colour=(255, 255, 0))
sense.clear()
print("看到了吗?")
跑:python3 hat_hello.py
看到什么算成功:矩阵上滚出绿色的 PI,然后显示一个黄色的 7,最后熄灭。如果字是倒着的,把 set_rotation 改成 180 再跑一次。
第 2 步 · 一次把所有传感器读一遍
nano hat_sensors.py
from sense_hat import SenseHat
import time
sense = SenseHat()
for i in range(5):
t1 = sense.get_temperature()
t2 = sense.get_temperature_from_pressure()
h = sense.get_humidity()
p = sense.get_pressure()
acc = sense.get_accelerometer_raw()
o = sense.get_orientation()
print(f"--- 第 {i + 1} 次 ---")
print(f"温度(传感器): {t1:.1f} °C 温度(气压推算): {t2:.1f} °C")
print(f"湿度: {h:.1f} % 气压: {p:.1f} hPa")
print(f"加速度: x={acc['x']:+.2f} y={acc['y']:+.2f} z={acc['z']:+.2f} g")
print(f"姿态: 俯仰 {o['pitch']:.1f}° 横滚 {o['roll']:.1f}° 偏航 {o['yaw']:.1f}°")
time.sleep(2)
跑:python3 hat_sensors.py
你会看到什么算成功(数字会随环境变化,下面是示意):
--- 第 1 次 ---
温度(传感器): 41.3 °C 温度(气压推算): 33.8 °C
湿度: 46.2 % 气压: 1002.7 hPa
加速度: x=+0.01 y=-0.03 z=+1.00 g
姿态: 俯仰 0.4° 横滚 -0.4° 偏航 152.3°
读结果 · 三个数慢慢看:
| 现象 | 含义 |
|---|---|
| 两个温度差 5~8 度 | 正常的。上面那个是被 CPU 烤热的(5.1 讲的坑) |
z ≈ +1.00 g |
这就是重力。静止平放时,某个轴会正好"扛着 1 个 g"——g 是重力加速度单位 |
| 倾斜板子时 x/y 变化 | 那根"扛重力"的轴在转移——加速度计就是这么感知倾斜的 |
yaw 慢慢漂 |
正常的。磁力计容易受周围磁场干扰(音箱、电机、甚至桌子里的钢筋) |
动手试试:把板子某一边抬起来,再跑一次,看 x / y / z 怎么变。你刚才做的事,就是"用代码读出一个物理动作"。
第 3 步 · 摇杆控制一个小点(小游戏雏形)
nano hat_joystick.py
from sense_hat import SenseHat, ACTION_PRESSED
import time
sense = SenseHat()
sense.clear()
x, y = 3, 3 # 从中间开始
def draw():
sense.clear()
sense.set_pixel(x, y, 255, 255, 255)
def move(dx, dy):
global x, y
x = min(7, max(0, x + dx)) # 夹在 0~7 之间,出不了边界
y = min(7, max(0, y + dy))
draw()
draw()
print("用摇杆移动白点,Ctrl+C 退出。")
try:
while True:
for event in sense.stick.get_events():
if event.action == ACTION_PRESSED:
if event.direction == "up":
move(0, -1) # 往上=y 变小(y=0 在最上面)
elif event.direction == "down":
move(0, 1)
elif event.direction == "left":
move(-1, 0)
elif event.direction == "right":
move(1, 0)
time.sleep(0.05)
finally:
sense.clear()
跑:python3 hat_joystick.py
看到什么算成功:中间有一颗白点,用摇杆推它四处移动,到边就停住。
读结果:min(7, max(0, ...)) 这个套法叫边界夹取——先和 0 取较大者(防小于 0),再和 7 取较小者(防大于 7)。这是所有游戏里"角色不能走出屏幕"的标准写法。
这一小段代码里,已经有一个游戏的全部要素了:一个位置(x, y)、一个渲染函数(draw())、一个输入处理(摇杆事件)。你在这一节串起来的东西,就是 7-3 到 7-5 的全部积累。
七、30 秒实验:用气压算一算"我在几米高"
气压随高度下降,所以从气压能反推海拔:
python3 -c "
p = 1000.0 # 把它改成你刚才读到的气压值
h = 44330 * (1 - (p / 1013.25) ** (1 / 5.255))
print(f'气压 {p:.1f} hPa → 大约海拔 {h:.0f} 米')
"
你会看到什么(用 p = 1000.0 时):
气压 1000.0 hPa → 大约海拔 111 米
读结果:1013.25 hPa 是海平面的标准大气压,所以气压低于它就意味着"比海平面高"。把 p 换成你自己读到的值跑一遍,就知道自己大概在海拔多少米。
气压还随天气变化(同一个地方,晴天和台风天能差几十 hPa,换算成高度就是几百米)。 所以这个公式只能给你"数量级"的答案——想真正测高度,得结合当天的当地气压基准去校准(或者用 GPS)。 这个实验的价值不在"测准",而在让你看到:传感器读出来的一个数字,经过一条公式,就变成了另一个有意义的信息。这就是"物联网"最基础的动作。
八、常见误区
- "
sense.get_temperature()读到 41 度,是板子出问题了" —— 不是。温度传感器紧挨着 CPU,测的是"被烤热的板子"。想接近室温就用get_temperature_from_pressure(),或者用排线把 HAT 引开。 - "
show_message能显示中文" —— 不能。它只有一套很小的英文字体,中文会变成问号/方块。矩阵上的文字只能用英文和数字。 - "显示的字是倒的,说明接线接反了" —— 不是接线问题。HAT 是整块压上去的,没有接反这回事。用
set_rotation()调方向(依次试 0/90/180/270)。 - "传感器这么多,肯定占掉了一堆 GPIO 针" —— 它主要靠 I2C(两根线)通信,板上自带小控制器替它管灯和摇杆。这也是 HAT 这种形态存在的意义:不占用你一堆针。
- "
i2cdetect全是空格,是软件没装好" —— 先查排针有没有插到底、有没有对齐(物理接触问题最常见),再查 I2C 开了没有(ls /dev/i2c*)。 - "摇杆和 7-3 的按钮是两种完全不同的东西" —— 本质一样,都是"读一个输入 + 触发一段代码",摇杆只是多了方向信息。学过的概念换个设备照样用,这就是本节最想让你体会的一点。
- "
set_pixels()里放多少个元素都行" —— 必须正好 64 个(8×8),少了或多了会报错。顺序是从左上开始、一行一行排下去。
自测三问(点开对答案)
1. Sense HAT 的 LED 矩阵坐标里,`(0, 0)` 是哪一个角?往上走是 `y` 变大还是变小? 2. 你读到传感器温度 41.5°C、气压推算温度 34.1°C。哪个更接近室温?为什么差这么多? 3. Sense HAT 装上以后,"7-3 学的按钮"和"7-6 的摇杆"在用法上像在哪里? **参考答案** 1. **`(0, 0)` 是最左上角**;`x` 向右增大(0→7),**`y` 向下增大(0→7)**。 **所以"往上"= `y` 变小**——这就是 Demo 第 3 步里 `move(0, -1)` 表示"上"的原因。**屏幕上"y 向下长"是几乎所有图形系统的共同约定**(因为它来源于"一行一行往下打印"的排字方式)。 2. **气压推算的那个(34.1°C)更接近室温。**原因是 **`get_temperature()` 那颗传感器就贴在 CPU 旁边,CPU 工作时会发热,把它烤高了**;而气压传感器那颗离热源远一些。 想更准有两条路:**改用 `get_temperature_from_pressure()`**,或者**用 40 针排线把 Sense HAT 引到离板子远一点的地方**(物理隔开热源)。 3. **像在"事件驱动"这层**:两边都不是你去"不停问它按了没",而是**拿到事件/回调去处理**;区别只是**摇杆的事件多带一个"方向"**(上下左右中),而按钮只有"按下/松开"。 **这正说明:硬件换了一茬,你在 7-3 学的写法还能直接用**——因为"设备对象 + 事件回调"这套抽象是 gpiozero 和 Sense HAT 库**共同的设计思路**。- ☑ 第七章 完结 —— 教材这一章已读完全部六节:认清 40 针的构成与编号、学会用 gpiozero 指挥它们、让 LED 亮起来也让按钮有反应、用 PWM 做出"半亮"的模拟输出、外接 ADC 芯片读出模拟电压,最后用一整块 Sense HAT 把"灯 + 按钮 + 一堆传感器"一次装好
- ☑ 第八章 当树莓派遇到 Arduino 开发板 —— 已完结:在树莓派使用 Python 建立串口通信(请来一位补短板的伙伴:认清 Arduino 这块"没有操作系统的板子"、在树莓派上把开发环境装好、第一次把代码灌进芯片、用 Python 从外面指挥它,最后自己订一份串口协议把两块板子接成一条双向回路)
7-1 看清这排针 → 7-2 拿到指挥它的工具 → 7-3 第一次通电(灯亮了)→ 7-4 让灯会"半亮" → 7-5 学会"读"外面的世界 → 7-6 用一块板子把这一切打包。 走完这一章,你这台树莓派终于不只是"一台小电脑"了——它有了眼睛(传感器)、有了手(输出针)、有了耳朵(输入针),能感知和改变身边的物理世界。 这也是全课到此为止最大的转折:前六章你都在"把机器本身搭起来、用起来";从这一章起,机器开始替你接触现实。后面要学的摄像头、显示屏、机器人,全都建立在这一章的基础上。
相关笔记
- 模拟输入 —— 上一节:外接 ADC 芯片读模拟量,本节那些传感器就是"打包好的同类能力"
- 认识 Arduino Uno 开发板 —— 下一章起点:请来一位专门补树莓派短板的伙伴
- 在树莓派使用 Python 建立串口通信 —— 下一章小结:自己订一份协议,把两块板子接成一条双向回路
- 数字输出与输入 —— 摇杆就是按钮的升级版;
ACTION_PRESSED与when_pressed是同一种思路 - 模拟输出 —— 64 颗灯要做"彩色",靠的就是三色 PWM 混色
- 认识树莓派的 GPIO 引脚 —— HAT 身份识别为什么占着 BCM 0/1,答案在那里
- 设置 Raspberry Pi OS —— I2C 开关的位置(
sense-hat包会自动开,但知道它在哪更放心) - 🍓 树莓派 · 索引 —— 全课路线图