- 2024-05-30
-
发表了主题帖:
【2023 DigiKey大赛参与奖】开箱帖:STM32U5A5+F411 NUCLEO
因为下单比较晚,而且近期较忙,最近稍微闲一点,有时间发一下帖子,本次我是参与奖,然后因为金额不够买我想要的mpu,最后还是决定了入手一个stm32家的比较经典的f411和一个U5系列的板卡,下面为收到的开箱图,后续应该会玩一玩U5,看看有没有机会分享帖子吧,下次一定!
最后感谢下得捷电子和eeworld
虽然两个小板子,但是包装非常大
然后是外壳
最后是经典小碎纸
- 2024-05-07
-
发表了主题帖:
《深度学习与医学图像处理》阅读分享四——关键点检测
本帖最后由 Zhao_kar 于 2024-5-7 22:58 编辑
《深度学习与医学图像处理》阅读分享四——关键点检测
本节主讲关键点检测的模型
一、概念
关键点检测是计算机视觉领域的一种技术,用于在图像或视频中定位和识别特定对象或特征的位置。这些对象或特征可以是人脸、人体关节点、车辆、物体等等。关键点通常是图像中的特定位置,比如人脸关键点可能包括眼睛、鼻子、嘴巴等部位的位置。
关键点检测的目标是确定这些关键点在图像中的精确位置。它可以应用于许多应用场景,如人脸识别、姿态估计、手势识别、行人检测等。在人工智能和机器学习的帮助下,现代关键点检测算法能够在不同的环境和复杂的场景中准确地检测和定位这些关键点。
二、坐标关键点检测和热图关键点检测
1、坐标关键点检测:
特点:在坐标关键点检测中,通常直接输出每个关键点的准确坐标。这意味着检测器会返回一系列点的(x, y)坐标,表示图像中每个关键点的位置。
区别:相较于热图关键点检测,这种方法更加直接,输出结果更易理解。然而,它也需要更多的训练数据和更复杂的模型来学习关键点的准确位置。
2、热图关键点检测:
特点:热图关键点检测输出的是关键点位置的概率分布热图。这意味着对于每个关键点,检测器会生成一个与输入图像尺寸相同的热图,其中每个像素的值表示该像素处是关键点的概率。
区别:相对于坐标关键点检测,热图关键点检测更加灵活,因为它可以处理不同尺寸的输入图像,并且可以检测到关键点的模糊位置。此外,热图关键点检测还可以利用一些后处理技术来提高关键点检测的准确性。
综上,热图关键点模型效果更优,下面介绍几种基于热图的关键点模型
三、CPM
CPM指的是Convolutional Pose Machine(卷积姿势机器)。CPM是一种基于深度学习的方法,用于姿势估计和关键点检测。
CPM模型通常由多个卷积神经网络(CNN)阶段组成,每个阶段都负责在不同尺度上生成热图。第一个阶段处理输入图像,并生成初始热图,然后通过级联的阶段逐渐提高热图的分辨率和准确性。其中一个关键特点是通过级联的阶段来逐步提高姿势估计的精度。每个阶段都可以在之前阶段的输出基础上进行训练,使得模型能够逐渐细化关键点位置的估计。这种级联结构使得CPM在处理复杂姿势和场景时表现出色。
这里以书中给的图例为简单描述:一阶段中,图像先经过七个卷积层,三个池化层进行特征提取,P为关键点个数,然后在2阶段下,有两个输入,一个是第一阶段的特征输出,一个是原图像的特征提取,然后再通过五个卷积层进行融合和提取。详见下图
四、stack hourglass
Stacked Hourglass是一种基于卷积神经网络(CNN)的姿势估计方法,它采用了Hourglass网络结构的堆叠。每个Hourglass模块由对称的上采样和下采样层构成,有助于捕捉不同尺度的特征并提高姿势估计的准确性。通过堆叠多个Hourglass模块,Stacked Hourglass能够逐步细化关键点位置的估计,取得优异的姿势检测效果。
首先主要包括四个部分:下采样模块、沙漏模块、中间监督模块、关键点热力图
分别进行如下操作:降低图像分辨率,堆叠沙漏模块+引入中间监督模块,使得各个沙漏模块的输出均参与最终的损失计算,再利用计划函数将最后一个沙漏模块的输出结果转换为类别的概率值,生成关键点热力图。
补充(各个模块):
下采样模块是指在神经网络中的一种结构,用于将输入特征图的空间分辨率降低,同时增加特征图的通道数。这有助于提取更高级别的语义信息并减少计算量。通常通过池化层或者卷积层来实现。
沙漏模块是指沿着网络中间有多层的网络模块,形象地类似于一个沙漏,中间部分较窄,两边较宽。这种模块的设计目的是通过逐层的上采样和下采样来实现多尺度特征的融合,从而提高对目标的定位精度。
中间监督模块是指在网络的中间层添加监督信号,用于在训练过程中引导网络学习更好的特征表示。这种模块的存在有助于减轻梯度消失问题,并且可以加速训练过程。在姿势估计等任务中,中间监督模块通常用于在沙漏模块的不同层次上进行关键点位置的预测和监督。
-
发表了主题帖:
《深度学习与医学图像处理》阅读分享三——医学图像处理
本帖最后由 Zhao_kar 于 2024-5-7 22:10 编辑
《深度学习与医学图像处理》阅读分享——数据预处理部分
本节涉及数据预处理部分和数据增强部分,本节主要分享数据预处理部分,至于数据增强部分不做分享。
首先本书在数据预处理主要有如下几点:
插值
重采样
信号强度直方图
数据归一化
连通域分析和形态学方法
一、插值
首先结合实际环境和应用场景理解,如对图像进行旋转、放大的操作时,如一个2*2的四个像素,假如要放大成4*4的16像素,那么此时可以通过插值算法来进行操作,即我们进行放大操作时,因为会产生未知的像素点,而插值可以由原有的信息来进行预测。
两种常见插值方法:
1、最近邻插值法
这个比较简单,就是把距离最近的输入像素等于变换后的像素即可,所以计算速度很快,同时缺点也很明显,即为放大后的图像带有锯齿
2、双线性插值法
这里有一个公式,实际上可以结合图a来推,这个数学推导不难,总之了解有这么一个结论就可以了,而以此可以升为双线性插值法,相应的,效果会比较好,但是速度会慢一点。
详细效果对比见下图(可以看到图a是有明显的锯齿的)
二、重采样
这里涉及一个概念叫体素间距,场景为:实际上医学中人体部位的真实大小很重要,因此设备与协议之前的差别会导致不同的体素间距,因此要对体素间距进行重采样,保证体素个数可以反映实际成像大小。
这里有一个公式为:真实尺寸=体素个数*体素间距
所以为了保证真实尺寸不变,若增大图像的体素间距,则会导致个数变小,同时变小就出现了一中的问题,那么相应的还得考虑插值。
简单概括就是要改变体素间距和体素个数从而确保真实尺寸,实际上会结合插值和重采样。
三、直方图
说实在直方图这个比较简单,就是一个信号强度的体现,这里附上本书的一张图了解一下即可。
四、数据归一化
这个我觉得大部分做算法的应该都会用得上,比如在做信号处理 时,要对采集到的波形做归一化处理,从而去结合后续硬件软件等。
其实概念上就是消除量纲,本书有两个:
1、区间归一化
其实就是把最大值定位1,最小值定为0,其余参数除最大值,映射到0-1即可,这个自己写个函数就能实现
该方法对信号值敏感,对分布不敏感,适用于强度分布不固定的影像,如CT这种不同强度值含义固定的图像。
2、Z—score归一化
这个为映射到标准正态分布, 这个的特点和上面的方法相反,适用 于MR图像。
五、连通域分析和形态学方法
这里书中没有详细描述,只介绍了概念,这边就先只给出概念。
1、连通域一般是指图像中具有相同像素值且位置相邻的像素 点组成的图像区域。连通域分析是指将图像中的各个连通区域找出并标记。连通区域分析在图像分析处理的众多应用领域非常常用。连通区域分析处理的对象一般是一张二值化后的图像。
2、图像形态学也叫数学形态学,是指一系列处理图像形状特征的图像处理技术,是一门建立在格伦和拓扑学基础上的图像分析学科,是数学形态学图像处理的基本理论。其基本思想是利用一种特殊的结构元来测量或提取输入图像中相应的形状或特征,以便进一步进行图像分析和目标识别
- 2024-04-16
-
发表了主题帖:
STM32MP135F-DK开箱测评
本帖最后由 Zhao_kar 于 2024-4-16 23:11 编辑
STM32MP135F-DK开箱测评+虚拟机的环境搭建
备注:感谢eeworld提供本次机会让我学习Linux的板卡,该板卡是基于Arm®的单cortex®-A7 32位STM32MP135微处理器,本节我先只更新开箱测评和讲一下我搭建虚拟机的一些问题。然后因为本人是Linux的新手,还有因为vivado下了好几个版本的关系,我的D盘清理过后只有100G,因为数据比较多,还有很多软件在里面,我就没分盘,但是我现在学下来看到的大部分都说最好是单独留一个盘出来(我的笔记本不能扩容),这一点还不知道影响大不大,后续进度我感觉也悬,但是都拿到了,那就先自己学着学着研究研究吧。
一、开箱测评
1、首先是本次板卡,我觉得设计的挺好的,因为是之前的寄回板卡,目前是没有出厂设置的,然后我还没搭建好开发环境,现在也只能看到个上电的情况。
2、打开包装可以看到板卡,上面连接的屏幕,还有一个摄像头模块,同时官方也给留了一个SD卡,16GB的。
所有的硬件如下图:
3、然后这个板卡有四个开关,可以在说明手册了解这个如何选择和各个模式的情况,然后他有两个led,如果没放sd卡,会呈现红色,放了则会使用另一个led显示蓝色,如下图:
基本上开箱就这些内容。
二、虚拟机的配置
我这部分只讲我出现的蓝屏问题,我原先是按照某点原子的教程,使用的VMware15.5和Ubuntu18.04两个版本,但是按照实际流程来,最后会出现,启动Ubuntu系统的时候,电脑直接蓝屏重启,然后然后然后,所有网上能试过的方法我都试过了,我真不知道是什么问题,接着我看了stm的官方手册,我觉得还是换一下吧,接下来就用VMware17来安装,把15.5删掉了,重新试了一下,结果发现是ok的。我就直接按照网上帖子的教程走了一遍,然后又出现了第二个问题。
就是默认好像是会选择英文,也不是说英文不能用,问题是某点的资料都是中文的,接着我就在里面改设置,因为需要联网才能下载中文的包,结果就是没有wifi的配置,系统没办法联网,我本来打算试一下网线以太网,后面发现某点启动教程好像不太一样,我试了一下确实是可以直接选择中文的,然后暂时还不知道这个是什么情况
还有一个问题就是,SD卡插在u盘里面没办法读取,这个好像是跟sd的配置有关系,我知道有这么个东西,但是没有详细去了解,先写在这里了,等到后续研究明白了我应该会补充一下。
哦对了还有一个就是mp135里面确实没有m4核,我之前一直看的mp157资料,才发现,确实是没注意
PS:上述均为我自己安装虚拟机和Ubuntu的一些问题,因为本人是新手,有一些可能比较抽象的现象和问题,还请多多包涵。
- 2024-04-14
-
发表了主题帖:
《深度学习与医学图像处理》阅读分享二
本帖最后由 Zhao_kar 于 2024-4-14 23:39 编辑
阅读分享二——几个常见的,且医学图像所用到的,成像原理以及常见格式
本节主要解释几个检测的原理,以及dicom的组成和结构。
一、常见的医学图像数据
首先本书以X线检查、磁共振检查、超声检查等为例
1、先是X射线的成像原理:
当其通过人体时,密度高的部位吸收X线多,密度低的部位吸收X线少,投照到特殊的感光胶片时,表现为:密度高的部位呈现白色,密度低的部位呈灰或黑色。由此可以得到X线片,这个就不放图了,网上随便找一个x光片看看就行了,可以明显看到人体骨骼,也就是密度高区域,是很明显的白色,而对于平时低密度的部位,如果出线了明显白色,那么说明那个部位密度变高了,也就是有可能出现了病变。
当然这种方法不是通用的,对于一部分部位,需要对比剂,也就是注射造影剂来进行造影检查。
然后这里第一个成像,提到了计算机体层成像,也就是CT,上一节有提到过,他有一个便于理解的对比点:CT不同于x线的地方是,用x线对人体进行厚度扫描,然后再由计算机计算后,以不同的灰度显示出来。也就是逐层显示,这里这个知识我确实不了解,还专门去搜了一下,这里放一篇别的博主发的文章,里面比较详细的提到了CT的具体内容算法等等:章节一: CT (computed tomography) 计算机断层扫描 - 知乎 (zhihu.com)。其实比较简单的理解就是从2D变成了3D,但是仍是2维展示,但是通过灰度来显示3D数据,有点像地图上的等高线吧,我也忘记那个叫什么图了,就是地图上,通过不同颜色来显示海拔高度,是一个道理。
2、第二个成像是磁共振成像:
其原理比较复杂,但是简单解释就是,人体置于特殊的磁场时,用无线电射频脉冲激发人体内氢原子核,引起氢原子核共振,并吸收能量。在停止射频脉冲后,氢原子核按特定频率发出射电信号,并将吸收的能量释放出来,被体外的接受器收录,经电子计算机处理获得图像,这就叫做核磁共振成像。
然后是MRI图像,在氢原子恢复原始状态的这个过程所需的时间,叫弛豫时间,分为纵向时间T1和横向时间T2,对这两个进行采集,编码和计算后,得到的就是图像了。这里面以一个脑脊液的加权成像为例,有些学术词我也不是很清楚,但是简单的描述这个过程就是:T1和T2在一个组织内的时间会不一样,进而影响加权成像,同时通过其他方法,如水抑制T2成像可以让脑脊液信号减弱,有利于其余病变区域的显示;还有磁敏感加权等等,这里只是据举例,大概了解就行。
然后MRI同时也可用于血管成像,一般叫MRA,分为两类,一个是不需要对比剂的普通MRA,一个是要对比剂来显示小血管。
3、第三个成像为超声成像:
主要是声波的物理特性来进行检查,包含反射、折射、散射、衍射等等。我这里举个我个人的理解+比较简单的例子来解释,比如一个正常人的喉咙,若没有感冒,此时声带发出的声音是直接从里面正常传出来的,要是感冒或者等等病症导致喉咙发炎,他肿了,那么声音通过大概是会有个阻碍的过程,导致声音变化,这个例子可能比较抽象,不太符合波的这个体现,那么更简单的理解就是,假如身体有个肿瘤或者硬块,本来声波正常过的时候是没什么的,有病变后,声波经过他衰减了,那么最后仪器测试的数据就是不符合正常情况。
上述例子只是理解,实际定义为:声波经过组织器官等后产生了衰减,这些衰减信息经过接收放大处理后就得到了最后的声像图。
然后现在超声检查主要分为ABMD四个类型,A是一维声波,B是二维声波,M是当前最常用的,是基于B的二维上加了个慢扫描波,然后D就是多普勒超声。
最后还补充了一个心电图,这个比较简单,我就不解释了,感兴趣的搜一搜就知道了。
二、常见的影像格式
首先是几个常见的格式:DICOM、Analyze、Nifti、Minc,目前DICOM和NIFTI是实用最广的。
1、DICOM:这个目前一般用于射线照相、超声检查、CT、MRI等
首先他的数据集是多个数据元素合到一起的,每个数据元素又细分为:标签、元素值类型、元素值长度、元素值域。
数据元素又可以分为3类,首先都包括标签、元素值长度、元素值域,然后不同的地方在于元素值类型,有两个是有这个的,叫显式VR,第三个是没有的,叫隐式VR,前两个区别在于元素值长度的表达方式不同。
这个数据集肯定是很多个数据元素合到一起的,这里有一个标准就是显式和隐式不能一起用,即不能嵌套。
然后本书以一个python的例子解析了dicom,我这边就不放出了,后续需要补充我再加吧。
2、Analyze
这个格式包括两个二进制文件,一个img的数据文件,包括像素,一个是元数据头文件,因为其不支持无符号16为格式等其他缺陷,现在基本已经被nifti取代,这个了解了解就行。
3、Nifti
这个其实就是analyze的进化版,他不仅有那两个文件,还支持无符号16位数据格式,还可以区分左右结构,这个也是了解了解就行,书上也没多讲。
4、Minc
其有两个版本,一个Minc1,一个Minc2,1已经停止维护,现在Minc2基于HDF5,这个也没有细说,了解了解即可,感兴趣的可以查查看,目前主流还是DICOM和Nifti。
上述为本节内容,后续应该就会在pycharm上面试试看代码和学习医学图像处理算法的内容了
- 2024-04-03
-
回复了主题帖:
《深度学习与医学图像处理》阅读分享一
bigbat 发表于 2024-4-3 12:16
DICOM重要的内容是通讯和图像描述,如果想了解可以看看DICOMK C/C++编写,fo-DICOM C#编写。还有java语言的 ...
谢谢科普,我现在也是刚接触这些方面的知识
- 2024-04-02
-
发表了主题帖:
《深度学习与医学图像处理》阅读分享一
本帖最后由 Zhao_kar 于 2024-4-2 23:26 编辑
《深度学习与医学图像处理》阅读分享一
感谢本次eeworld提供的阅读机会,本人属于大概了解过有深度学习这么一个东西,但是没有系统学习过,同时图像处理是我目前在学习的一部分内容,但是同样的,我从来没有接触过医学图像处理,还是想学习一下这方面的知识,比如本书里面提到的常见的影响格式DICOM,就是我不了解的内容,所以之后会分享学习记录。
同时本节为第一章的内容,按照计划和实际读起来,这一章更多的是一个引入和一些基础概念的学习,这里附上我个人的心得和记录
一、人工智能概述+机器学习+深度学习
首先是机器学习概念的引入,机器学习算法分为监督学习、非监督学习和强化学习三种
在监督学习中,是具有输入与输出的,即我们通过算法,把数据输入放入合适的函数模型,将其与数据输出对应起来,以实现机器学习的过程
在非监督学习中,只有输入,没有输出,其主要目的为寻找数据的潜在结构信息,类似人类,我们就是通过观察去发现原理和规律,并不是直接被告知规律的。
而强化学习,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略,达成特定目标的问题,
这里我不知道这个智能体是个什么东西,还去查了一些资料,这里附上一个比较通俗的解释
强化学习中两个核心的概念就是:「智能体」agent和「环境」environment。环境表示智能体生存以及交互的世界。每一次交互时,智能体会观察到世界当前所处「状态」state的「观测值」observation,然后决定采取什么「动作」action。环境会随着智能体的动作而发生变化,当然环境自身也可能一直处于变化中。
本节中有一句话我不太理解,这里放出
“机器学习开发的算法使得计算机无须显式编程就能从现有数据中进行学习”,这里的显式编程,我查了资料后,个人的理解为:
这里结合例子理解,在常规的算法时,一般我们通过确定的函数等等去规定计算过程,机器只负责执行这个过程,也就是机器不会思考,比如区分苹果和梨,一个红色一个黄色,如果机器在识别的时候,你只要根据人为计算的规律,并且告诉他,红色的是苹果,黄色的是梨,那么机器就会执行你的过程,去进行分类,但是实际上区分的这个过程绝对不会这么理想,上述这种就是显式编程,即为自定义规则去计算。但是在机器学习中,他不需要这个,这样子就可以理解书中的这句话了。
然后深度学习是机器学习的一个分支,是一种使用包含复杂结构或者多重非线性变换构成的多个处理层对数据进行高层抽象的算法。
目前深度学习框架有如下:卷积神经网络、深度置信网络、递归神经网络等
应用在:计算机视觉图像、音频语音识别、自然语言处理、生物信息学领域
二、人工智能在医学的应用
1、优化医院管理和医学教育:如构造在患者床位轮转方面的预测模型,制定更优决策,以及物资调配方面
2、辅助医学诊断:比如ct图像识别肺结节,超声图像的诊断
3、实现疾病预测:如通过fMRI识别大脑中与阿尔茨海默病识别发病相关的区域,进行预测。
三、医学图像方面的应用
1、医学图像采集与重建:目前已用于计算机断层图像:CT,可实现低分辨率到高分辨率的重建,比如不同场强下的MR图像,一个3T,一个7T,可以将3T转换为7T的MR图像。
(这里解释一下这个T是啥意思:T代表的是tesla,就是场强的大小,场强越大,信噪比SNR越高,T越大通常意味着可以提供更高的分辨率,但是也会带来缺陷,因为随着场强的增加,氢核会以更高的频率共振,所以超高场磁共振必须使用更短的波长,从而使用更高能量的无线电脉冲来使质子摆动。人体组织也会从这些波中吸收更多的能量。)
2、医学图像变换
3、基于医学图像的病灶区域检测与分割
4、基于医学的智能诊断
后续会根据实际学习记录进行更新,个人大概看下来,感觉书中真的有很多医学的知识,对我来说是完全没见过,希望后续的学习能按照计划推进
- 2024-03-21
-
回复了主题帖:
测评入围名单:STM32MP135F-DK(第二批)
个人信息无误,确认可以完成评测计划
- 2024-03-13
-
回复了主题帖:
读书入围名单:《深度学习与医学图像处理》
个人信息无误,确认可以完成阅读分享计划
- 2024-03-03
-
发表了主题帖:
控制之美(卷2)第七节——矩阵求导
本帖最后由 Zhao_kar 于 2024-3-3 15:38 编辑
控制之美(卷2)第七节——矩阵求导实例之线性回归
声明:
本节用一个简单的实际例子来展开向量矩阵求导,同样也是前面的运动员例子
只是15个运动员只需要考虑身高和体重这两个变量
实例引入:
首先根据数据可以绘制身高+体重的散点图,一般身高越高,体重越重,我们可以用一个线性函数表示
此时定义一个预测方程为下图,其中左到右分别是身高估计值、两个参量和一个实际体重
我们要确认y1和y2,同时定义一个代价函数,使其损失最小,代价函数定义为上图第二个式子:
其中式子左右的差就是实际值与估计值的误差,也就是说平方和最小时,预测方程可以较好体现数据的关系
这种方法就是最小二乘法
计算过程:
首先是把四个参数写成矩阵形式,构建方程,然后引入代价函数
然后得到一个函数后,把它分成三个部分,去做求导,求导公式见下图左上部分,且计算后得到如下右边部分的结果
最后附上仿真验证
- 2024-03-02
-
发表了主题帖:
控制之美(卷2)第六节——卡尔曼滤波器的实际仿真
本帖最后由 Zhao_kar 于 2024-3-2 21:01 编辑
控制之美(卷2)第六节——卡尔曼滤波器的实际仿真
声明:
本节是结合前面的理论的一个实例仿真,后续最后一节再补充一个矩阵求导的实例
本节以无人机的高度观测为例展示,即为本书的代码仿真
仿真:
首先状态空间方程根据之前的章节已知:Xk+1=Axk+Buk,这里不写了
接着构建一个矩阵,也就是A,这个是系统矩阵A = [0 1 0; 0 0 1 ;0 0 0];
然后再构建输入的,其中三个变量,本例以高度、速度、重力加速度
再把过程噪声w补上,也就是最开始的方程,前面的文章提到了
此时要考虑第二个测量zk,所以接下来是Hm,也就是测量矩阵,定义H_m = [1 0 0; 0 1 0; 0 0 1];
采样时间设为0.1s,两个协方差分别设为
R_c = [1 0 0; 0 1 0; 0 0 0];
Q_c = [0.01 0 0; 0 0.01 0; 0 0 0];
同时要换成离散系统,sys_d = c2d(ss(A,B),Ts);使用c2d函数进行转换
然后设定初始值,本例为一个0,1,-10,也就是说高度0,1m/s的速度上升,加速度直接当成10
然后设定输入为g,也就是加速度抵消,实则为一个匀速上升的运动
接下来就是设定后验的三个参数,以及运行次数,然后跑个循环
本例运行步数设定是100次,定义过程噪声协方差矩阵 Q_c 和测量噪声协方差矩阵 R_c
然后是初始化,有两个
初始化系统状态 x0 和后验估计 x_hat0
初始化后验估计误差协方差矩阵 P0
定义系统状态和测量的噪声矩阵 w 和 v
然后数据是随机生成的,这个不用管
定义完之后就可以跑循环仿真了,主要的过程如下:
计算系统的实际状态 x 和测量值 z
使用线性卡尔曼滤波器(函数 F8_LinearKalmanFilter)更新状态估计和估计误差协方差,这个实际上是作者在前面讲算法的时候的一个函数文件
将系统状态、测量值、先验估计和后验估计保存到相应的矩阵中
这部分的代码就不放出来了,总之可以得到一个系统,详见仿真图,通过卡尔曼滤波器的使用,可以有效地对无人机高度进行预测,并且能够减少传感器测量误差和系统噪声对预测结果的影响
这里可以顺便看看工作区的变量数值,其实稍微对比一下是可以发现规律是对的,预测结果没有什么问题
-
发表了主题帖:
控制之美(卷2)第五节——卡尔曼滤波器的增益求解公式和算法
本帖最后由 Zhao_kar 于 2024-3-2 18:23 编辑
控制之美(卷2)第四节——线性卡尔曼滤波器的研究模型
声明:
本节为是增益求解部分,这里涉及一个矩阵求导,后面会单独发一篇补充一下一个例子来分析
然后会有一些数学推导,我会附上我自己记得笔记,可能会比较难看明白,还请体谅(数学推导基本跑不掉,很多的概念需要学习)
一、先验状态估计误差
第四节里面主要是解释了K的设计原因,也就是用一个合适的矩阵去让后验接近于实际变量
此处我们需要引入误差,一个先验一个后验
然后上一节的两个式子带入先验,可以进行推导
得到如下图的结果
二、先验状态协方差矩阵
先验协方差矩阵就是下图的Pk
然后图里的ek根据一开始的两个误差,然后进行数学推导
红色部分说明了后验与先验误差之间的关系。而后续则是一个非常复杂的数学推导,光靠文字没办法写清楚
但是我们关注的是一个跟K同维度的矩阵,经过求导可以得到如图
在最后的式子中,Hm和Rc都是已知量,,因此可以根据先验状态估计的协方差矩阵P-k就可以求卡尔曼增益了
二、运算过程
说实在,前面讲了那么多理论,但是实际运算时,还是要考虑算法
算法主要分为时间更新和测量更新,详细的运算过程我觉得书上这个画的非常清楚,同时,这个图也是前面那几个概念的体现
从计算先验估计,然后先验估计协方差矩阵,到卡尔曼增益K,再到后验估计和后验协方差矩阵,刚好完成一个循环
-
发表了主题帖:
控制之美(卷2)第四节——线性卡尔曼滤波器的研究模型
本帖最后由 Zhao_kar 于 2024-3-2 17:35 编辑
控制之美(卷2)第四节——线性卡尔曼滤波器的研究模型
声明:
本节为是研究模型的概念,以及结合之前第二节部分的模型补充
然后会有一些数学推导,我会附上我自己记得笔记,可能会比较难看明白,还请体谅(数学推导基本跑不掉,很多的概念需要学习)
一、先引入状态空间方程
首先是定义一个带有过程噪声的线性离散系统的状态空间方程,见下图
其中从左到右依次为,状态向量,状态矩阵,输入矩阵,输入向量,过程噪声
过程噪声产生于从k-1到k的这个过程中,无法建立模型,但是可以通过前面补充的正态分布的部分的知识去估计
所以过程噪声服从一个期望0,协方差矩阵Qc的正态分布,见下图
二、然后是观测向量的定义
其中从左到右依次为观测向量、观测矩阵、状态向量、测量噪声
这个矩阵H代表观测值与状态向量的关系,观测值也就是设备读取到的值
同一的过程噪声,这个测量噪声也是一个服从Rc的正态分布
然后上面两个概念,都可以考虑协方差矩阵中,相互独立的情况,可以化简为对角矩阵
三、引入先验状态估计
由于上面两个模型,都具备噪声,都没办法求我们需要的状态向量,但是通过卡尔曼滤波器去数据融合,就可以得到一个相对准确的估值
先假定时不变系统,也就是两个矩阵为常数
则先验状态估计为,见下
相对原先等式少了过程噪声,同时用估计值代替实际值
同理,对测量值也做一样处理,引入测量估计
其表示通过测量得到的状态估计值,同时因为测量噪声,所以也是一个根据测量值的估计值
通过这两个估计值,一个是状态空间方程算的,一个是测量后得到的
综上,定义后验状态估计为
然后进行融合,也就是第二节提到的思路,可以得到如下结果。
其中右边括号部分也称为测量残余,当K趋近0时,说明Xk=Xk-,也就是说测量噪声远大于过程噪声
此时有后验接近于先验,相反,K接近于Hm逆时,则说明后验接近于测量值
上述均为卡尔曼滤波器的一般模型的解释,实际运用中,可以根据这个数据融合的思路去用于其他的模型。
- 2024-03-01
-
发表了主题帖:
控制之美(卷2)第三节——协方差理论
本帖最后由 Zhao_kar 于 2024-3-1 23:35 编辑
控制之美(卷2)第三节——协方差理论
声明:
本节主要是协方差的补充,以及结合上一次例子的引入
然后会有一些数学推导,我会附上我自己记得笔记,可能会比较难看明白,还请体谅
一、协方差与协方差矩阵的基本概念
首先在上一节,也就是第二节,用了一个测高度的例子,这个例子中,两个设备的测量结果是不相互影响的,所以在数据融合部分只需要考虑方差即可,但是在多个设备的测量结果是互相影响的情况下,比如一个信号会对第二个信号进行放大或者别的影响,此时就应该考虑关联性了。
由这个关系则引出协方差,并且用协方差表示,同时多组信号时,可以使用协方差矩阵
接下来就是本次例子的引入,本书使用了一组参数15个的运动员的数据,如身高、体重、年龄
二、例子的实际引入+计算+结果分析
首先具体的参数不重要,这里我就不放出来了,只需要知道n=15,且我们使用三个行向量分别表示
如上图的H、W、A,也就是三个参数,此时三个行向量可以写成如下
则此时他们的统计平均数为也就是和除15,见下
同理,统计方差也可求,得到如下参数,且标准差也可由此得到,见下图两部分
根据这些数据,首先已知方差和标准差都是度量数据离散程度的一个值,越大则越分散
标准差与数据本身使用的是同一单位,相对于方差更加直观,如:
上图的数据中,身高平均为181,标准差是6.8,也就是说,这一组数据中基本覆盖在上下6.8的范围内
由上可以直观判断,这两个概念只与一组数据有联系,也就是三个标准差只能反映三个行向量的单独关系
就好比你不能用身高的标准差去衡量体重的,因此引入协方差
协方差是可以决定两组变量的联合变化程度的,首先先见下列式子
由例子计算得到的结果如下,我们针对这三个数据进行观测
首先我们舍弃数学的想法,用常识去分析,在对于同一职业运动员时,此处同一是为了舍弃别的影响,自己想想举重和长跑对比就理解了
身高与体重肯定是正比关系,而身高与年龄确实有很大关系,但是此处例子均为成年运动员,所以实际上关系并不是很大,同时年龄和体重也是一个道理,或者说:
后两个对比,其相关性较弱,在基于常识的理解后,我们根据实际数学结果分析
由三个结果,可以判断身高和体重是正相关关系,因为只有他是正值,同时,后两个小于0,可以说明是负相关的。
也可以说,绝对值的大小判断,后两个的值太小了,相关性比较微弱,此处附带本书的图,通过这个图就可以更加直观的验证了
三、协方差矩阵
由上述的实例学习,就可以明显的知道后两组关系不明显,此时可以使用一个矩阵把这三个变量的关系以一种形式显示出来,也就是协方差矩阵
PS;协方差矩阵是对称的,详细的表达式见下图
补充:若这些事件全部都不相关,也就是说只有对角线有值,其余全为0,根据这个可以进行推导,得到下图的结果,这个分布会跟后面的卡尔曼滤波模型有点关系,下一节再补充
- 2024-02-28
-
发表了主题帖:
控制之美(卷2)第二节——卡尔曼滤波的基础补充
本帖最后由 Zhao_kar 于 2024-2-28 22:46 编辑
控制之美(卷2)第二节——卡尔曼滤波的基础补充
声明:
本书的基础理论部分的数学基础——矩阵部分就不展开了,先从测评计划的主体开始
本节先是一些基础概念的补充,同时设计到概率论的知识
概率论部分就点一下就行了,详细的还是要自己学习
一、卡尔曼滤波器的基本了解
首先卡尔曼滤波是一种最优化的、递归的、数字处理的算法
双特性:滤波和观测
观测可以对系统状态进行估计,而滤波可以对噪声信号进行滤波和去噪声
二、递归算法和数据融合的概念
1、本书使用了一个无人机通过气压计检测高度的实例,但是在理解递归之前,我个人觉得直接对推导过程进行学习更好,这个过程比较容易理解
2、首先是一个小学就会学到的概念:平均值,先假定我们要测定一个数据,需要通过多次测量取平均,此处用Z(k)表示测量后的估计值,其中k表示测量的次数,那么Z(k)就应该=[Z1+Z2+...Z(k)]/k。
(公式如下)
这个是一个平均值的概念,但是这个方法会有两个缺陷:其一是每次运算要做k次加法和一次乘法,乘法还没什么,但是基数非常大的时候,用计算机去加这么多的数可是一个非常麻烦的事情,计算量太大了;其二是需要保存所有的测量数据,那么大基数下也就意味着大量的存储空间。得知缺陷后,下面引入解决方案
3、在前面的等式中,把第k项拿出来,保留到k-1项,拆分成左右两个部分,左边是从1到k-1的和除k,右边是z的k除k。
4、然后在左边的部分中,把加式拿出一个k-1,则原式变成如下。
(公式如下)
5、则加式里面的则为k-1次测量的平均值,那么经过化简,可以得到一个新的代表平均值的一个等式
(3)
6、从这个等式里面可以看出来,只需要两次加法和一次乘法运算,也就是说计算的速度很快(相对比前面要运算k-1个加法运算),同时只需要保留前一次的平均值和当前次数,所以空间也会变小很多。
7、综上,这种方式就是递归算法,具体定义为:通过上次估值得到下一次估值的算法,也就是知道k-1,可以得k。
8、至于数据融合,简单描述+个人理解的语言,就是:假如在基数很大的情况下,也就是k很大的时候,新的测量结果对估计值的影响就不会很大,但是基数很小的时候,若测量结果出现较大误差,那么就会有很大的影响,就比如测身高180cm,然后前一次平均值是178,结果新的结果测出来是170,虽然数学上讲可以通过算法排除这种误差很大的值,但是我们在第五点的等式中,让1/k为一个系数K,定义这个系数为调整系数,而且我们在基数小的时候让他尽量的大,让基数大的时候变小,也就是k变大,K变小。上述即为数据融合。
三、概率论的基础知识
1、期望和方差,说实话,这个部分学校课程都学过了,而且本书没有过多的去究其定义,想知道定义的建议去自行学习,这里放上概率论的一个表述:
(写公式太麻烦了直接放图了)
2、然后是正态分布,这个高中课程就学过了,详细的也不解释了,这个百度一下看看图片就知道了。
3、然后是本书的一个实际例子,解释了误差融合,假定我们现在要用无人机测量高度,且无人机上装载了两个仪器,假定A测定50m,B测定52m。
4、若使用平均值,那么就是51m,同时考虑实际上传感器的误差,设定误差e1=Za-Z1,e2=Za-Z2,其中Z1、2为测定数字,Za为实际无人机高度。
5、此时,假设A服从正态分布1,B服从正态分布2,本书有张图比较直观,这里考虑版权不放出来,先不管那么多,只需要知道正态分布A的期望是0,标准差是1,而B的正态分布的期望是0,标准差是0.5,通过实际的图,也就是百分之99.73,即可知道对于A,误差在47-53m,也就是50的上下3m,而对于B,50.5-53.5m,这些是单独分析的结论。
6、然后接下来是融合,首先融合可以写成
7、其中K可取0-1,若A的精度高于B,也就是标准差小于B,那么可以让K=0,此时z'=z1,同理,反过来时,K=1,z'=z2,居中则让K=0.5
8、同时,因为前面提到了A的标准差大于B,也就是K应当大于0.5且接近于Z2.
9、经过推导,这里推导比较复杂,不做描述,主要是确认最优的参数K
10、可以得到K应该是一个趋近于0.8的值,且此时误差最小,也就是最优估计值,带入可得Z=51.6,标准差为0.45,同时标准差相对之前也变小了。
11、上述就是本书给出的一个两个数据融合的例子,结合新的正态分布曲线,是可以看出来直观的表现的
- 2024-02-22
-
上传了资料:
25k测评的部分工程
- 2024-02-01
-
回复了主题帖:
【Tang Primer25K Dock】七——I2S加麦克风阵列的基本驱动测试
wangerxian 发表于 2024-1-31 18:54
有研究过无线麦克风吗,最近刚好有需求。
额没有,这个帖子也是最近研究麦克风现学的
- 2024-01-29
-
发表了主题帖:
【Tang Primer25K Dock】七——I2S加麦克风阵列的基本驱动测试
本帖最后由 Zhao_kar 于 2024-1-29 19:32 编辑
【Tang Primer25K Dock】七——I2S加麦克风阵列的基本驱动测试
备注:
下一节第八节就是测评计划提到的数字时钟了,更新顺序比较乱,但是都会在备注里面简单说一下
本节理论部分会多一点,毕竟涉及到一个新的概念
事先声明,因为只是简单的测试,实际上的演示效果会比较差
然后补充一下,实际学下来,实际上我数字时钟的代码甚至都没有分层,而这个I2S却是分了I2S的驱动部分和LED的驱动部分两个子模块。
一、I2S的原理
1、首先了解一下基本概念:
I2S(Inter-IC Sound)。I2S是一种用于在集成电路之间传输音频数据的串行通信接口标准。
它通常用于连接音频编解码器、数字信号处理器(DSP)、微控制器和其他音频设备。
I2S接口使用了三个主要的信号线:时钟信号(SCK),帧同步信号(WS或LRCLK),和串行数据线(SD)。
这种接口广泛用于数字音频传输,支持高质量的音频传输和处理。
2、协议介绍:
I2S是一种通过三根线(时钟线、数据线和帧选择线)进行全双工音频通信的协议。
时钟线(SCK)用于同步数据传输。
数据线(SD)传输左右声道的音频数据。
帧选择线(WS,或LRCLK)用于指示是左声道还是右声道的数据。
3、时钟同步:
I2S协议需要时钟的同步,这是通过时钟线(SCK)实现的。所有设备都需要基于相同的时钟频率进行操作。
其中时钟可以是主设备生成,也可以是外部提供。
4、数据格式:
I2S支持不同的数据格式,如标准I2S、左对齐(Left-Justified)和右对齐(Right-Justified)等。
标准I2S数据格式是16位,每个帧有一个左声道和一个右声道的采样数据。
5、帧同步:
I2S协议中的帧同步线(WS)在每个音频帧的开始指示左右声道的数据。
帧同步信号通常以低电平表示左声道,高电平表示右声道。
6、补充:
上述的基本概念中,我们需要重点关注的是I2S的两个时钟,也就是实际麦克风阵列上的WS和MIC_CLK
这里有一个注意的地方:下述两个时钟
主时钟(mclk):频率是采样频率的256倍或384倍
采样时钟(bclk):bclk的频率=2×采样频率×采样位数。
同时,正常人可以听到的声音频率范围为20Hz~20KHz,这个也是一个需要了解的知识
然后,后续代码的时钟分频部分,这里提前说一下,驱动这个模块前,先去看看手册,这里你可以看到详细的时序需求,这也是为什么后面代码乘了个64的原因
然后在官方里面有提到这个部分:所以即可知道时钟的要求,那么我们就可以对他进行分频率
接下来直接看代码部分
二、I2S的子模块部分
module i2s_top(
input clk,//输入时钟
input rst_n,//复位信号
output reg [23:0] ldata,//输出左声道数据
output reg [23:0] rdata,//输出右声道数据
output ready,//数据就绪一次给一个脉冲
output mic_sck,//输出麦克风时钟,连麦克风阵列mic_sck
output mic_ws,//输出麦克风左右声道信号,连麦克风阵列mic_ws
input mic_sd//麦克风输入
);
//对时钟分频
localparam div_clk = 50;
localparam div_ws = div_clk * 64;
reg [15:0] sck_cnt;
reg [21:0] ws_cnt;
reg [23:0] shift_reg_l;
reg [23:0] shift_reg_r;
assign mic_sck = (sck_cnt > div_clk/2-1) ? 1'b1 : 1'b0;
assign mic_ws = (ws_cnt > div_ws/2-1) ? 1'b1 : 1'b0;
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
sck_cnt <= 0;
end
else begin
if(sck_cnt == div_clk-1) begin
sck_cnt <= 0;
end
else begin
sck_cnt <= sck_cnt + 1;
end
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
ws_cnt <= 0;
end
else begin
if(ws_cnt == div_ws-1) begin
ws_cnt <= 0;
end
else begin
ws_cnt <= ws_cnt + 1;
end
end
end
//根据I2S时序采集数据,并通过shift_reg更新
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
shift_reg_l <= 0;
shift_reg_r <= 0;
end
else begin
case(ws_cnt)
div_clk*1+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*2+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*3+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*4+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*5+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*6+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*7+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*8+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*9+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*10+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*11+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*12+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*13+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*14+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*15+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*16+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*17+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*18+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*19+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*20+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*21+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*22+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*23+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*24+div_clk/2-1: begin shift_reg_l <= {shift_reg_l[22:0],mic_sd}; shift_reg_r <= shift_reg_r; end
div_clk*33+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*34+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*35+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*36+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*37+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*38+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*39+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*40+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*41+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*42+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*43+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*44+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*45+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*46+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*47+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*48+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*49+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*50+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*51+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*52+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*53+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*54+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*55+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
div_clk*56+div_clk/2-1: begin shift_reg_l <= shift_reg_l; shift_reg_r <= {shift_reg_r[22:0],mic_sd}; end
default: begin shift_reg_l <= shift_reg_l; shift_reg_r <= shift_reg_r; end
endcase
end
end
//根据时序逻辑将shift_reg最终输出为左右声道音频值
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
ldata <= 0;
rdata <= 0;
end
else begin
if(ws_cnt == div_clk*56+div_clk/2) begin
ldata <= shift_reg_l;
rdata <= shift_reg_r;
end
else begin
ldata <= ldata;
rdata <= rdata;
end
end
end
assign ready = (ws_cnt == div_clk*56+div_clk/2) ? 1'b1 : 1'b0;//就绪脉冲
endmodule
三、顶层模块部分
module top(
input clk50M,//系统输入时钟,H11
input rst_n,//系统复位信号,低电平复位
output mic_sck,//输出麦克风时钟,连麦克风阵列mic_sck
output mic_ws,//输出麦克风左右声道信号,连麦克风阵列mic_ws
input mic_sd0,//麦克风输入,连mic_d0
input mic_sd1,//麦克风输入,连mic_d1
input mic_sd2,//麦克风输入,连mic_d2
input mic_sd3,//麦克风输入,连mic_d3
output led_sck,//输出灯带时钟,连led_ck
output led_dat//输出灯带数据,连led_da
);
wire signed [23:0] mic_data00;//麦克风1数据(有符号整数)
wire signed [23:0] mic_data01;//麦克风2数据(有符号整数)
wire signed [23:0] mic_data10;//麦克风3数据(有符号整数)
wire signed [23:0] mic_data11;//麦克风4数据(有符号整数)
wire signed [23:0] mic_data20;//麦克风5数据(有符号整数)
wire signed [23:0] mic_data21;//麦克风6数据(有符号整数)
wire signed [23:0] mic_data3;//麦克风7数据(有符号整数)
wire [23:0] abs_mic_data00;//麦克风1数据绝对值
wire [23:0] abs_mic_data01;//麦克风2数据绝对值
wire [23:0] abs_mic_data10;//麦克风3数据绝对值
wire [23:0] abs_mic_data11;//麦克风4数据绝对值
wire [23:0] abs_mic_data20;//麦克风5数据绝对值
wire [23:0] abs_mic_data21;//麦克风6数据绝对值
wire [23:0] abs_mic_data3;//麦克风7数据绝对值
//对接收到的麦克风数据取绝对值
assign abs_mic_data00 = (mic_data00 >= 0) ? $unsigned(mic_data00) : $unsigned(-mic_data00);
assign abs_mic_data01 = (mic_data01 >= 0) ? $unsigned(mic_data01) : $unsigned(-mic_data01);
assign abs_mic_data10 = (mic_data10 >= 0) ? $unsigned(mic_data10) : $unsigned(-mic_data10);
assign abs_mic_data11 = (mic_data11 >= 0) ? $unsigned(mic_data11) : $unsigned(-mic_data11);
assign abs_mic_data20 = (mic_data20 >= 0) ? $unsigned(mic_data20) : $unsigned(-mic_data20);
assign abs_mic_data21 = (mic_data21 >= 0) ? $unsigned(mic_data21) : $unsigned(-mic_data21);
assign abs_mic_data3 = (mic_data3 >= 0) ? $unsigned(mic_data3 ) : $unsigned(-mic_data3 );
reg [11:0] dir;//12个灯亮灭控制寄存器
reg [31:0] set_cnt;//灯带刷新计数寄存器
reg [2:0] comp_state;//麦克风强度比较状态机state
reg [31:0] maixv;//麦克风最大强度值缓存
reg [2:0] maixn;//强度麦克风序号缓存
reg [31:0] maxv;//麦克风最大强度值
reg [2:0] maxn;//强度麦克风序号
//例化麦克风驱动,麦克风1 2
i2s_top i2s_u0(
.clk(clk50M),
.rst_n(rst_n),
.ldata(mic_data00),
.rdata(mic_data01),
.ready(),
.mic_sck(mic_sck),
.mic_ws(mic_ws),
.mic_sd(mic_sd0)
);
//例化麦克风驱动,麦克风3 4
i2s_top i2s_u1(
.clk(clk50M),
.rst_n(rst_n),
.ldata(mic_data10),
.rdata(mic_data11),
.ready(),
.mic_sck(),
.mic_ws(),
.mic_sd(mic_sd1)
);
//例化麦克风驱动,麦克风5 6
i2s_top i2s_u2(
.clk(clk50M),
.rst_n(rst_n),
.ldata(mic_data20),
.rdata(mic_data21),
.ready(),
.mic_sck(),
.mic_ws(),
.mic_sd(mic_sd2)
);
//例化麦克风驱动,麦克风7
i2s_top i2s_u3(
.clk(clk50M),
.rst_n(rst_n),
.ldata(),
.rdata(mic_data3),
.ready(),
.mic_sck(),
.mic_ws(),
.mic_sd(mic_sd3)
);
//灯带驱动
led12 led12_u(
.clk(clk50M),
.rst_n(rst_n),
.direct(dir),
.bgr(24'h0000fc),//颜色红色 蓝:24'hfc0000 绿:24'h00hc00
.refresh(1'b1),
.led_sck(led_sck),
.led_dat(led_dat)
);
//比较7个麦克风的声音强度,获取最强的麦克风序号
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n) begin
comp_state <= 0;
maixv <= 0;
maixn <= 0;
maxv <= 0;
maxn <= 0;
end
else begin
case(comp_state)
0: begin
if(abs_mic_data00 > maixv) begin
maixv <= abs_mic_data00;
maixn <= 1;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
1: begin
if(abs_mic_data01 > maixv) begin
maixv <= abs_mic_data01;
maixn <= 2;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
2: begin
if(abs_mic_data10 > maixv) begin
maixv <= abs_mic_data10;
maixn <= 3;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
3: begin
if(abs_mic_data11 > maixv) begin
maixv <= abs_mic_data11;
maixn <= 4;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
4: begin
if(abs_mic_data20 > maixv) begin
maixv <= abs_mic_data20;
maixn <= 5;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
5: begin
if(abs_mic_data21 > maixv) begin
maixv <= abs_mic_data21;
maixn <= 6;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
6: begin
if(abs_mic_data3 > maixv) begin
maixv <= abs_mic_data3;
maixn <= 7;
end
else begin
maixv <= maixv;
maixn <= maixn;
end
comp_state <= comp_state + 1;
end
7: begin
maxv <= maixv;
if(maixv>9000)//设置个阈值,声音太小全都忽略,都不亮
maxn <= maixn;
else
maxn <= 0;
maixv <= 0;
maixn <= 0;
comp_state <= 0;
end
default: begin
comp_state <= 0;
end
endcase
end
end
//根据最强的麦克风序号亮相应的指示灯
always @(posedge clk50M or negedge rst_n) begin
if(~rst_n) begin
set_cnt <= 0;
dir <= 0;
end
else begin
if(set_cnt == 2699999) begin
set_cnt <= 0;
case(maxn)
0: dir <= 12'b000000000000;
1: dir <= 12'b000000000001;
2: dir <= 12'b000000000100;
3: dir <= 12'b000000010000;
4: dir <= 12'b000001000000;
5: dir <= 12'b000100000000;
6: dir <= 12'b010000000000;
7: dir <= 12'b111111111111;
default: dir <= 12'b000000000000;
endcase
end
else begin
set_cnt <= set_cnt + 1;
end
end
end
endmodule
四、实际视频演示
PS:因为比较简单,所以实际演示效果并不好,且中间那个麦克风的声音最大时,全部的灯都会亮,加上延时问题,会出现经常全部亮的情况,可以在判别的部分降低频率进行测试,之前试过,就不演示了
[localvideo]13f511feb76faeb66aa9e6d3a87a5fd2[/localvideo]
- 2024-01-28
-
发表了主题帖:
【Tang Primer25K Dock】六——SK9822的测试
本帖最后由 Zhao_kar 于 2024-1-28 23:23 编辑
【Tang Primer25K Dock】六——SK9822的测试
备注:
在I2S的测试前,因为麦克风阵列的模块上有SK9822这个led,所以I2S的数据要通过SK9822来展示,所以这一节先把这个灯的测试写了
这个灯的驱动比较简单,本节就分两个部分,一个原理+一个代码和演示
本节主要代码来自于别人开源的,如果侵权了请帖子回复,最后I2S的实际测试跟这个不一样
一、SK9822的原理和功能
1、基本信息
首先SK9822是一种RGB LED控制芯片,支持红、绿、蓝三种颜色。
然后在通信协议上,一般SK9822采用串行通信协议,类似于WS2812B或APA102这种LED,同时串行通信使得多个LED可以通过单一信号线级联,以简化控制。
一般可以通过单片机来控制,但只要通过发送特定格式的数据,就可以以控制LED的颜色和亮度。
电源要求一般跟常见的led一样,SK9822通常工作在低电压(例如5V)下,因此供电电压一般需要符合规定的工作范围,也就是3.3-5。
由于支持串行通信,SK9822非常适合制作灵活的LED照明效果,也就是这个视频里面演示出来的效果。
至于格式,一般发送给SK9822的数据通常是按照一定格式排列的,以表示红、绿、蓝三个通道的亮度值,以及可能的其他控制信息。
2、详细资料和模块资料
首先本次测试需要的是这个麦克风阵列上面的12个灯
然后上面的七个麦克风在下一节的I2S会用到
官网有相关的资料,可以自己去看看
硬件连接如果不需要麦克风,实际上只需要连接电源和那个led的时钟线和数据线就可以了
二、驱动代码
1、顶层文件(其实就一个文件)
module top (
input clk, // Clock
output reg sk9822_ck,
output reg sk9822_da
);
parameter SD9822_NUM = 12;//串联灯珠数量
parameter FRAME_LEN = 32;//数据帧长度
//起始帧
parameter START_FRAME = 32'H00000000;
//结束帧
parameter END_FRAME = 32'HFFFFFFFF;
//数据帧
parameter LED_LIGHT = 5'B01111; //全局亮度
reg [23:0] data_rgb = 24'h000001; //具体颜色
wire [31:0] data_frame = {3'b111,LED_LIGHT,data_rgb};
assign data_frame = {3'b111,LED_LIGHT,data_rgb};
//clk降频
parameter CLK_FRE = 50_000_000;
reg [23:0] clk_delay;
wire clk_slow = clk_delay[13];
always@(posedge clk) clk_delay <= clk_delay + 1;
//状态机
reg [31:0] send_frame;
reg [6:0] send_frame_cnt = 0;
reg [4:0] send_bit_cnt = 0;
reg send_bit;
always@(posedge clk_slow)
if(!sk9822_ck)begin //上升沿发送
sk9822_ck <= 1;
end
else begin //下降沿取值
sk9822_ck <= 0;
sk9822_da <= send_frame[(FRAME_LEN - 1) - send_bit_cnt];
send_bit_cnt <= send_bit_cnt + 1;
if(send_bit_cnt == FRAME_LEN - 1)
send_frame_cnt <= (send_frame_cnt < (SD9822_NUM + 1))? send_frame_cnt + 1 : 0;
end
//数据控制
always@(*)
if(send_frame_cnt == 0)
send_frame = START_FRAME;
else if(send_frame_cnt == (SD9822_NUM + 1))
send_frame = END_FRAME;
else
send_frame = data_frame;
//颜色转移
always@(posedge clk_slow)
if(send_frame_cnt ==(SD9822_NUM + 1) & send_bit_cnt == 0)
data_rgb <= {data_rgb[22:0],data_rgb[23]};//24位RGB移位显示
endmodule
2、约束文件,只需要两个引脚就行了
IO_LOC "sk9822_da" K5;
IO_PORT "sk9822_da" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "sk9822_ck" L5;
IO_PORT "sk9822_ck" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "clk" E2;
IO_PORT "clk" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
3、视频演示
[localvideo]11aade52c399cdfafa5ccb3499fa00db[/localvideo]
-
回复了主题帖:
【Tang Primer25K Dock】五——数码管的硬件设计+基本测试
忘记补充引脚约束文件了,见下
//Copyright (C)2014-2023 Gowin Semiconductor Corporation.
//All rights reserved.
//File Title: Physical Constraints file
//GOWIN Version: V1.9.9 Beta-5
//Part Number: GW5A-LV25MG121NES
//Device: GW5A-25
//Device Version: A
//Created Time: Sat 01 27 22:38:45 2024
IO_LOC "seg[6]" K7;
IO_PORT "seg[6]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[5]" L7;
IO_PORT "seg[5]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[4]" L10;
IO_PORT "seg[4]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[3]" L9;
IO_PORT "seg[3]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[2]" J8;
IO_PORT "seg[2]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[1]" F7;
IO_PORT "seg[1]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "seg[0]" J11;
IO_PORT "seg[0]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[5]" L8;
IO_PORT "wei[5]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[4]" K10;
IO_PORT "wei[4]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[3]" K9;
IO_PORT "wei[3]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[2]" K8;
IO_PORT "wei[2]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[1]" F6;
IO_PORT "wei[1]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "wei[0]" J10;
IO_PORT "wei[0]" PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
IO_LOC "sw[2]" G8;
IO_PORT "sw[2]" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "sw[1]" H8;
IO_PORT "sw[1]" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "sw[0]" H5;
IO_PORT "sw[0]" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "sys_rst_n" F5;
IO_PORT "sys_rst_n" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;
IO_LOC "sys_clk" E2;
IO_PORT "sys_clk" PULL_MODE=NONE DRIVE=OFF BANK_VCCIO=3.3;