pinball_exp_rp5/README.md
2026-06-11 19:37:42 +08:00

24 KiB
Raw Blame History

树莓派 5 圆柱运动控制系统

本项目运行在 Raspberry Pi 5(树莓派 5通过多种通信协议控制三个圆柱在水槽中的运动并实时测量受力情况。


目录

  1. 项目简介
  2. 整体架构
  3. 什么是树莓派Raspberry Pi
  4. GPIO 概念
  5. 通信协议简介
  6. Python 环境搭建
  7. 项目文件结构
  8. 如何运行
  9. 三个驱动模块详解
  10. signal_features.json 说明
  11. 常见问题 FAQ
  12. 参考资料

1. 项目简介

这是一个在 Raspberry Pi 5 上运行的控制系统,用于在水槽中控制三个圆柱的运动并测量受力。系统通过以下方式工作:

  • 三个空心杯电机(带编码器反馈)控制圆柱的旋转
  • 三个步进电机控制圆柱的直线移动
  • 一个高精度 ADC(模数转换器)测量应变片传来的电压信号,从而算出受力大小

2. 整体架构

┌─────────────────────────────────────────────────────────────────────┐
│                         Raspberry Pi 5                                │
│                                                                      │
│   ┌──────────────┐    ┌──────────────┐    ┌──────────────────────┐  │
│   │   SPI 总线    │    │   I2C 总线    │    │    硬件 PWM          │  │
│   │ /dev/spidev0.0│    │ /dev/i2c-1   │    │    GPIO12 输出       │  │
│   └───────┬───────┘    └──────┬───────┘    └──────────┬───────────┘  │
│           │                   │                        │              │
└───────────┼───────────────────┼────────────────────────┼──────────────┘
            │                   │                        │
            ▼                   ▼                        ▼
    ┌──────────────┐   ┌──────────────────┐   ┌──────────────────┐
    │ ADS124S08    │   │ 4EncoderMotor    │   │ StepMotor Driver │
    │ ADC 芯片     │   │ 电机模块 (I2C)   │   │ (I2C + PWM)      │
    │ (SPI 通信)   │   │ 地址: 0x24       │   │ 地址: 0x27       │
    └──────┬───────┘   └────────┬─────────┘   └────────┬─────────┘
           │                    │                       │
           ▼                    ▼                       ▼
     应变片信号            空心杯电机 (×4)          步进电机 (×3)
     (电压测量)            (控制圆柱旋转)          (控制圆柱平移)

3. 什么是树莓派Raspberry Pi

树莓派是一台只有信用卡大小的 Linux 计算机。它虽然小,但具备普通电脑的所有功能——有 CPU、内存、USB 接口、HDMI 视频输出、网络接口等。树莓派最特别的地方是它有一排GPIO 引脚,可以通过这些引脚直接控制外部电子设备(比如电机、传感器、灯光等)。

本项目使用的型号Raspberry Pi 5

树莓派引脚图

树莓派 5 的 40 针 GPIO 排针布局如下从上方俯视SD 卡槽朝上):

           3.3V  (1) (2)  5V
     GPIO2 (SDA)  (3) (4)  5V
     GPIO3 (SCL)  (5) (6)  GND
       GPIO4      (7) (8)  GPIO14 (TXD)
         GND      (9) (10) GPIO15 (RXD)
      GPIO17      (11)(12) GPIO18
      GPIO27      (13)(14) GND
      GPIO22      (15)(16) GPIO23
         3.3V     (17)(18) GPIO24
      GPIO10(MOSI)(19)(20) GND
       GPIO9(MISO)(21)(22) GPIO25
      GPIO11(SCLK)(23)(24) GPIO8 (CS)
         GND      (25)(26) GPIO7 (CS1)
       GPIO0(IDSD)(27)(28) GPIO1 (IDSC)
       GPIO5      (29)(30) GND
       GPIO6      (31)(32) GPIO12 (PWM)
      GPIO13      (33)(34) GND
      GPIO19      (35)(36) GPIO16
      GPIO26      (37)(38) GPIO20
         GND      (39)(40) GPIO21

详细引脚功能说明请参考:树莓派官方引脚文档


4. GPIO 概念

什么是 GPIO

GPIO 是 General Purpose Input Output(通用输入输出引脚)的缩写。可以把 GPIO 引脚想象成树莓派身上伸出来的"小触手",每个触手可以:

  • 输出高电平3.3V或低电平0V:相当于开或关一个开关
  • 检测外部电平是高还是低:相当于感应外部有没有电信号

高电平和低电平

  • 高电平HIGH / 1 / 3.3V:引脚输出 3.3 伏特的电压,相当于"开"
  • 低电平LOW / 0 / 0V:引脚输出 0 伏特的电压,相当于"关"

简单来说,树莓派通过把 GPIO 引脚设置为高电平或低电平,来控制外部设备。比如把某个引脚设为高电平,就等于告诉连接的设备"开始工作"。

本项目中使用的 GPIO 引脚

引脚 (BCM) 功能 连接设备
GPIO12 PWM 输出(产生步进脉冲) StepMotor 模块
GPIO5 步进电机 0 方向控制 StepMotor 模块
GPIO6 步进电机 1 方向控制 StepMotor 模块
GPIO16 步进电机 2 方向控制 StepMotor 模块
GPIO22 电源阶段 1 控制(先供电) ADC (ADS124S08)
GPIO17 电源阶段 1 控制(先供电) ADC
GPIO24 电源阶段 2 控制(后供电) ADC
GPIO27 电源阶段 2 控制(后供电) ADC
GPIO23 保持低电平(初始化期间) ADC
GPIO18 保持低电平(初始化期间) ADC
GPIO25 DRDY 输入检测ADC数据就绪 ADC
GPIO10 SPI MOSI主出从入 ADC
GPIO9 SPI MISO主入从出 ADC
GPIO11 SPI SCLK时钟线 ADC
GPIO8 SPI CS片选低电平选中 ADC
GPIO2 I2C SDA数据线 电机模块
GPIO3 I2C SCL时钟线 电机模块

BCM 编号是 Broadcom 芯片的 GPIO 编号方式,也是 Python 库(如 gpiozero默认使用的编号方式。


5. 通信协议简介

5.1 SPISerial Peripheral Interface串行外设接口

是什么

SPI 是一种同步串行通信协议,用 4 根线连接主设备(这里是树莓派)和从设备(这里是 ADC 芯片)。

四根线的比喻

把 SPI 通信想象成老师和学生对话:

线名 全称 比喻
SCLK Serial Clock串行时钟 老师用节拍器打拍子,所有人按这个节奏说话
MOSI Master Out Slave In主出从入 老师对学生说话的通道
MISO Master In Slave Out主入从出 学生对老师说话的通道
CS Chip Select片选 老师点名:喊谁的名字(拉低电平),谁就说话

CS 低电平有效:当 CS 线从高电平变为低电平时,表示"我要跟你说话了";当 CS 线回到高电平时,表示"对话结束"。

为什么 ADC 用 SPI

  • 速度快SPI 时钟频率可达几十 MHz适合高速数据读取
  • 全双工:可以同时发送和接收数据(就像两个人可以同时说话和听)
  • 高精度 ADC如 ADS124S08需要快速、可靠地传输 24 位数据SPI 非常适合

本项目中的 SPI

  • 主设备:树莓派 5
  • 从设备ADS124S08 ADC 芯片
  • 总线路径/dev/spidev0.0(总线 0设备 0
  • 时钟频率1 MHz每秒传输 1,000,000 位数据)
  • SPI 模式:模式 1CPOL=0CPHA=1

5.2 I2CInter-Integrated Circuit内部集成电路

是什么

I2C 是一种同步串行通信协议,只用 2 根线就可以连接多个设备。

两根线的比喻

I2C 像学校的广播系统

线名 全称 比喻
SDA Serial Data串行数据线 广播的话筒和喇叭,说话和听话都用这一根
SCL Serial Clock串行时钟线 广播的节拍信号,控制说话的节奏

所有设备都接在同一对线上,但每个设备都有一个唯一的地址(就像一个班每个学生有学号)。主机喊学号,对应学号的设备才应答;其他设备保持安静。

为什么电机模块用 I2C

  • 接线简单:只需要两根线,可以挂多个设备(每个设备用不同地址区分)
  • 设备数量:理论上一个 I2C 总线上最多可以连接 127 个设备
  • 电机控制不需要极快的数据传输速度I2C 完全够用

本项目中的 I2C

设备 I2C 地址 用途
4EncoderMotor 模块 0x24 控制空心杯电机旋转
StepMotor Driver 模块 0x27 配置步进电机(使能/复位等)

树莓派通过 /dev/i2c-1I2C 总线 1与这两个模块通信。


5.3 PWMPulse Width Modulation脉冲宽度调制

是什么

PWM 是一种通过快速开关信号来模拟不同电压输出的技术。就像快速眨眼——如果你半睁半闭眼睛,看起来就像在"眯着眼"PWM 也是类似的道理。

两个重要参数

参数 含义 比喻
频率 (Frequency) 每秒开关多少次,单位 Hz赫兹 每秒眨眼的次数
占空比 (Duty Cycle) 高电平时间占一个周期的比例 每次眨眼时眼睛闭着的时间比例——80% 闭着 = 20% 占空比

比如:频率 1000 Hz = 每秒开关 1000 次。占空比 50% = 一半时间通(高电平),一半时间断(低电平)。

为什么步进电机用 PWM

步进电机是一种特殊的电机,每收到一个脉冲就走一步。所以:

  • PWM 的频率越高 → 脉冲来得越快 → 电机转速越快
  • PWM 的频率越低 → 脉冲来得越慢 → 电机转速越慢

树莓派的硬件 PWM(由专用硬件产生,不受 CPU 负载影响)通过 GPIO12 引脚输出脉冲,经过 StepMotor 模块驱动步进电机。


6. Python 环境搭建

6.1 检查环境

树莓派 OS操作系统自带 Python 3。打开终端Terminal输入以下命令检查版本

python3 --version

如果显示类似 Python 3.11.x 或更高版本,说明 Python 已安装。

6.2 创建虚拟环境(推荐)

虚拟环境可以把本项目的 Python 依赖与你电脑上的其他项目隔离开,避免版本冲突。

# 进入项目文件夹
cd pinball_exp_rp5

# 创建虚拟环境(在项目根目录下生成 venv 文件夹)
python3 -m venv venv

# 激活虚拟环境
source venv/bin/activate

# 激活后,终端提示符前面会出现 (venv) 字样

以后每次使用本项目,都先运行 source venv/bin/activate 来激活虚拟环境。

6.3 安装依赖

首先确保 requirements.txt 文件在项目根目录下,然后运行:

pip install -r requirements.txt

各依赖库的作用

库名 作用 安装方式
spidev 操作 SPI 总线的 Python 库。树莓派通过它跟 ADC 芯片通信。 pip install spidev
smbus2 操作 I2C 总线的 Python 库。树莓派通过它跟电机模块通信。 pip install smbus2
RPi.GPIO 控制 GPIO 引脚的经典库(可选)。本项目中主要用 gpiozero 代替。 pip install RPi.GPIO
gpiozero 简化版的 GPIO 控制库。Raspberry Pi OS 自带,无需额外安装。 系统自带
rpi_hardware_pwm 控制树莓派硬件 PWM 输出的库,用于步进电机的脉冲产生。 pip install rpi_hardware_pwm
jupyter 运行 .ipynb 笔记本文件的环境,可以在浏览器中交互式运行代码。 pip install jupyter
matplotlib 绘图库,test.ipynb 中用它将 ADC 采集的数据画成图表。 pip install matplotlib

注意rpi_hardware_pwmmatplotlib 不在 requirements.txt 中,需要单独安装。

# 如果上面的安装命令执行完后还缺少某些库,可以手动补装:
pip install rpi_hardware_pwm matplotlib jupyter

7. 项目文件结构

pinball_exp_rp5/
│
├── README.md                  # 本文件 — 项目说明(你正在读的这份文档)
├── requirements.txt           # Python 依赖列表(自动安装用)
│
├── drv_adc.py                 # ADC 驱动模块 — 采集电压信号SPI 通信)
├── drv_encodermotor.py        # 编码器电机驱动模块 — 控制圆柱旋转I2C 通信)
├── drv_stepmotor.py           # 步进电机驱动模块 — 控制圆柱平移I2C + PWM
│
├── test.ipynb                 # 教学演示 NotebookJupyter 笔记本)
└── signal_features.json       # 实验信号参数(已脱敏处理)

8. 如何运行

# 1. 激活虚拟环境(如果之前创建了虚拟环境)
source venv/bin/activate

# 2. 启动 Jupyter Notebook
jupyter notebook

# 3. 终端会显示一个链接(类似 http://127.0.0.1:8888/
#    用浏览器打开这个链接

# 4. 在浏览器中点击 test.ipynb 文件

# 5. 在 Notebook 中按顺序从上到下运行每个 Cell代码单元格
#    - 点击一个 Cell 选中它
#    - 按 Shift + Enter 运行当前 Cell 并自动跳转到下一个
#    - 也可以点工具栏上的 "Run" 按钮

什么是 Jupyter Notebook Jupyter Notebook 是一种交互式的编程环境,把代码、运行结果、文字说明放在一个文件(.ipynb)中。每个代码块叫做一个"Cell",可以单独运行,非常适合教学和实验。


9. 三个驱动模块详解

9.1 ADC 模块(drv_adc.py

控制芯片

TI ADS124S08 —— 这是一款 24 位高精度模数转换器ADC

  • 24 位意味着它可以分辨非常微小的电压变化(最小可分辨约 0.3 微伏)
  • 可以把连续的模拟电压信号转换成计算机可以处理的数字信号
  • 本项目中用于读取应变片经放大后的电压信号,从而计算受力

通信方式

SPI 总线,路径:/dev/spidev0.0

为什么要分两步给 ADC 上电?

ADS124S08 芯片的模拟电路和数字电路需要按照特定顺序启动。如果同时上电芯片内部状态可能不稳定。所以驱动程序先启动第一部分电源Stage 1等待 100ms 让电源稳定后再启动第二部分电源Stage 2

典型调用流程

from drv_adc import ADS124S08

# 1. 创建 ADC 对象(自动完成上电和初始化)
adc = ADS124S08()

# 2. 配置可编程增益放大器PGA
#    pga_en=1: 启用放大  gain=5: 放大 32 倍
adc.set_pga(pga_en=1, gain=5)

# 3. 配置采样率
#    dr=8: 200 SPS每秒采样 200 次)  mode=1: 单次转换模式
adc.set_datarate(dr=8, mode=1)

# 4. 选择要测量的输入通道(单端模式,以 AINCOM 为公共端)
#    这里选择通道 0AIN0 引脚)
adc.set_input_mux(pos_channel=0)

# 5. 读取多个通道的电压原始值
values = adc.request_channels([0, 1, 2])

# 6. 清理(关闭 SPI 总线,关掉 GPIO 电源)
adc.close()

几个重要的配置

PGA 增益倍数对照表:

gain 编码 实际放大倍数 适用信号
0 ×1 强信号(>1V
1 ×2 中等信号
2 ×4 中等信号
3 ×8 较弱信号
4 ×16 弱信号
5 ×32 微弱信号(如应变片)
6 ×64 极微弱信号
7 ×128 最微弱信号

数据速率对照表:

dr 编码 实际速率SPS 每次采样等待时间
0 2.5 400 ms
4 20 50 ms
8 200 5 ms
13 4000 0.25 ms

SPS = Samples Per Second每秒采样次数。数值越大采样越快但噪声也越大。


9.2 编码器电机模块(drv_encodermotor.py

控制硬件

M5Stack Module 4EncoderMotor —— 一个可以同时控制 4 个空心杯电机的模块。每个电机都带有编码器(可以测量电机转了多少圈)。

通信方式

I2C 总线,模块地址:0x24

什么是编码器?

编码器是安装在电机轴上的传感器。电机每转一个角度,编码器就产生一个脉冲。通过统计脉冲数量,计算机就知道电机转了多少圈、转到了哪个位置。

三种控制模式

模式 名称 说明
NORMAL_MODE (0x00) 普通模式 直接设置 PWM 占空比控制转速,没有闭环控制
POSITION_MODE (0x01) 位置模式 使用 PID 闭环控制,让电机转到指定位置并停住
SPEED_MODE (0x02) 速度模式 使用 PID 闭环控制,让电机保持指定的转速

什么是 PID 控制?

PID 是比例P-积分I-微分D控制的简称是一种自动调节技术。打个比方

你想让水龙头流出的水稳定在每分钟 1 升:

  • P比例:看当前流量跟目标差多少,差得多就多拧一点,差得少就少拧一点
  • I积分:如果长时间有微小偏差(比如一直是 0.98 升),就慢慢增加拧开的幅度
  • D微分:如果流量突然变化(有人碰了水管),就快速反向调整一下

三个参数配合,就能让电机精确地达到并保持目标速度或位置。

典型调用流程

from drv_encodermotor import EncoderMotorDriver, SPEED_MODE

# 使用 with 语句(自动打开和关闭 I2C 连接)
with EncoderMotorDriver(bus=1) as driver:
    
    # 1. 设置电机 0 为速度模式
    driver.set_mode(0, SPEED_MODE)
    
    # 2. 设置速度模式的 PID 参数
    driver.set_speed_pid(0, kp=1, ki=100, kd=1)
    
    # 3. 设置线速度(单位:米/秒)
    #    电机将以 0.01 米/秒的速度旋转
    driver.set_linear_speed_m_s(0, 0.01)

with 语句是 Python 的一种语法确保无论代码是否出错I2C 连接都会被正确关闭。等价于 try: ... finally: driver.close()


9.3 步进电机模块(drv_stepmotor.py

控制硬件

M5Stack StepMotor Driver —— 一个可以驱动最多 3 个步进电机的模块。

通信方式

  • I2C(地址 0x27用于模块配置使能、复位等
  • 硬件 PWMGPIO12用于产生步进脉冲控制电机速度

什么是步进电机?

步进电机是一种特殊的电机,每收到一个电脉冲就转动一个固定的角度(称为"步距角")。本项目中电机的步距角是 1.8°(每圈 200 步),加上驱动器的微步进和减速器后,每圈需要 89,600 个脉冲

三个电机的关系

  • 三个步进电机共享同一路 PWM(即同时以相同的频率步进)
  • 但每个电机可以独立控制方向(通过独立的 GPIO 引脚)
  • 这样三个圆柱可以同时移动,但方向可以不同

S 曲线加减速

电机突然启动或停止会产生机械冲击。本项目使用 S 曲线加减速来让电机平滑地加速和减速。名字来源于速度随时间变化的形状像字母 "S"。

具体做法是用余弦函数来计算每个时刻的速度:

  • 启动时:速度从 0 慢慢增加(就像汽车起步)
  • 中间阶段:加速变快
  • 接近目标速度时:加速又变慢,最终平稳到达目标速度

减速时反过来,整个过程非常平滑,减少了机械冲击和电机失步的风险。

典型调用流程

from drv_stepmotor import StepMotorDriver

# 1. 创建步进电机驱动对象
drv = StepMotorDriver()

# 2. 使能电机(允许接收脉冲)
drv.enable_motor()

# 3. 设置方向True = 正向)
drv.set_dir(True)

# 4. 启动电机并以 S 曲线加速到 0.01 米/秒
drv.start(0.01)

# 5. 在运行中改变速度
# drv.change_speed(0.02)

# 6. 以 S 曲线减速停止
drv.stop()

# 7. 清理资源
drv.close()

# 也支持 with 语句
# with StepMotorDriver() as drv:
#     drv.enable_motor()
#     drv.set_dir(True)
#     drv.start(0.01)
#     time.sleep(2)
#     drv.stop()

10. signal_features.json 说明

这个文件存储了从 LBM格子玻尔兹曼方法 计算机仿真中提取的信号特征。LBM 是一种流体力学仿真方法,用于模拟水流绕过圆柱时的受力情况。

文件内容格式

{
  "signals": [
    {
      "name": "action1",                    // 第 1 个圆柱的信号
      "mean": 0.00166,                      // 信号的直流分量(平均值)
      "components": [                       // 正弦波分量列表
        {
          "frequency": 0.135,              // 频率 (Hz)
          "amplitude": 0.00137,            // 幅值
          "phase": 0.187                   // 相位 (弧度)
        }
      ]
    }
  ]
}

每个 action对应一个圆柱包含

  • mean均值:信号的直流偏置
  • 多个正弦波分量:每个分量有频率、幅值、相位

这些参数用于在主控制循环中合成周期运动信号,让圆柱按照仿真得出的最优方式运动。数据已做脱敏处理(数值做了随机偏移),仅供学习参考。


11. 常见问题 FAQ

Q: SPI 设备找不到

ls -l /dev/spidev*

如果显示 No such file or directory,说明 SPI 接口未启用。

解决方法

sudo raspi-config
# 进入 Interface Options → SPI → Enable
# 重启树莓派

Q: I2C 设备找不到

i2cdetect -y 1

如果命令不存在或没有显示设备地址,说明 I2C 接口未启用。

解决方法

sudo raspi-config
# 进入 Interface Options → I2C → Enable
# 重启树莓派

扫描到的设备地址应该显示为十六进制编号(如 2427)。

Q: 电机不转

请按顺序检查:

  1. 电源:电机模块是否有外部电源供电?树莓派的 5V 引脚通常不足以驱动电机
  2. 使能信号:是否调用了 enable_motor()(步进电机)或设置了正确的模式(编码器电机)
  3. 接线I2C 连线是否正确SDA → GPIO2SCL → GPIO3
  4. 地址I2C 地址是否匹配(编码器电机 0x24步进电机 0x27

Q: ADC 读数为 0

  1. 检查 PGA 配置是否正确(增益是否设得太低,信号太弱读不出来)
  2. 检查输入通道选择是否正确(是不是选对了 AIN 引脚)
  3. 检查 SPI 接线
  4. check_status() 方法查看 ADC 状态寄存器

Q: ImportError: No module named xxx

运行以下命令安装缺失的库:

pip install xxx

常用的库安装命令:

pip install spidev smbus2 rpi_hardware_pwm matplotlib jupyter

Q: Permission denied权限不足

通常是因为当前用户没有访问 SPI 或 I2C 设备的权限。pi 用户默认有权限。如果使用其他用户,可以将用户加入相关组:

sudo usermod -a -G spi,i2c,gpio 用户名
# 然后注销并重新登录

12. 参考资料