先说个很多人都问过的问题:手头同时压着 Arduino 和树莓派两块板子,到底先用哪个来学 Python 控制硬件?我的答案很直接——两个都要玩,但玩法完全不一样。Arduino 适合做实时性高的底层控制,树莓派适合跑 Python 直接驱动 GPIO 和处理图像、网络这类复杂任务。这篇文章就把两条路线串起来讲清楚,从环境配置、串口通信、舵机控制、红外解码,到树莓派 5 的 GPIO、PWM、摄像头和 YOLOv5 部署,配套完整代码和踩坑记录。无论你是想做个智能小车、遥控舵机,还是想让树莓派识别物体,都能从这里直接找到可落地的方案。
1. 先搞懂:Python、Arduino 和树莓派三者之间到底是什么关系
1.1 Arduino 和树莓派是两种完全不同的“工具”
很多新手以为 Arduino 和树莓派是同类产品,只是性能差异而已。这个误解会导致后面选型全乱。Arduino 本质是单片机(MCU),芯片上直接跑编译好的机器码,没有操作系统,上电瞬间就能响应引脚变化,微秒级延时都能实现。它适合舵机、电机、传感器数据采集这种需要精确时序和稳定性的活。
树莓派则是完整的 Linux 小电脑。它跑的是 Raspberry Pi OS 或者 Ubuntu 22.04 这类完整系统,Python 直接装在上面,可以通过 GPIO 引脚控制外部设备。因为操作系统会调度任务,它的实时性不如 Arduino,但算力碾压,能跑摄像头图像识别、神经网络推理、Web 服务这些 Arduino 完全做不了的事。
| 维度 | Arduino | 树莓派 |
|---|---|---|
| 本质 | 单片机,无系统 | Linux 小电脑 |
| 编程方式 | C/C++ 编译烧录 | Python 直接运行 |
| 实时性 | 微秒级响应 | 受系统调度影响 |
| 典型任务 | 舵机、小车、传感器 | 图像识别、网络服务、上位机 |
| 典型功耗 | 毫瓦级 | 几瓦到十几瓦 |
用 Python 去控制 Arduino 或树莓派,其实是两条完全不同的链路。控制 Arduino 时,Python 通常跑在 PC 或者树莓派上,作为“上位机”通过串口给 Arduino 下发指令,Arduino 负责底层执行。而控制树莓派时,Python 直接运行在树莓派里,通过 GPIO 库操作硬件,甚至可以同时控制下挂的 Arduino。
1.2 Python 在两条链路里分别扮演什么角色
先说 Arduino 这条链路。Arduino 本身不跑 Python,它的固件是 C++ 写的,但我不需要去改那套 C++ 业务逻辑。我在 PC 上用 Python 写一个指令脚本,通过 USB 串口把命令发给 Arduino,Arduino 收到后解析指令、操作引脚。这样做的最大好处是:改控制策略、加逻辑判断、做数据记录都在 Python 这边完成,不用反复给 Arduino 重新烧录。Arduino 更像是“执行部门”,Python 是“发号施令的管理层”。
树莓派这条链路就简单粗暴了。树莓派上直接跑 Python,用 gpiozero 或 RPi.GPIO 这类库操作引脚。树莓派 5 的性能已经可以跑轻量级深度学习模型,Python 生态里还有 OpenCV、NumPy、PyTorch 这些库,所以树莓派是“既能开会决策又能当场动手的执行者”。有些项目里我会同时用树莓派和 Arduino:树莓派负责摄像头识别和网络通信,识别到目标后通过串口告诉 Arduino 去控制舵机或电机,这种组合能发挥各自优势,后面会给出实际配置。
2. 环境准备:新手最容易卡住的三个环节
2.1 Python 与 VSCode 开发环境配置
不管走哪条链路,PC 上必须先有可用的 Python 环境。强烈建议直接用 3.10 以上版本,因为新版本的语法和类型转换更顺手,而且后述树莓派部署 YOLOv5 用的库对 Python 版本有要求,太低会有一堆兼容性问题。
在 Windows 上安装 Python 时,印象里最大的坑是安装界面底部那个 “Add Python to PATH” 选项没勾。没勾上的话,在命令行敲 python 会提示“不是内部或外部命令”,新手会误以为自己安装失败了。装了之后在命令行执行:
python --version pip --version两个都有输出就说明环境正常。开发 IDE 我建议用 VSCode,装一个 Python 扩展之后,会自动识别解释器、提供代码补全和调试功能。写串口通信代码时,VSCode 的调试控制台能看到 print 输出,排查问题比记事本强太多。
Python 语法层面,新手先掌握变量、类型转换(int()、str()、float())、if 判断、for 循环和函数定义就够了。比如网上常见的“李白打酒”这类练习题,本质就是练循环和条件判断,能把这类题写通,串口命令解析代码也就看懂了。实际上,Python 硬件控制的代码量比爬虫、Web 开发少得多,核心就是“发指令、读数据、做判断”这三个动作。
2.2 Arduino IDE 与库管理细节
Arduino 开发板需要 Arduino IDE 来写固件。下载地址直接搜 Arduino IDE 官网,选对应操作系统的版本即可,不需要额外安装驱动,多数开发板插上 USB 就被系统识别成串口设备。
安装后第一件事是设置中文字体,不是为了好看,而是默认字体在中文注释时会乱码,影响代码阅读。第二件事是改项目文件存放位置。Arduino 默认把库和项目存在 C 盘用户目录,时间一长 C 盘就满了。在文件 -> 首选项 -> 设置中找到“项目文件夹位置”,改成 D 盘或其它空间充足的位置,重启 IDE 生效。要安装第三方库,用工具 -> 管理库搜索库名,比如 Servo、IRremote,安装后库会自动放进新位置。
需要注意的是,Arduino 板型选择必须和实际硬件对应。常见的 Arduino Uno、Arduino Nano 选择对应型号即可。如果你想用 STM32F103C8T6 或 ESP32,需要先在开发板管理器中添加对应的开发板 URL,再安装开发板支持包。ESP32 还有一个细节是settimezone函数,用于设置时区,这在做带时间戳的数据采集时会用到,在 Arduino 代码中调用configTime时可以先设置setenv("TZ", "UTC-8", 1),避免日志时间差 8 小时。
2.3 树莓派系统初始化与软件源更换
树莓派 4B/5 安装系统,官方推荐用 Raspberry Pi Imager 写卡。写卡时可以直接预配置 SSH、Wi-Fi 和用户名密码,省去接显示器的麻烦。如果要在树莓派 5 上跑 Ubuntu 22.04,建议写 Ubuntu Server 版本,然后自己装桌面,因为 Ubuntu 官方的树莓派 5 桌面镜像成熟度不如 Raspberry Pi OS。
装完系统第一步是更新软件源。默认源在国外服务器,下载速度很慢,国内用户建议换成清华源。更换方法是编辑/etc/apt/sources.list和各系统版本对应的.list文件,把源地址前缀改成mirrors.tuna.tsinghua.edu.cn对应路径,然后执行sudo apt update && sudo apt upgrade。这一步能节约大量时间,后续装 Python 库和系统包都会快很多。
SSH 连接树莓派时,默认只有命令行界面。如果需要图形化桌面,用 VNC 或者 X11 转发。每次 SSH 后执行export DISPLAY=:0.0再运行图形程序就能把窗口显示到树莓派连接的屏幕上,比折腾 xrdp 省事。实测树莓派 4B 上跑轻量桌面没问题,树莓派 5 更流畅。
3. Python 控制 Arduino:串口上位机的完整链路
3.1 自定义串口通信协议,别用裸数据
Python 控制 Arduino 的底层是串口通信。PC 和 Arduino 通过 USB 线建立虚拟串口,Python 用 pyserial 库读写串口,Arduino 用Serial对象读写。但千万不能直接写裸数据,比如往串口发个1表示开灯、0表示关灯,一旦命令变多,解析逻辑会混乱。
我一般用简单可靠的文本行协议:每条命令以<开头,>结尾,中间用逗号分隔命令名和参数。比如<SERVO,90>表示控制舵机转到 90 度,<MOTOR,150,150>表示电机左右轮速度分别为 150。Arduino 端收到完整一行后统一解析,这样即使某条命令中途被截断,也不影响下一条。
PC 端 Python 代码:
import serial import time ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) time.sleep(2) # 等待 Arduino 复位 def send_command(cmd): ser.write(f'<{cmd}>\n'.encode()) line = ser.readline().decode().strip() return line print(send_command('SERVO,90')) print(send_command('MOTOR,150,150'))在 Windows 上,串口名通常是COM3或COM4,Linux 上则是/dev/ttyUSB0或/dev/ttyACM0。注意,Arduino 上电时会自动复位,复位瞬间串口不可用,所以 Python 打开串口后要time.sleep(2)稍作等待。
Arduino 端解析代码:
String inputString = ""; void setup() { Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); } void loop() { while (Serial.available()) { char c = Serial.read(); if (c == '>') { parseCommand(inputString); inputString = ""; } else if (c != '<') { inputString += c; } } } void parseCommand(String cmd) { int comma = cmd.indexOf(','); String name = cmd.substring(0, comma); int value = cmd.substring(comma + 1).toInt(); if (name == "LED") { digitalWrite(LED_BUILTIN, value); } else if (name == "SERVO") { // 舵机控制代码见下一节 } }3.2 舵机控制:从 Arduino 直接写到 Python 下发
Arduino 控制舵机是经典入门项目。舵机内部有一个电机和位置反馈电位器,控制信号是周期 20ms、脉宽 0.5ms~2.5ms 的 PWM 波,0.5ms 对应 0 度,2.5ms 对应 180 度。Arduino 的 Servo 库把这些细节封装好了,只需要调用myservo.write(angle)。
在 Arduino 固件里加一个舵机分支:
#include <Servo.h> Servo myservo; void setup() { Serial.begin(9600); myservo.attach(9); // 舵机信号线接数字引脚 9 } void parseCommand(String cmd) { int comma = cmd.indexOf(','); String name = cmd.substring(0, comma); int value = cmd.substring(comma + 1).toInt(); if (name == "SERVO") { myservo.write(constrain(value, 0, 180)); Serial.println("SERVO_OK"); } }然后 Python 端只要循环发送角度即可实现往返扫描:
import serial import time ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1) time.sleep(2) for angle in range(0, 181, 10): ser.write(f'<SERVO,{angle}>\n'.encode()) print(ser.readline().decode().strip()) time.sleep(0.2)实测下来,Arduino 舵机响应速度和直接写 Arduino 程序几乎没有差别,因为最终控制还是 Arduino 在引脚上完成的,Python 只是换个角度就能修改运动轨迹,免去了反复烧录的麻烦。顺便说一句,树莓派 Pico 也能控制舵机,但 Pico 更常用 MicroPython 编程,本身也是 Python 生态,原理一样,只是引脚映射不同,控制代码基本可以直接迁移。
3.3 NEC 格式红外信号的解码与发送
红外遥控在 Arduino 项目里很常见。热词里频繁出现的 NEC 格式,是红外遥控协议的一种。NEC 协议用 38kHz 载波调制,数据帧包含引导码、地址码、地址反码、命令码、命令反码。解码时,通过测量收到的高电平和低电平持续时间来区分 0 和 1:560us 高电平加 560us 低电平是逻辑 0,560us 高电平加 1.69ms 低电平是逻辑 1。
Arduino 端用 IRremote 库可以很方便地接收 NEC 码值。比如按下遥控器某个按键,串口打印出一个 32 位十六进制值:
#include <IRremote.h> int RECV_PIN = 11; IRrecv irrecv(RECV_PIN); decode_results results; void setup() { Serial.begin(9600); irrecv.enableIRIn(); } void loop() { if (irrecv.decode(&results)) { Serial.println(results.value, HEX); irrecv.resume(); } }Python 端读取到的就是类似FF30CF这样的值。想复现某个按键,Python 发指令给 Arduino,Arduino 调 IRsend 库按 NEC 格式重新发送即可。这个能力非常实用——早上我用树莓派的摄像头识别到有人进入房间,Python 直接通过串口让 Arduino 发送红外指令打开空调,全程不用额外硬件。
有一点必须注意:Arduino 接收和发送红外时,引脚不能乱接。接收头的 VCC、GND、OUT 三根线要接对,OUT 接在RECV_PIN上。如果引脚接反,解码值是乱码,排查半天发现是硬件问题,浪费时间。
3.4 组合实战:Arduino 智能小车的控制逻辑
把前面的知识组合起来,就能做一个 Python 控制的智能小车。常见的 L298N 电机驱动板有两个使能引脚和四个输入引脚,Arduino 通过数字输出控制电机方向,通过 PWM 引脚控制速度。Python 端只需要发送两个值:左轮速度和右轮速度。
约定MOTOR,left_speed,right_speed命令,正值前进、负值后退、0 停止。Arduino 端把速度值和方向转换为引脚输出:
int enA = 5; // PWM 引脚 int in1 = 2; int in2 = 3; int enB = 6; int in3 = 4; int in4 = 7; void setMotor(int speed, int pwmPin, int pin1, int pin2) { if (speed > 0) { digitalWrite(pin1, HIGH); digitalWrite(pin2, LOW); } else if (speed < 0) { digitalWrite(pin1, LOW); digitalWrite(pin2, HIGH); speed = -speed; } else { digitalWrite(pin1, LOW); digitalWrite(pin2, LOW); } analogWrite(pwmPin, constrain(speed, 0, 255)); } void parseCommand(String cmd) { int firstComma = cmd.indexOf(','); int secondComma = cmd.indexOf(',', firstComma + 1); String name = cmd.substring(0, firstComma); int left = cmd.substring(firstComma + 1, secondComma).toInt(); int right = cmd.substring(secondComma + 1).toInt(); if (name == "MOTOR") { setMotor(left, enA, in1, in2); setMotor(right, enB, in3, in4); Serial.println("MOTOR_OK"); } }Python 端写完控制策略后,可以定义一系列高级动作:
def forward(speed=180): send_command(f'MOTOR,{speed},{speed}') def turn_left(angle=30): duration = angle / 90 send_command('MOTOR,-100,100') time.sleep(duration) send_command('MOTOR,0,0')用这套组合我做过一个避障小车:树莓派上跑超声波测距逻辑,距离小于 20cm 时发MOTOR命令让 Arduino 转弯。关键是整个控制策略全在 Python 里,调试循环逻辑时改 Python 代码立即生效,不用重新烧录 Arduino,开发效率高了一个数量级。
4. Python 控制树莓派:GPIO、PWM、摄像头与深度学习
4.1 GPIO 引脚对照与库的选择
树莓派控制硬件的第一件事是搞清楚引脚。树莓派 4B 和 5 的 40-pin 排针物理位置一致,但树莓派 5 的部分引脚功能映射和芯片地址发生了变化,老代码不一定直接兼容。GPIO 引脚有两种编号方式:物理编号(板子上第几针)和 BCM 编号(博通芯片的 GPIO 编号)。建议统一用 BCM 编号,因为所有 Python 库的文档和示例默认用 BCM。
树莓派上主流的 Python GPIO 库有三个:RPi.GPIO、gpiozero、pigpio。RPi.GPIO 是老牌库,但树莓派 5 上支持不完善;gpiozero 是官方推荐的库,封装更高级,支持舵机、LED、按钮、距离传感器等组件,代码简洁;pigpio 走的是守护进程模式,提供精确的硬件 PWM 和高频采样能力,适合电机控制这类需要精确时序的场景。
我建议新手直接用 gpiozero,因为 API 设计非常友好。例如控制一个 LED 闪烁:
from gpiozero import LED from time import sleep led = LED(17) # BCM 17 while True: led.on() sleep(0.5) led.off() sleep(0.5)4.2 PWM 波输出与舵机控制
树莓派输出 PWM 波有两种方式。gpiozero 的 LED 类本身支持软 PWM,LED(pin, frequency=100)后用led.value = 0.5就能设置 50% 占空比。但这只是软件 PWM,精度受系统调度影响,用于灯光渐变、蜂鸣器音调足够;用于舵机的精确角度控制,最好用 pigpio 的硬件 PWM 或者直接使用 gpiozero 内置的 Servo 类。
gpiozero 的 Servo 类封装了舵机控制,开箱即用:
from gpiozero import Servo from time import sleep servo = Servo(18) # 舵机信号线接 BCM 18 servo.min() # -1 角度 sleep(1) servo.max() # +1 角度 sleep(1) servo.value = 0 # 中间角度这里的servo.value范围是 -1 到 1,对应舵机运动的两个极限位置。不同舵机的脉宽范围有差异,比如标准舵机是 0.5ms~2.5ms,但一些 360 度连续旋转舵机需要不同的脉宽,此时要调整min_pulse_width和max_pulse_width参数,否则舵机可能会抖动或者转到不了极限位置。
需要给电机驱动板提供精确 PWM 时,pigpio 是更稳的选择。开启守护进程sudo pigpiod,然后:
import pigpio pi = pigpio.pi() pi.set_mode(18, pigpio.OUTPUT) pi.set_PWM_frequency(18, 50) # 50Hz,周期 20ms pi.set_PWM_dutycycle(18, 64) # 0.5ms / 20ms * 2554.3 OV5647 摄像头模块与图像采集
树莓派官方摄像头模块 OV5647 是 500 万像素,支持视频拍摄和静态图像采集。树莓派 5 上,新的 Camera Connector 接口与 4B 不同,但软件层面统一使用 libcamera 框架。Python 端操作摄像头推荐用 picamera2 库,它是 libcamera 的封装,接口简单。
from picamera2 import Picamera2 picam2 = Picamera2() config = picam2.still_configuration() picam2.configure(config) picam2.start() picam2.capture_file("capture.jpg") picam2.stop()如果摄像头接到树莓派后被误识别成别的设备,执行sudo apt install -y libcamera-apps安装最新软件包,再运行libcamera-hello测试画面。需要指出的是,OV5647 模块的排线方向非常容易插反。金属触点一面要对准主板丝印标注的方向,插反后libcamera-hello会提示找不到设备,排线重插即可,不是硬件损坏。
采集图像后如果不做处理就浪费了。用 OpenCV 可以读取图片、做颜色检测:
import cv2 img = cv2.imread("capture.jpg") hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 设定红色的 HSV 范围,检测画面中红色物体的轮廓 mask = cv2.inRange(hsv, (0, 100, 100), (10, 255, 255)) contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)这套流程是后面部署 YOLOv5 的基础。颜色检测适合简单场景,复杂场景就要上深度学习模型了。
还有一块屏幕值得提:ILI9341 是常见的 2.8 寸或 3.2 寸 SPI 液晶屏模块。树莓派官方驱动通过设备树配置启用,不用 Python 逐个像素写入。安装fbtft相关内核驱动或直接用wiringPi初始化后,可以把屏幕当成简单显示终端,显示摄像头画面、传感器数值。如果你只是做数据监控,建议优先考虑 OLED 屏幕加luma.oled库,驱动简单且不占太多引脚。
4.4 树莓派 5 上部署自己的 YOLOv5 模型
树莓派 5 的性能足够跑轻量级深度学习模型。部署自己训练的 YOLOv5 模型,流程分三步:导出模型、安装依赖、推理。假设你已经用 YOLOv5 训练好了自定义数据集,得到一个best.pt权重文件,接下来在树莓派上操作。
先安装依赖。YOLOv5 要求 Python 3.8+,树莓派 5 上装 Ubuntu 22.04 自带 Python 3.10,满足要求。用清华源安装 PyTorch CPU 版本会快很多:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu然后在自己训练的 YOLOv5 项目目录里安装剩余依赖:
pip install -r requirements.txt推理前务必将模型转换为 TorchScript 或 ONNX,纯 Python 的 PyTorch 推理在树莓派上较慢。转换命令在 PC 上执行,用官方提供的export.py:
python export.py --weights best.pt --include onnx把生成的best.onnx拷贝到树莓派,再用 onnxruntime 推理:
import cv2 import numpy as np import onnxruntime as ort session = ort.InferenceSession("best.onnx", providers=["CPUExecutionProvider"]) img = cv2.imread("capture.jpg") img_resized = cv2.resize(img, (640, 640)) img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB) img_input = img_rgb.astype(np.float32) / 255.0 img_input = np.transpose(img_input, (2, 0, 1))[None, ...] outputs = session.run(None, {session.get_inputs()[0].name: img_input})树莓派 5 的 CPU 推理 640x640 输入大约在 1 到 3 秒一帧,实时检测体验一般,但做定点拍照识别、智能门禁这类场景足够。如果想提速,可以下载针对树莓派优化的 NCNN 版本,或者降低输入分辨率到 320x320。
部署完成后,最爽的玩法是把视觉和硬件控制打通:摄像头每 3 秒拍一张照片,YOLOv5 检测到目标类别后,Python 根据目标位置计算舵机角度,通过 GPIO 驱动云台指向目标。这就是一个完整的视觉追踪系统,全部代码都用 Python,不需要碰底层 C++。
4.5 树莓派 5 上性能调优的几个实际经验
- 开启 zram:树莓派 5 默认内存 4G 或 8G,跑 YOLOv5 时内存容易吃紧。安装
zram-tools并把ALGO=lz4、SIZE=2048,相当于给内存扩容,减少 swap 卡顿。 - 使用外部 NVMe 或 U 盘启动:树莓派 5 支持 PCIe,改装 NVMe 固态后系统加载、模型载入速度提升明显,SD 卡读小文件太慢。
- 降低屏幕分辨率:如果不接显示器,通过
raspi-config强制关掉桌面,只保留 SSH 命令行,能节约 300~500MB 内存。 - 散热必须到位:树莓派 5 满载运行时 CPU 温度轻松到 80 度以上,超过 85 度会主动降频。加一个主动散热风扇,温度能压到 50 度左右,推理速度才稳定。
5. 常见问题排查与避坑实录
5.1 Arduino 上传超时与串口占用
Arduino IDE 点击上传后提示avrdude: stk500_recv(): programmer is not responding,是新手遇到最多的问题。原因通常是串口被占用——Python 脚本占着串口没释放,或者 IDE 自动选择的串口不对。排查步骤:关掉所有 Python 程序,拔插 USB 线,在工具 -> 端口重新选择,然后点击上传。
还有两个隐蔽原因:一是使用了劣质的“只能充电不能传数据”的 USB 线,看起来能供电但数据不通;二是 Arduino 板子上的 bootloader 损坏。劣质线问题换一根线就解决,bootloader 损坏则需要用另一块 Arduino 做 ISP 烧录。所以购买开发板时一定别买杂牌,正经的 UNO 克隆板几十块钱,稳定性好很多。
5.2 树莓派红灯闪烁与电源问题
树莓派板载红灯闪烁或者亮着但系统反复重启,绝大多数情况是电源功率不足。树莓派 4B 推荐 5V/3A,树莓派 5 推荐 5V/5A。很多人用手机充电头供电,电流不够导致持续欠压。欠压时系统还会在日志里输出Under-voltage detected字样。排查方法:换一个功率足够的电源适配器,尽量选官方电源,不要用那种标注“快充”但协议不兼容的充电头。
红灯常亮但绿灯不闪,一般是系统启动失败。可以重新烧录 SD 卡,或者用fsck修复分区。顺便提一个 SD 卡相关的常见报错:在树莓派上挂载 U 盘或修改系统分区时,提示“将磁盘写入文件时被拒绝访问”,八成是 SD 卡上的分区处于只读状态,或者当前用户没有挂载目录的写权限。执行mount -o remount,rw /重挂载,或者给目录加写权限即可。
5.3 树莓派上安装库与换源时的常见坑
在树莓派上执行pip install特别慢,直接换国内源。在用户目录下创建~/.pip/pip.conf:
[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple extra-index-url = https://mirrors.aliyun.com/pypi/simple/这样后续安装 PyTorch、OpenCV、pyserial 等库就快多了。系统级apt包如果下载慢,同样按前面说的更软件源地址。
如果使用pip install时遇到“要安装缺失的节点,请先运行 pip install -U --pre comfyui-m”这类提示,说明某个 Python 包依赖了另一个未安装的插件组件。这不是树莓派特有问题,在 PC 上也会出现。解决方式是严格按照项目文档装配依赖,逐个安装缺失包,不要盲目相信自动安装。
5.4 SSH 连接树莓派图形化界面的几种方式
默认 SSH 连接只有命令行。想要图形界面,最省事的方式是 VNC。在树莓派上执行:
sudo raspi-config选择Interface Options -> VNC -> Enable。之后在 PC 上用 VNC Viewer 连接树莓派 IP 即可看到完整桌面。需要说明的是,树莓派默认的 VNC 走的是 Wayland 协议,在树莓派 5 上和部分老版本 VNC Viewer 不兼容,如果连接后黑屏,换成 RealVNC Viewer 最新版本就好。
X11 转发也是一种方案。PC 上执行:
ssh -X pi@树莓派IP然后直接在 SSH 会话里运行树莓派上的 GUI 程序,窗口会显示在 PC 上。这种方式配置简单,但画面响应慢,适合偶尔修改桌面配置,不适合日常使用。
5.5 Wokwi 仿真平台快速调试
没有实体板子或者想快速验证 Arduino 代码逻辑,用 Wokwi 这类在线仿真平台很香。支持 Arduino Uno、ESP32、树莓派 Pico 等板型,可以直接拖拽 LED、舵机、红外接收模块进行连线,也能跑 Python 写的 MicroPython 代码。我在给客户做方案时经常先用 Wokwi 跑通逻辑,再上实体板,省了不少烧录次数。
Wokwi 上调试 ESP32 时有个细节:仿真环境默认不支持外部网络时间同步,所以configTime和settimezone可能拿不到真实时间,但代码逻辑本身没问题。实际部署到 ESP32 开发板后,时钟就能正常生效。
5.6 树莓派 5 引脚编号不生效的处理
gpiozero 在树莓派 5 上偶尔出现“无法访问 GPIO”的报错。老代码如果用的是 RPi.GPIO 库,树莓派 5 上很可能直接报错,因为该库针对旧芯片,访问方式不兼容。处理方法是更换为 gpiozero 或 pigpio,同时检查/boot/firmware/config.txt里dtoverlay配置是否冲突。有的老教程会加dtoverlay=w1-gpio这类参数,在树莓派 5 上可能会引发 GPIO 冲突,注释掉即可。
6. 一些关于方案选型的个人心得
写到这里,最后分享几个实战得来的体会。
先说说选型。同一个项目到底用 Arduino 还是树莓派,不要只看性能参数,要看你愿意承担多少调试成本。纯硬件控制、需要稳定运行的(比如舵机云台、温控风扇),用 Arduino,稳定性和实时性是它不可替代的优势。需要视觉、网络、数据库这类复杂逻辑的,用树莓派,Python 生态让开发速度非常快。如果预算允许,两者配合是最好的答案:树莓派做大脑,Arduino 做小脑,通过串口沟通,各干各擅长的活。
再说说开发节奏。我习惯的做法是先在 Wokwi 仿真平台上验证 Arduino 固件逻辑,确认无误后再烧录到实体板;树莓派那边先在一台 PC 上写 Python 脚本连虚拟串口模拟 Arduino 响应,把控制策略调试好再对真实硬件。这样两层逻辑分开调试,问题定位快,也避免了反复烧录损伤开发板。
串口通信看似简单,实际想稳定,一定要设计好协议和容错机制。文本行协议比二进制数据更好调试,Python 和 Arduino 两端都加超时重发机制,能减少很多偶发问题。如果你现在正在做一个 Arduino 或树莓派项目,卡在环境或通信环节,按照上面的步骤走一遍,大部分坑都能避开。