资讯动态

用Python控制Arduino和树莓派:串口、GPIO与PWM实战指南

发布时间:2026/9/11 22:08:22 来源:尧图企业网站定制
桌面左上角堆着杜邦线、面包板、两个舵机桌角是一块树莓派4B和一块Arduino Uno。我最初想做的事情就是标题里这句话用Python控制Arduino和树莓派。只不过第一次上手时连“串口”“GPIO”“PWM”这些词的边界都分不清楚网上资料越搜越乱一个舵机控制看了五个视频接法都不同代码倒是都能跑但谁都不敢信。后来自己把主线理清才发现这条路其实没有想象中那么绕只是大多数教程只讲了“怎么接线、怎么复制代码”没讲“为什么是这条路”。这篇文章想做的事是把你和硬件之间那层窗户纸捅破Python到底能控制Arduino的哪些部分、树莓派上的Python和Arduino上的Python有什么不同、环境怎么一次配好、哪些坑属于“所有人都会踩一遍”统统说清楚。无论你是刚装好Python准备入门的小白还是已经用Arduino IDE写过几段C、想转用Python做智能小车、舵机控制、LED全彩屏或摄像头项目的人这篇文章应该都能帮你把路线图铺平。1. 分清三种玩法电脑当大脑还是板子当大脑很多人一上来就搜“Python控制Arduino”然后照着教程做做完发现换个场景又不会了。问题不在于代码而是没搞清楚Python和硬件之间到底有哪几种协作关系。按我自己的经验至少可以分成三种模式A电脑上的Python做决策Arduino做执行模式B树莓派上直接跑PythonPython读写GPIO模式C把MicroPython固件烧进单片机Python直接跑在Arduino上这三种模式长得像底层逻辑完全不同。选错模式后续代码写起来会特别别扭。1.1 三种运行模式的本质区别模式A是我最推荐新手先玩的。电脑或树莓派的Python端负责发指令、收数据、跑算法Arduino端用C写一个循环不断处理串口过来的命令。典型例子就是智能小车Python端可以接收键盘输入或摄像头画面决定“向左转”然后通过串口发一个left字符串给ArduinoArduino再把电机驱动芯片的引脚拉高拉低。好处是分工明确Python端挂了不影响Arduino继续维持舵机角度或电机状态实时性有保障。适合需要快速响应的场合比如舵机、机械臂、循迹小车。模式B是树莓派玩家的主场。树莓派上跑的是完整的Linux系统Python可以直接操作树莓派引脚不需要额外一块单片机。适合做图像识别、视频流、网页控制台这类“需要大算力”的项目。比如你想让小车通过摄像头识别红色小球然后追过去用Arduino算力根本不够直接在树莓派上装OpenCV跑Python识别结果再决定电机怎么动这就是模式B的典型场景。模式C是进阶玩法。把MicroPython固件烧进Arduino、ESP32或树莓派Pico之后这些单片机就不跑C了而是跑一个精简版Python解释器。你可以直接在串口REPL里输入from machine import Pin然后控制引脚。好处是开发效率和代码可读性比C高很多烧录也快坏处是生态兼容性和实时性不如原生C某些高级外设库不一定有MicroPython版本。1.2 选型时先问自己三个问题动手之前建议先回答三个问题你的项目需要跑什么“重”任务如果只是控制舵机、读传感器、驱动电机Arduino加串口完全够用如果需要跑OpenCV、神经网络、Web服务器那就默认选树莓派。你的设备是否要求独立运行、低功耗Arduino插上电池能跑几个月树莓派跑个全功能Linux待机功耗也低不到哪去。可穿戴的小东西模式C更合适。你愿意写多少C模式A必须在Arduino IDE里写一小段C固件很多人卡在这。如果一行C都不想写直接上模式B把所有逻辑都丢给树莓派Python。代价是实时性和稳定性都要自己兜底。我见过不少朋友拿树莓派当Arduino用硬用Python翻转GPIO做高频PWM结果波形抖动得厉害。也见过有人非要在Arduino上跑MicroPython做摄像头图像处理结果内存直接爆掉。选型不是越贵越好是匹配项目需求才算对。2. 环境收拾干净Arduino与树莓派的软件部分一次配好很多人觉得“Python安装”只是装个解释器的事真上手才发现硬件开发一半的时间在解决环境问题。Arduino IDE上传超时、库装到哪了找不到、树莓派换源、OpenCV装了半天装不上这些热搜词背后都是血泪。2.1 Arduino侧IDE、开发板管理器、库目录一次搞清楚如果只是玩Arduino Uno官方Arduino IDE 2.x就够了。但看到“arduino ide开发esp8266的nodemcu”、“esp32环境搭建arduino”、“arduino开发 stm32f103c8t6”这些词就知道很多人不只是玩Uno。第一件事是在“文件—首选项—附加开发板管理器网址”里填入第三方开发板的JSON地址。填完后到“开发板管理器”搜索ESP8266、ESP32或STM32安装对应的平台包。教程很多但有一个细节经常被忽略不同开发板的JSON地址不要一次塞太多否则开发板管理器加载特别慢。建议只填你真正会用到的平台。库的安装位置也是个高频问题。Arduino默认把库放在文档/Arduino/libraries目录。如果你觉得C盘不够用或者想统一管理可以在首选项里修改“编辑器位置sketchbook location”把它改到别的盘然后重启IDE。但要注意改完之后IDE只认新目录下的libraries文件夹旧的库不会自动迁移。而且Arduino库目录名有硬性要求不能用空格和连字符文件夹里必须直接包含.h和.cpp文件。很多人改了位置发现库还是找不到十有八九是目录层级错了。上传超时是另一个反复出现的坑。遇到avrdude: stk500_recv(): programmer is not responding这类报错先别怀疑人生按这个顺序排查现象最常见原因解决办法找不到端口USB线是纯供电线不带数据换一根能传数据的USB线端口能找到但上传超时板子型号选错开发板管理器里选成了别的型号核对板子型号和端口上传失败但不报错串口监视器还开着占用了串口关闭串口监视器再上传老款Pro Mini/接特殊板板上没有自动复位电路按住复位键点上传瞬间松开2.2 VSCode替代Arduino IDE为什么我推荐这么干Arduino IDE写小Demo还行写超过两三百行的固件就开始难受了没有像样的自动补全、没有Git集成、报错信息也不够直观。所以“用vscode替代arduino编辑器”成了很多人的刚需。方案有两种。轻量一点的是装VSCode的Arduino扩展配合Arduino CLI能补全、能编译、能烧录体验比原版IDE好很多。但要注意这个扩展需要LEGACY Mode初次配置要指定Arduino IDE的安装路径。复杂一点的是PlatformIO它自带编译器、库管理、串口监视器和多框架支持缺点是第一次创建工程会下载大量工具链耐心等就好。我的建议是如果你只是临时烧个舵机控制程序官方IDE最稳如果你打算长期做硬件项目或者要用Python和Arduino配合开发直接上VSCode把Arduino代码和Python代码放在同一个工作区切换上下文会舒服得多。2.3 树莓派侧系统、Python环境、OpenCV一次性配好树莓派这块系统用Raspberry Pi Imager烧录就行。镜像我推荐Raspberry Pi OS Lite加手动装桌面还是直接装全功能版看需求。如果你只想让它当个无人值守的控制中枢Lite版更省资源如果还要连显示器写代码就装Desktop版。树莓派默认源在某些网络环境下下载速度很慢装个OpenCV能干等半小时。通常的处理方式是把软件源切换到访问速度更快的镜像。这个操作直接改/etc/apt/sources.list或/etc/apt/sources.list.d/raspi.list即可不同版本的系统写法略有差异记得先备份原文件。但我想提醒的是换源只影响apt下载速度不影响Python的pip源。如果你用pip装包觉得慢那要单独配置pip源别混在一起。Python环境方面树莓派系统自带了Python 3千万不能用sudo pip install去装系统级包否则很容易把系统搞乱。正确做法是为每个项目建虚拟环境python3 -m venv ~/myenv source ~/myenv/bin/activate装OpenCV时我实测下来的经验是优先用sudo apt install python3-opencv它会自动匹配树莓派的硬件优化。如果确实需要新版特性再用pip装opencv-python。树莓派4B装吴恩达那种大型预编译包时默认交换空间不够会导致编译进程被杀死建议先把/etc/dphys-swapfile里的CONF_SWAPSIZE临时调大到2048装完再改回来。树莓派5是另一个高频话题。它的40pin布局和4B基本一致但底层驱动变了不少很多老教程里的RPi.GPIO在5上直接不能用了。我的建议是新项目直接选gpiozero或lgpio别在旧库上死磕。3. 串口是最朴素也最可靠的上位机通道模式A的核心链路就是串口。电脑通过USB线连接ArduinoPython程序通过串口发字节Arduino的串口缓冲区收到后解析、执行。整个过程听起来简单但有几个点会决定你写出来的东西是“能跑”还是“稳”。3.1 一个能直接跑的舵机控制案例先看代码。假设Arduino Uno的9号引脚接了一个SG90舵机电脑通过USB串口控制它转到指定角度。Arduino端C固件#include Servo.h Servo myservo; String inputString ; void setup() { Serial.begin(9600); myservo.attach(9); inputString.reserve(10); } void loop() { while (Serial.available()) { char c Serial.read(); if (c \n) { int angle inputString.toInt(); angle constrain(angle, 0, 180); myservo.write(angle); inputString ; } else { inputString c; } } }Python端import serial import time ser serial.Serial(COM3, 9600, timeout1) # Linux下换成 /dev/ttyUSB0 time.sleep(2) # 等Arduino复位完成 ser.write(b90\n) time.sleep(1) ser.write(b0\n) ser.close()这个例子看着简单但它包含了串口通信的两个核心设计波特率约定和消息边界。两边都设成9600数据才能对得上Python每条命令结尾加\nArduino按行解析消息才不会粘连。为什么要加\n因为串口是字节流不是消息队列。你发送b90\n如果PC端一次只发两个字符Arduino读到的就是9和0分两次到达。以换行符作为一帧数据的结束符是最容易实现的“拆包协议”。3.2 为什么Arduino总是“自动重启”用Python连接串口时很多新手会碰到一个怪现象Python脚本一运行Arduino板载LED闪一下程序好像重新启动了。这其实是Arduino板上的USB转串口芯片在搞事。每次电脑打开串口时DTR信号会拉低再拉高会触发板载复位电路Arduino的主控芯片就重启一次。所以Python端serial.Serial()调用完成后的那一下板子需要约1到2秒完成引导。解决办法很简单就是代码里在打开串口后time.sleep(2)等Arduino稳定下来再发指令。如果板子反应特别快或者特别慢把等待时间调整到3秒。如果你用的是Pro Mini这类没有USB芯片的板子或者自己搭的Serial转TTL模块DTR引脚不接复位也行但那就没有自动复位功能了。3.3 从“能跑”到“稳”协议设计比代码更值得花时间新手阶段发90\n控制舵机完全没问题。但项目一复杂比如要控制两个舵机、读取三个传感器字符串命令就会变成一场灾难。我的习惯是定义一套简单的行协议每条命令以JSON或CSV格式发送比如{servo1:90,servo2:45}\nArduino端先读一整行再解析JSON或自己用分隔符切分。这样以后加指令、加参数都不用改通信协议框架只改解析逻辑就行。自定义协议时还有一个容易忽略的点Arduino串口缓冲区只有64字节。一次命令别太长也别一次性连续发几十条不等对方处理完。否则缓冲区溢出数据就丢了。稳妥的做法是发一条等一条或者在协议里加应答。比如Arduino收到指令后执行完回一个ok\nPython端收到ok再发下一条。这个小改动能让很多莫名其妙的Bug自动消失。3.4 没有硬件也能练习Wokwi仿真平台值得一试如果暂时手上没有Arduino板子或者怕把硬件接错烧了完全可以在Wokwi这类在线仿真平台先跑通逻辑。它能仿真Arduino Uno、ESP32还能模拟舵机、LED、传感器串口监视器也有。我当时用Wokwi验证串口协议时发现仿真环境里没有DTR复位问题但在真实板子上有这说明仿真只能帮你验证逻辑正确性替代不了真机的“玄学”问题。把它当练习工具可以别指望仿真跑通了真机就一定跑通。4. pyFirmata把Arduino变成可插拔的USB外设如果你连固件里的C都不想写或者只是想快速验证一个想法还有一条路Firmata协议。它相当于给Arduino烧一个“通用解释器”然后Python端可以直接控制Arduino引脚就像操作一个USB外设。4.1 五分钟跑通pyFirmata第一步在Arduino IDE里打开“文件—示例—Firmata—StandardFirmata”选择你的板子和端口上传。这一步是在给Arduino植入“听Python指挥”的固件。第二步安装Python库pip install pyfirmata2第三步写Python代码点灯from pyfirmata2 import Arduino board Arduino(COM3) # Linux下换成 /dev/ttyUSB0 board.digital[13].write(1) # 点亮板载LED是不是很简洁不用写任何CPython直接控制引脚电平。PWM呼吸灯也简单from pyfirmata2 import Arduino import time board Arduino(COM3) for i in range(100): board.digital[9].mode pwm board.digital[9].write(i / 100.0) time.sleep(0.02) board.exit()读模拟值更直观analog_pin board.analog[0] analog_pin.mode input value analog_pin.read() print(value)4.2 pyFirmata的甜点与暗面pyFirmata最大的价值是开发速度快特别适合教学、数据采集、原型验证。比如你想画一个温度曲线单片机那边别管了直接用pyFirmata读模拟引脚Python里用matplotlib就能画。但它也有明显的短板。每条Firmata指令都要经过“上位机编码—USB传输—Arduino解码—执行”这条链路延迟和抖动都比原生C大。如果你要做高频PWM输出、编码器闭环、飞控这类对时序敏感的项目pyFirmata会显得力不从心。另一个问题是如果Python程序意外退出Arduino会停在最后一条指令的状态舵机该出力还是出力不会自动复位到安全位置。所以涉及运动控制的项目要么在Arduino固件里加超时保护要么慎用pyFirmata。我的经验是快速验证用pyFirmata正式项目写C固件再用Python做上位机。前者帮你省掉反复烧录的时间后者才扛得住长时间运行。5. 树莓派直接跑Python从GPIO、PWM到摄像头树莓派的Python控制完全是另一套逻辑Python直接运行在Linux上通过GPIO库操作引脚。没有“烧录固件”这一步了但也带来了新的问题用哪个GPIO库、PWM准不准、外设驱动怎么开。5.1 GPIO库选型别再用老教程硬套树莓派5树莓派生态的GPIO库至少有四个RPi.GPIO、gpiozero、pigpio、lgpio。新人最容易踩的坑是照着树莓派4B的老教程在树莓派5上写import RPi.GPIO结果直接报错或者行为异常。做一个简单对比库特点适用场景RPi.GPIO最老牌教程最多但树莓派5兼容性一般老项目维护gpiozero官方推荐面向对象封装了很多传感器接口新项目首选pigpio独立守护进程支持硬件PWM、远程GPIO需要伺服控制或较高PWM精度lgpio底层库gpiozero的后端之一需要细粒度控制时新项目我强烈建议直接用gpiozero。很多细节它都帮你处理了比如舵机控制、距离传感器、LED亮度渐变代码量能比RPi.GPIO少一半而且官方文档维护得很好。5.2 PWM输出控制舵机公式必须自己会算树莓派上跑“树莓派pwm波输出”是热搜常客控制舵机又是最典型的场景。很多人抄了一段代码改了Pin脚就上电结果舵机要么嗡嗡乱抖要么直接不动多数是因为没搞懂占空比。舵机的控制信号是50Hz的PWM也就是周期20ms。舵机根据“高电平持续时间”决定角度0°对应约0.5ms高电平90°对应约1.5ms180°对应约2.5ms。换算成占空比就是0°0.5ms / 20ms × 100% 2.5%90°1.5ms / 20ms × 100% 7.5%180°2.5ms / 20ms × 100% 12.5%用RPi.GPIO写是import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(18, GPIO.OUT) pwm GPIO.PWM(18, 50) # 50Hz pwm.start(2.5) # 0度 pwm.ChangeDutyCycle(7.5) # 转到90度如果是gpiozero舵机控制更简洁from gpiozero import Servo from time import sleep servo Servo(18) servo.value 0 # 中位 sleep(1) servo.value 1 # 最大角度看着简单但要注意不同品牌舵机的脉冲范围可能不是标准的0.5ms到2.5ms有的只有1ms到2ms。gpiozero的Servo类提供了min_pulse_width和max_pulse_width参数实测中发现舵机不到位时先调这两个参数而不是怀疑代码。5.3 LED全彩屏和ILI9341屏幕驱动别跟硬件驱动死磕树莓派驱动LED全彩屏通常有两种情况一种是WS2812B这类灯珠用rpi_ws281x库另一种是ILI9341这种SPI接口的TFT屏幕。WS2812B的控制协议是“单线串行”对时序要求极高不能用普通GPIO翻转模拟。我建议直接用rpi_ws281x官方绑定的Python库树莓派上要用DMA方式输出from rpi_ws281x import PixelStrip, Color # LED配置 LED_COUNT 8 LED_PIN 18 LED_FREQ_HZ 800000 LED_DMA 10 LED_BRIGHTNESS 255 strip PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA) strip.begin() for i in range(LED_COUNT): strip.setPixelColor(i, Color(255, 0, 0)) strip.show()注意用DMA方式时有些教程会让你禁用音频输出因为PWM和音频共用DMA通道报错时按这个思路排查。ILI9341屏幕有所谓“树莓派官方驱动”但实测下来最省心的其实是使用overlay_device_tree里的dtoverlayili9341然后在/dev/fb1上跑fbi或SDL。如果你只是想显示个仪表盘可以不用装一堆Python库直接用内核驱动就好。这一步对新手来说相当有门槛建议直接锁定一个靠谱的教程别东拼西凑。5.4 摄像头OV5647模块配OpenCV的入门玩法OV5647摄像头模块也就是树莓派官方Camera V1500万像素日常项目完全够用。树莓派4B和5用libcamera体系老式的raspistill命令已经废弃了所以别再抄老教程了。拍照可以直接用picamera2from picamera2 import Picamera2 import cv2 picam2 Picamera2() config picam2.create_still_configuration(main{size: (1920, 1080)}) picam2.configure(config) picam2.start() frame picam2.capture_array() cv2.imwrite(shot.jpg, frame) picam2.stop()如果你已经装了OpenCV还能直接在Python里做人脸检测、颜色识别给树莓派小车加“眼睛”。我第一次让小车识别到红色小球然后追过去时那个爽感确实比单纯点灯高太多。顺便提一句树莓派Pico虽然也叫“树莓派”但它不是跑Linux的单板计算机而是一块单片机。它可以用MicroPython的machine模块控制PWM、舵机写法类似Arduino但没有GPIO库也不能跑OpenCV。很多初学者把它和树莓派4B混在一起看到代码不一样就懵了。6. 我建议你把这些坑先趟一遍硬件项目的坑跟纯软件项目完全不同。软件报错有堆栈硬件报错往往就是“不工作”三个字。下面这些是我在给板子接线、写Python脚本、跑各种Demo时遇到且反复出现的问题按开发顺序列出来你照着检查能省很多时间。6.1 硬件接线与电平先过脑子再过电树莓派的GPIO是3.3V逻辑Arduino Uno的I/O是5V逻辑。如果你把Arduino的5V引脚输出直接接到树莓派GPIO上运气好是电位不识别运气不好就直接烧了树莓派。跨板通信时最好用逻辑电平转换模块或者在Arduino侧用分压电阻。另外不要试图用树莓派GPIO直接驱动继电器、电机或者多个LED。GPIO的电流输出能力很弱带不动大负载正确做法永远是“GPIO接驱动模块驱动模块接负载”。我第一次做智能小车时把电机驱动板的GND忘了接导致树莓派和电机驱动板电位不共地信号完全乱掉。后来师傅点了一句“所有模块必须共地”这句话到现在我都觉得比很多教程的代码值钱。6.2 权限、端口占用和“看不见的进程”树莓派上跑串口或GPIO最常见的两类报错串口打开失败通常是因为当前用户不在dialout组。执行sudo usermod -aG dialout 你的用户名注销重登后再试。GPIO报“Another process is currently using this GPIO”多半是有个Python脚本没退出或者后台守护进程还占着引脚。用ps aux | grep python查一下杀掉旧进程再跑。还有一次我死活连不上树莓派的串口排查了半天才发现是上一个终端里的picocom没关默认占用了/dev/ttyAMA0。凡是“端口打不开”的奇怪报错先问自己一句是不是有别的程序占着这个设备。6.3 树的坑GPIO编号、硬件PWM、老教程适配树莓派4B和5的40pin物理布局一样但引脚映射到芯片的内部编号可能有变化。RPi.GPIO用的是BCM编号还是BOARD编号一定看代码里setmode那行。有人照着教程写的GPIO.setmode(GPIO.BCM)却把杜邦线插在物理第18脚BCM编号24结果灯死活不亮就是这个原因。树莓派的硬件PWM通道是有限的而且不同型号能用的引脚不一样。如果你在树莓派5上发现某个PWM脚没输出别急着怀疑代码先用gpioinfo看一下当前引脚模式或者查一下对应型号的“硬件PWM可用引脚表”。树莓派5上老库的兼容性问题尤其多所以我在5.1节才专门说新项目直接用gpiozero或者lgpio。6.4 Arduino上传超时的最后一道排查流程“arduino 上传超时”这个热搜词常年不下榜。如果前面表格里的方法都试过了还不行剩下几个冷门原因USB线太长或质量太差数据容易丢。换根短一点的线试试。有的USB口供电能力弱插上板子后芯片不断掉电重启。把板子接到带外接电源的USB Hub或者给Arduino板载的5V脚外接稳定电源。如果用了Arduino Nano老版本基于ATmega168它不支持新的bootloader需要在“处理器”选项里改成“ATmega168P”或者老版本。笔记本合盖睡眠后USB设备会进入挂起状态重新拨插一次USB线就行。如果以上都排查完还是超时多数时候问题不在板子在上传工具链配置。可以在VSCode里换一种上传方式比如用AVRDUDE手动指定端口烧录。这个方案麻烦但有时还真能救回来。6.5 老生常谈的资源冲突Python进程和系统资源树莓派上如果同时开着摄像头直播、OpenCV识别、串口收发偶尔会出现帧率掉到惨不忍睹的情况。这不一定是硬件性能不够可能是你多个进程同时抢CPU或者摄像头模块被两个进程同时打开。注意picamera2的摄像头同一时间只能被一个进程占用如果你开了一个网页视频流程序再跑一个拍照脚本第二个程序就会报“No camera available”。先停掉第一个进程再试。另外GPIO资源不能跨进程共享这也是树莓派项目的经典雷区。你写了一个Flask服务控制LED又在命令行里跑了一个测试脚本两者同时操作同一个引脚轻则报错重则引脚状态互相干扰。解决办法是让所有控制逻辑集中在同一个常驻进程里其他进程通过消息队列或HTTP接口间接调用而不是各自直接控GPIO。写到这里我其实最想说的是硬件项目最贵的不是树莓派也不是Arduino板子是时间和情绪。控制好预期、每一步先跑通了再叠加功能比什么都重要。我自己的习惯是先做最小闭环比如先让LED亮起来再让它闪再加按键再接舵机最后才是串口控制、摄像头识别。每加一层验证一层这种踏实的感觉只有亲自试过才知道。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价