花费200元,我用全志H616和雪糕棒手搓了一台可UI交互的视觉循迹小车
常见的视觉循迹小车都具备有路径识别、轨迹跟踪、转向避障、自主决策等基本功能,如果不采用红外避障的方案,那么想要完全满足以上这些功能,摄像头、电机、传感器这类关键部件缺一不可,由此一来小车成本也就难以控制了。
但如果,有这样一款视觉循迹小车,它可以完全自己手搓,并用成本极低的雪糕棒来搭建车体架构,不仅保留了传统循迹小车具备的所有功能,还额外适配上一块小屏幕并配上UI界面用于升级人机交互方式。


更重要的是,它的器件成本被压缩到200元左右,这样的视觉循迹小车能让你心动吗~

核桃派视觉循迹小车简介
核桃派H616视觉循迹小车的循迹功能和人机交互界面整体代码由Python+Qt实现,它通过摄像头获取周围环境的图像信息,并利用图像处理算法识别出特定的标记或路径,然后根据标记或路径的形状和方向信息,自动控制小车的行驶方向和速度,以实现沿着预定轨迹自动行驶的目的。

手搓一台视觉循迹小车所需要用到的基础硬件材料如下:
1、核桃派H616开发板+LCD屏幕≈178元;
2、四个电机+车轮≈16元;
3、电机驱动模块≈4元;
4、摄像头≈50元;
5、移动电源≈20元;
6、雪糕棒若干≈4元(也可以≈不要钱);
循迹功能实现
要让小车实现循迹自运动的操作,其实也可以说是一个在教小车如何精准识别线路并做出判断的过程,想要小车的摄像头实现对路线的准确判断,就需要用到一个目前循迹小车最广泛采用的技术手段——二值化。

二值化是图像分割的一种方法,用于将图像中的像素点矩阵的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
在二值化过程中,将大于某个临界灰度值的像素灰度设为灰度极大值(通常是255),将小于这个值的像素灰度设为灰度极小值(通常是0),从而实现二值化。
# 根据不同模式,用不同的hsv上下限值
upper_hsv = (180,255,100)
lower_hsv = (0,0,0)
grayImage = cv2.inRange(hsvImage, np.array(lower_hsv), np.array( upper_hsv)) # 颜色二值化

二值化图像后,整个画面会被区分为黑白分明的两种颜色,之后就需要进行路线轮廓的描绘以及质心的标注,这个操作的目的是让小车知道该往左拐还是往右拐,进而控制两边车轮的速度。
- 获取最大轮廓
# 获取所有轮廓,画出所有轮廓
contours, hierarchy = cv2.findContours(grayImage, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
areas = [cv2.contourArea(c) for c in contours]]
cv2.drawContours(rgbImage,contours, -1,(0,255,0),3)
- 计算质心
# 计算面积最大轮廓的质心
areas = [cv2.contourArea(c) for c in contours]try:M = cv2.moments(contours[areas.index(max(areas))])except:passM10=M.get("m10")M01=M.get("m01")M00=M.get("m00")if M00 <= 0 :continuecX = int(M10 / M00)cY = int(M01 / M00)
# 绘制质心
cv2.circle(rgbImage, (cX, cY), 15, (255, 0, 255), -1)

在绘制轮廓与质心后就需要进行质心坐标的判断,这里的原理很简单,就是质心偏左就往左转,质心偏右就往右转,在判断的同时通过电机来控制两边车轮的速度进而控制小车的行驶方向。



根据质心的位置计算车轮速度的控制参数postion,根据具体的计算公式可以得知,postion的取值范围为-50到+50,其中0代表质心在正中间,+越大质心越往左,-越大质心越往右,则进行下面的速度计算和控制,否则将速度设置为0,再根据postion和delta_sum的值以及其他参数的调整,计算并控制左右车轮的速度。
# 车轮速度控制
if self.flag_start.status() == True :# -50 ~ +50 :0为正中间,+越大则越往左,-越大则越往右,postion = int(int(center_x - cX) / int(center_x / 50)) # postion += self.delta_sum step = self.postion_last - postionself.delta_sum += (postion - step)*0.01if self.delta_sum > 100:self.delta_sum = 100elif self.delta_sum < -100:self.delta_sum = -100if abs(postion) < 5 :self.delta_sum = 0# print("self.delta_sum", self.delta_sum)self.postion_last = postionspeed_l = 50 - postion - int(self.delta_sum) speed_r = 50 + postion + int(self.delta_sum) motor.L.speed(speed_l)motor.R.speed(speed_r)self._slider_l.setValue(speed_l)self._slider_r.setValue(speed_r)

人机交互界面
核桃派H616开发板上预装了PyQt,所以可以使用Qt自带的设计器软件来画窗口,在设计好后通过命令一键转化为Python代码,再去核桃派的开发文档复制一段显示案例的代码,就可以轻松在电脑上预览到刚刚的窗口画面。

为了在远程服务器上运行图形界面应用程序,通过设置os.environ[“DISPLAY”] = ":0.0"允许Thonny远程运行。
# 允许Thonny远程运行
import os
os.environ["DISPLAY"] = ":0.0"
定义了一个名为event_press的函数,用于处理QPushButton按钮的released事件。当按钮被释放时,切换work.flag_start的状态,并根据状态改变按钮的文本。
def event_press():if work.flag_start.status():work.flag_start.disable()ui.pushButton.setText("点击开始")else :work.flag_start.enable()ui.pushButton.setText("点击结束")

为了处理三个不同按钮的released事件,定义了change_to_mode三个函数,这些函数用于将work.flag_mode的模式设置为不同的值。
def change_to_mode0():work.flag_mode.set_mod( 0 )
def change_to_mode1():work.flag_mode.set_mod( 1 )
def change_to_mode2():work.flag_mode.set_mod( 2 )
ui.pushButton.released.connect(event_press)
ui.pushButton_auto.released.connect(change_to_mode0)
ui.pushButton_black.released.connect(change_to_mode1)
ui.pushButton_white.released.connect(change_to_mode2)
以上这些代码片段是构成一个由PyQt所创建GUI的关键部分。它通过创建一个窗口,并在窗口中显示了一些UI元素,同时定义了一些事件处理函数,这个应用程序根据用户的操作来控制某些功能,并使用定时器来让解释器每隔一段时间运行一次,以保持界面响应性能,最后进入主循环等待事件的触发和处理。
相关文章:
花费200元,我用全志H616和雪糕棒手搓了一台可UI交互的视觉循迹小车
常见的视觉循迹小车都具备有路径识别、轨迹跟踪、转向避障、自主决策等基本功能,如果不采用红外避障的方案,那么想要完全满足以上这些功能,摄像头、电机、传感器这类关键部件缺一不可,由此一来小车成本也就难以控制了。 但如果&a…...
AUTOSAR OS TASK
什么是TASK? 我们在裸机中跑代码,程序永远只能单活动流水执行,当程序需要等待的时候,CPU就一直在waiting状态,无法高效的利用CPU,这个时候就引入了并发运行需求。一个系统能同时执行多个不同活动的系统叫做并发系统。其中这个系统中的每个并发执行的活动都由TASK(任务)…...
陇剑杯 2021刷题记录
题目位置:https://www.nssctf.cn/上有 陇剑杯 2021 1. 签到题题目描述分析答案小结 2. jwt问1析1答案小结 问2析2答案小结 问3析3答案 问4析4答案 问5析5答案 问6析6答案 3. webshell问1析1答案 问2析2答案 问3析3答案 1. 签到题 题目描述 此时正在进行的可能是_…...
前端常见的设计模式
说到设计模式,大家想到的就是六大原则,23种模式。这么多模式,并非都要记住,但作为前端开发,对于前端出现率高的设计模式还是有必要了解并掌握的,浅浅掌握9种模式后,整理了这份文章。 六大原则&…...
OpenAI视频生成模型Sora的全面解析:从ViViT、扩散Transformer到NaViT、VideoPoet
前言 真没想到,距离视频生成上一轮的集中爆发(详见《Sora之前的视频生成发展史:从Gen2、Emu Video到PixelDance、SVD、Pika 1.0》)才过去三个月,没想OpenAI一出手,该领域又直接变天了 自打2.16日OpenAI发布sora以来(其开发团队包…...
3个密码学相关的问题
一、离散对数问题(Discrete Logarithm Problem, DLP) 问题描述:给定 有限阿贝尓群 G中的2个元素a和b,找出最小的正整数x满足:b a ^^ x (或者证明这样的x不存在)。 二、阶数问题(O…...
5G网络eMBB、uRLLC、mMTC
ITU(国际电信联盟)于2015年9月正式定义了5G的三大应用场景:eMBB(增强型移动宽带)、uRLLC(低时延高可靠通信)、mMTC(海量物联网通信)。 eMBB是4G MBB(移动宽带…...
matplotlib图例使用案例1.1:在不同行或列的图例上添加title
我们将图例进行行显示或者列显示后,只能想继续赋予不同行或者列不同的title来进行分类。比较简单的方式,就是通过ax.annotate方法添加标签,这样方法复用率比较低,每次使用都要微调ax.annotate的显示位置。比较方便的方法是在案例1…...
nginx 日志改为json格式
nginx 日志改为json格式 场景描述效果变更旧样式新样式 场景描述 正常使用nginx时,使用默认的日志输出格式,对于后续日志接入其他第三方日志收集、清洗环节,因分隔符问题可能不是很友好。 xxxx - - [19/Feb/2024:11:16:48 0800] "GET …...
【DDD】学习笔记-应用服务
Eric Evans 为运用领域驱动设计的系统架构划定了层次,在领域层和展现层之间引入了应用层(Application Layer):“应用层要尽量简单,不包含业务规则或者知识,而只为下一层(指领域层)中…...
【医学大模型】MEDDM LLM-Executable CGT 结构化医学知识: 将临床指导树结构化,便于LLM理解和应用
MEDDM LLM-Executable CGT 结构化医学知识: 将临床指导树结构化,便于LLM理解和应用 提出背景对比传统医学大模型流程步骤临床指导树流程图识别临床决策支持系统 总结解决方案设计数据收集与处理系统实施临床决策支持 提出背景 论文:https://arxiv.org/p…...
YOLOV8改进系列指南
基于Ultralytics的YOLOV8改进项目.(69.9) 为了感谢各位对V8项目的支持,本项目的赠品是yolov5-PAGCP通道剪枝算法.具体使用教程 专栏改进汇总 二次创新系列 ultralytics/cfg/models/v8/yolov8-RevCol.yaml 使用(ICLR2023)Reversible Column Networks对yolov8主干进行重设计,里…...
FlinkSql一个简单的测试程序
FlinkSql一个简单的测试程序 以下是一个简单的 Flink SQL 示例,展示了如何使用 Flink Table API 和 Flink SQL 进行基本的数据流处理。 定义数据实体 CC : - CC 类表示数据流中的元素,包含两个字段: character (字符&a…...
二、ActiveMQ安装
ActiveMQ安装 一、相关环境二、安装Java8三、下载安装包四、启动五、其他命令六、开放端口七、后台管理 一、相关环境 环境:Centos7.9安装ActiveMQ版本:5.15.9JDK8 二、安装Java8 安装教程:https://qingsi.blog.csdn.net/article/details/…...
通俗易懂的L0范数和L1范数及其Python实现
定义 L0 范数(L0-Norm) L0 范数并不是真正意义上的一个范数,因为它不满足范数的三角不等式性质,但它在数学优化和信号处理等领域有着实际的应用。L0 范数指的是向量中非零元素的个数。它通常用来度量向量的稀疏性。数学上表示为…...
如何在30天内使用python制作一个卡牌游戏
如何在30天内使用python制作一个卡牌游戏 第1-5天:规划和设计第6-10天:搭建游戏框架第11-20天:核心游戏机制开发第21-25天:游戏界面和用户体验第26-30天:测试和发布附加建议游戏类型游戏规则设计界面设计技术选型第6-…...
VsCode指定插件安装目录
VsCode指定插件安装目录 VsCode安装的默认目录是在用户目录(%HomePath%)下的.vscode文件夹下的extensions目录下,随着安装插件越来越多会占用大量C盘空间。 指定VsCode的插件目录 Vscode安装目录: D:\Microsoft VS Code\Code.exeVscode插件安装目录&a…...
解决npm淘宝镜像到期问题
1 背景 由于node安装插件是从国外服务器下载,如果没有“特殊手法”,就可能会遇到下载速度慢、或其它异常问题。 所以如果npm的服务器在中国就好了,于是我们乐于分享的淘宝团队干了这事。你可以用此只读的淘宝服务代替官方版本,且…...
【JAVA】java泛型 详解
java泛型 详解 一、参数化类型(Parameterized Type):二. 泛型类(Generic Class):三. 泛型方法(Generic Method):四. 通配符类型(Wildcard Type)&a…...
基于RBAC的权限管理的理论实现和权限管理的实现
权限管理的理论 首先需要两个页面支持,分别是角色管理和员工管理,其中角色管理对应的是角色和权限的配合,员工管理则是将登录的员工账号和员工所处的角色进行对应,即通过新增角色这个概念,让权限和员工并不直接关联&a…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
