简介:一份基于Python与OpenCV的Tello无人机二维码扫描与数字识别完整项目,面向软件工程、人工智能、通信工程、自动化等计算机相关专业的在校学生、老师及企业员工,既可作为毕业设计、课程设计或项目初期立项演示,也适合初学者在视觉识别与无人机控制方向进阶学习。压缩包内共11个文件,包含5个Python源码脚本(覆盖无人机控制、二维码扫描、数字识别、UI交互等模块)、3张JPG图片模板(用于二维码/数字匹配)、1份Markdown使用文档、1份License说明以及1个补充ZIP资料包,整体大小约26KB,结构清晰,便于按模块阅读和二次修改。该项目为个人高分毕业设计源码,已获导师指导认可,答辩评审分达95分,代码均经过测试运行成功;内置tello.py、tello_control_ui.py、tello_QR_code.py等脚本,可实现从图像采集、二维码扫描到数字识别的完整流程,并支持在此基础上扩展其他功能。目前已有189人学习浏览,适合需要快速搭建无人机视觉识别项目或完成课设毕设任务的开发者下载参考。 把这份“优秀项目”的zip拿到手之后,我做的第一件事不是急着解压跑代码,而是先扫了一遍使用文档,因为无人机项目的坑往往不在算法,而在环境、通信和现场调试。这个项目全称叫“基于python+OpenCV的Tello无人机二维码扫描+数字识别”,实际跑通之后我觉得它非常适合两类人:一类是刚学完OpenCV基础、想找个完整项目练手的学生,另一类是正在做物流巡检、仓库盘点类毕业设计或课题的开发者。整套代码做了一件很清晰的事情:让Tello无人机起飞后,在悬停或缓慢移动过程中,通过摄像头采集画面,实时检测画面里的二维码,并在二维码附近完成数字识别,最后把识别结果打印出来或者用于后续控制逻辑。这其实就是一个典型的“无人机+机器视觉”入门闭环,麻雀虽小五脏俱全。接下来我把整个项目从资料拆解、环境配置到核心算法和试飞排错,按我实际跑通的顺序完整过一遍。
1. 先把项目资料拆开看看
1.1 源码里到底有哪些模块
解压看了一圈,项目结构并不复杂,但模块划分比较清楚,属于一看就能上手的类型。常见的目录大概是这样:
- main.py:主入口,负责初始化和整个任务的状态流转
- tello_ctrl.py:封装Tello的SDK指令,起降、悬停、转向都在这层
- qrcode_detect.py:二维码检测与解码模块
- digit_recognition.py:数字识别模块
- requirements.txt:依赖清单
- docs/:使用文档,包含环境搭建和操作步骤
- templates/:数字识别用的模板图(如果你拿到的是模板匹配版本)
- models/:如果项目用了CNN,这里会放训练好的模型权重
我觉得这个划分比较舒服的地方在于,控制、识别、业务逻辑完全分离。你单独拿qrcode_detect.py出来改成一款桌面端扫码工具也能用,单独拿digit_recognition.py去识别一张表格里的数字也可以跑,不会因为绑定了无人机而没办法复用。这也是我后来重新组织代码时会刻意保持的习惯:把传感器数据和业务判断拆开,调试的时候会省很多事。
1.2 两条主线:通信链路与识别链路
整个项目同时存在两条并行链路。第一条是控制链路,电脑通过Wi-Fi连接Tello,默认往192.168.10.1的8889端口发UDP指令,比如command、takeoff、land、streamon,Tello收到后会回复ok或error,这类指令是短连接式的,发一条回一条,谁也不等谁。第二条是视频链路,在term“streamon”之后,Tello会通过UDP端口11111持续推送H.264编码的视频帧,电脑按帧解码后送给OpenCV做处理。两条链路互不阻塞,控制指令不会因为视频带宽占满而迟迟得不到响应,这也是Tello做视觉项目比同价位其它平台更顺手的核心原因。
识别链路则是从视频帧开始的:先拿一帧画面做二维码检测,检测到二维码后,根据二维码在画面中的位置去推导数字区域,再对数字区域做预处理和识别,最终输出一个结构化结果,比如{"box_id": "A01", "num": 3}。识别结果既可以只是打印在终端,也可以反向控制无人机,比如识别到某个数字后执行对应动作。
2. 环境搭建:python+OpenCV 一次配好
2.1 Python 环境与依赖安装
先说环境。这个项目基于python开发,所以我建议直接用Python 3.8或者3.9,这两个版本在OpenCV、djitellopy这些库的支持上都比较稳定,Python 3.10以上我也试过,问题不大,但没必要给自己增加不确定性。装依赖用虚拟环境隔离,不要直接装进系统环境,否则后面项目多了会乱成一锅粥:
python -m venv tello_env # Windows tello_env\Scripts\activate # macOS / Linux source tello_env/bin/activate pip install opencv-python numpy djitellopy这里有个经验点:如果你只是做纯算法开发,不涉及显示窗口,装opencv-python-headless可以省掉一堆GUI依赖,体积更小、更稳定;但像这种无人机项目,你大概率要实时看画面调参,所以装完整的opencv-python更合适,imshow、waitKey这些函数都能直接用。如果你在服务器上跑,再用headless版本也不迟。
2.2 Tello 连接方式与命令通道
项目文档里一般会写明连接步骤:先把Tello开机,等它发出Wi-Fi热点,名称一般是TELLO-XXXX,用电脑连上这个热点,然后才能通过SDK发指令。如果连不上热点,先确认是不是把Tello的USB口插到电脑上充电了,有些机型在USB充电模式下不会正常广播热点,拔掉数据线等几秒再试。
代码层面有两种常见写法。第一种是用djitellopy,封装程度高,适合快速上手:
from djitellopy import Tello tello = Tello() tello.connect() tello.streamon()第二种是直接用socket走Tello原生SDK,适合需要完全掌控通信细节的场景:
import socket import time tello_ip = '192.168.10.1' cmd_port = 8889 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(('', 9000)) sock.settimeout(5) def send_cmd(cmd): sock.sendto(cmd.encode(), (tello_ip, cmd_port)) try: resp, _ = sock.recvfrom(128) return resp.decode() except socket.timeout: return "timeout" print(send_cmd("command")) # 进入SDK模式 print(send_cmd("streamon")) # 打开视频流注意一个顺序问题:必须先进入SDK模式,也就是先发command并收到ok,再发其它指令才有效。很多同学第一次跑的时候直接发takeoff,结果无人机动都不动,就是因为跳过了这一步。
3. 二维码扫描模块:核心实现与调优
3.1 QRCodeDetector 的使用细节
OpenCV从3.x开始就内置了二维码检测功能,cv2.QRCodeDetector对常规场景够用,不用额外装zxing或pyzbar。核心调用就三行:
import cv2 detector = cv2.QRCodeDetector() data, points, _ = detector.detectAndDecode(frame)data是解码出来的字符串,如果这一帧没识别出来就是空字符串;points是二维码四个角点坐标的数组。对一张静止的二维码图片,这个函数基本是零失败率,但放到无人机画面里,情况就不一样了。
Tello的视频流分辨率一般是960x720,帧率可达30fps,但实际会因为Wi-Fi带宽和电脑解码能力降到15到20fps,而且画面里有运动模糊,二维码稍微小一点就很容易出现“检测到但解不出内容”的情况,也就是points有值,data却为空。我的处理方式是:不把单帧识别结果当真,而是做连续帧投票,连续几帧识别出相同内容才认定为最终结果。这样一来,即便某一帧模糊了,后面几帧也能把内容补回来。
3.2 飞行场景下的识别增强
我实际测试中发现,二维码检测失败绝大多数不是算法问题,而是画面质量问题。Tello的摄像头不能手动变焦,光圈固定,对光照变化很敏感。室内顶灯直射时,二维码反光会让边缘和黑白色块糊在一起。上电后先不要急着起飞,用实时画面观察一下现场亮度,如果整体偏暗,可以适当降低飞行高度让二维码在画面里占更大比例;如果过曝,要么换个朝向,要么在二维码上方稍微偏一点角度,避开镜面反射。
预处理也能明显提升识别率。一般我会先把BGR转灰度,再做一次直方图均衡化,对比度提上来之后,二维码模块的边界会清晰很多。还有一种思路是缩小检测区域,既然我们已经知道二维码在画面里的范围,可以先把整帧缩到原来的一半再检测,检测速度翻倍,识别率也不会下降太多。cv2.QRCodeDetector在图像分辨率高反而容易出现误检,因为细节太多。
def detect_qr_efficient(frame): scale = 0.5 small = cv2.resize(frame, None, fx=scale, fy=scale, interpolation=cv2.INTER_AREA) detector = cv2.QRCodeDetector() data, points, _ = detector.detectAndDecode(small) if points is not None: points = points / scale # 坐标映射回原图 return data, points注意:detectAndDecode是同步调用,在性能一般的笔记本上,单帧处理时间尽量控制在50毫秒以内。中高端电脑跑起来没问题,但如果你用的是老款低压处理器,建议加一个跳帧策略,比如每处理两帧就跳过一帧,优先保证飞控链路的实时性。
4. 数字识别模块:模板匹配与轻量模型
4.1 数字区域定位与切分
数字识别的前提是先拿到数字区域的图片。大部分场景里,数字不是孤零零出现的,而是和二维码印在同一张标签上,比如仓库货架的编号牌:左边是二维码,右边是数字“3”或者“27”。二维码检测成功之后,points返回的四个点可以直接用来估算数字区域的位置。常见的做法是取二维码右侧区域,沿着points的右上和右下两点向右扩展一定像素,得到一个新的矩形ROI。
实际代码逻辑类似:
import cv2 import numpy as np def get_digit_roi(frame, points): # points shape: (4, 2) pts = np.int32(points).reshape(-1, 2) top_right = pts[1] # 根据OpenCV返回点序调整 bottom_right = pts[2] x_start = max(0, min(top_right[0], bottom_right[0]) + 5) x_end = min(frame.shape[1], x_start + 120) y_top = max(0, min(top_right[1], bottom_right[1]) - 10) y_bottom = min(frame.shape[0], max(top_right[1], bottom_right[1]) + 10) roi = frame[y_top:y_bottom, x_start:x_end] return roi这个位置关系不一定每个项目都一样,但思路是通用的:先根据二维码定位,再按相对位置裁数字区域。如果二维码和数字不在一起,那就只能靠轮廓分析找画面里的数字区域了,MSER或者findContours都能做,只是复杂度会上去不少。
4.2 识别模型的选择与训练思路
数字识别的算法路线,项目源码里通常会给两种方案。第一种是模板匹配,把0到9的数字分别存一张模板图,比如每个数字是20x30的灰度图,然后对ROI做同样尺寸的resize,再用matchTemplate计算相似度,取分数最高的作为结果。这种方案实现最简单,不需要训练,但短板很明显:如果实际环境中的数字字体、粗细、颜色和模板差异太大,误识别会非常严重。
我在另一个项目里踩过一次模板匹配的坑:模板用Arial字体,实际要识别的标签是微软雅黑加粗字体,数字“0”经常被误判成“8”,“1”被判成“7”,后来重新截了一组现场数据的模板才解决问题。所以如果你用模板匹配,一定要从Tello实际拍摄的画面里截取模板,不要图省事用网上下载的字体图。
第二种方案是轻量级深度学习模型,比如基于卷积神经网络的手写数字识别思路。很多参考项目直接用MNIST数据集训练一个CNN,但MNIST是手写数字,对印刷体数字来说存在较大的数据分布差异,直接迁移效果不一定好。更好的做法是准备几百张现场拍摄的数字样本,用CNN做迁移学习或者微调一个简单分类器,二三十轮训练下来准确率能到98%以上。
实际代码里,识别一张ROI的模板匹配大概是这样:
def recognize_digit(roi_gray, templates): roi_gray = cv2.resize(roi_gray, (20, 30)) scores = [] for i in range(10): result = cv2.matchTemplate(roi_gray, templates[i], cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(result) scores.append(max_val) best_digit = int(np.argmax(scores)) best_score = float(scores[best_digit]) return best_digit, best_score如果识别结果置信度低于0.6,我一般会判定为不确定,而不是强行输出一个数字,这样后续业务逻辑不会因为错误识别而做出错误动作。
5. 实操过程:从起飞到识别全流程
5.1 飞行前检查清单
别急着接电起飞,先按这个清单过一遍:
- 场地:室内空旷区域,尽量避开镜子、玻璃、白色高反光墙面,至少保证起飞点附近2米半径内没有障碍物
- 电量:Tello机身指示灯正常亮起,不用USB线连接电脑
- 桨叶:检查桨叶有没有变形、缺角,破损桨叶会导致起飞后抖动异常
- WiFi:电脑能搜到Tello热点并成功连接,终端ping 192.168.10.1能通
- 干扰:关闭电脑上的蓝牙,蓝牙和Wi-Fi共用2.4GHz频段时会互相干扰,导致视频卡顿甚至断链
5.2 运行主程序的完整流程
启动主程序后,整个流程大概是这样的:
main ├─ 连接Tello,进入SDK模式 ├─ 打开视频流 ├─ 起飞并悬停在指定高度 ├─ 循环采集画面: │ ├─ 从视频流取一帧 │ ├─ 二维码检测 │ ├─ 如果检测到二维码: │ │ ├─ 根据二维码位置切出数字区域ROI │ │ ├─ 数字识别 │ │ └─ 输出结构化结果 │ └─ 如果未检测到: │ └─ 保持悬停,或执行小范围转向继续扫描 └─ 任务结束,降落这一步最核心的是异常处理。Tello在飞行过程中偶尔会因为环境光变化、风力扰动而发生小幅漂移,画面中的二维码位置也会跟着移动。我跑测试时发现,如果在识别到二维码后立刻执行降落,会高度骤降产生明显下洗气流,画面模糊甚至翻倒。稳妥的做法是:识别逻辑结束之后,先发送一个悬停指令,等待一两秒让机体稳定,再执行下一个动作。
5.3 关键参数调整心得
有几个参数值得你反复试:
- 悬停高度:我建议先设置0.8到1.2米,太低容易触发地面效应,太高二维码在画面里太小。当二维码实际尺寸是5厘米见方时,1米高度下它在960x720画面里大约占60像素左右,QRCodeDetector识别起来比较舒服。
- 扫描速度:如果你用程序控制无人机转头扫描,不要用默认的大角度转向,一次转15到20度,转完后停半秒再检测,稳定帧的命中率远高于连续转动。
- 置信度阈值:数字识别结果低于0.75就丢弃,宁可漏检也别误检,毕竟很多场景下误检比漏检更难处理。
- 跳帧策略:如果电脑CPU占用已经接近满载,优先处理奇数帧,视频画面会略有卡顿,但二维码识别结果反而更稳定。
6. 常见问题与排查技巧实录
6.1 高频问题速查表
我把跑这个项目过程中最容易遇到的问题整理成了一张表,遇到问题可以直接对照排查:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 运行报错No module named cv2 | OpenCV未安装或装错了虚拟环境 | 执行pip install opencv-python,确认当前在tello_env环境内 |
| 发送takeoff没有反应 | 没有先发command指令进入SDK模式,或没有连接Tello热点 | 先发送command并确认收到ok |
| 视频窗口一直黑屏 | 没有执行streamon,或者Wi-Fi不稳定 | 发送streamon,检查Tello热点连接强度 |
| 二维码检测到了但data为空 | 画面模糊、二维码太小或反光 | 降低高度、调整角度、做灰度均衡化预处理 |
| 数字识别把6认成8 | 模板或模型没见过这种字体,ROI切割偏了 | 用现场截图重新制作模板,或调整ROI偏移量 |
| 飞行过程中画面卡顿 | 电脑解码能力不足或Wi-Fi干扰 | 降低分辨率输出、关闭蓝牙、换一台性能更好的电脑 |
| 无人机自动降落 | 低电量保护或飞到限定范围外 | 回来看电量,别等低电量报警才准备降落 |
| 手机/Tello热点连接不上 | 之前配过其它Wi-Fi导致热点广播异常 | 重启Tello,等待热点名称出现再连接 |
6.2 现场调试经验
最后说几个文档里不会写的细节。
第一,UDP通信没有可靠性保证,指令丢失是正常的。我在跑长流程的时候,会自己写一个简单的重发机制:发指令后1秒内没收到应答,自动重发一次,最多重发三次。对takeoff、land这种关键指令尤其重要。第二,Tello过热之后会自动降频,表现就是视频帧率突然下降、指令响应变慢,这时候不要一直排查代码问题,摸一下机身,如果明显发烫,停一会再继续。第三,调试阶段不要用自动降落,最好手动控制,任务结束之后连续按两次land,防止降落时侧翻。
这个项目真正值钱的地方,不是某个算法有多深,而是把“能不能识别”变成了“能不能在无人机上稳定识别”。桌面静态图片上跑识别是第一步,当你发现画面模糊、光照变化、飞行抖动这些真实问题开始影响识别结果时,你才真正开始理解什么叫做工程落地。如果你打算把它改造成自己的项目,我的建议很直接:先原样跑通,再逐项优化。先固定悬停识别,再尝试移动中识别;先调二维码,再调数字;两个都稳了,再去加自动巡航或者路径规划。千万别第一次就想着堆功能,把基本链路跑稳,才是这个项目带给你的最大收益。
本文还有配套的精品资源,点击获取