暂时未有相关云产品技术能力~
目录1 什么是毕业设计?2 毕设时间节点3 毕业论文基本格式3.1 中文摘要页3.2 英文摘要页3.3 目录页3.4 标题3.5 公式3.6 表格3.7 插图3.8 参考文献4 常见问题(Q&A)1 什么是毕业设计?什么是毕业设计?什么是毕业论文?二者有什么区别?先说说二者的概念。毕业设计 是普通高校学生毕业前总结性的独立作业,是实践性教学最后一个环节。旨在检验学生综合运用所学理论、知识和技能解决实际问题的能力。在教师指导下,学生就选定的课题进行工程设计和研究,包括设计、计算、绘图、工艺技术、经济论证以及合理化建议等,最后提交一份报告论文。毕业论文 是对某一专业领域的现实问题或理论问题进行 科学研究探索的具有一定意义的书面报告,用以培养学生进行社会调查研究;文献资料收集、阅读和整理、使用;提出论点、综合论证、总结写作等基本技能。简言之毕业设计≈实践项目+实验报告,一般实验性强的农、工、医科都采用毕业设计毕业论文,一般理论性强的理、文科采用论文形式毕业设计中的实验报告相当于毕业论文,但一般需要附上工程图纸、实验图表等进行论证和说明。考虑到CSDN平台上大多数是工科专业的,在不致混淆的情况下,本文认为毕业设计等价于毕业论文。2 毕设时间节点开题阶段重点检查是否落实“一人一题”的原则,学生进行毕业设计的条件是否具备,能否正常开展工作。学生或指导教师撰写毕业设计任务书,学生撰写并提交开题报告。中期检查着重检查毕业设计环节中的学风、工作进度、教师指导情况,对存在问题采取改进措施,部分高校要提交中期检查表。毕业答辩重点检查学生毕业答辩资格审查情况及学生毕业设计成果的规范化情况,考察学生的工程化表达能力。上面提到的三个文书概念总结如下,本文不展开序号 书面材料 解释1 毕业设计任务书 完成毕业设计的准备工作,进度安排,教学主管签字2 开题报告 毕业设计前期的预研报告,一般包括项目背景和文献综述3 中期检查 核对当前毕业设计进度是否符合预期,以及对下阶段工作的展望3 毕业论文基本格式3.1 中文摘要页课题名称:小二号,黑体,加粗,居中,行距18磅,段前0.5行,段后0.5行。上下各空一行摘要:四号,黑体,居中。行距18磅。段前0.5行,段后0.5行关键词:3~5个,五号宋体。逗号分开,最后一个关键词后面无标点符号。摘要正文:300字左右,五号宋体,首行缩进2个汉字符。行距18磅。3.2 英文摘要页英文课题名称:小二号,Times New Roman,加粗,居中,行距18磅,段前0.5行,段后0.5行。上下各空一行。ABSTRACT:四号Times New Roman 居中,段前0.5行,段后0.5行。行距18磅。其余与中文摘要要求相同3.3 目录页目录:换页,上下各空一行;四号黑体居中,目录2字中间空1格,段前0.5行,段后0.5行。行距18磅目录项:五号宋体(英文Times New Roman),单倍行距3.4 标题1级标题:换页,空一行;四号,黑体(英文Times New Roman),居中,行距18磅,段前0.5行,段后0.5行2级标题:五号,黑体(英文Times New Roman),顶格,序号与题名之间空一格,行距18磅,段前0.5行,段后0.5行3级标题:五号,黑体(英文Times New Roman),缩进2个汉字符书写序号,序号与题名之间空一格,行距18磅,段前0.5行,段后0.5行3.5 公式公式应另起一行,正文中的公式、算式或方程式等应编排序号,公式的编号用圆括号括起,序号标注于该式所在行(当有续行时,应标注于最后一行)的行末。公式按章节顺序编号,如(2.2)表示第二章的第二个公式。公式序号必须连续,不得重复或跳缺。重复引用的公式不得另编新序号。公式和编号之间不加虚线。公式:五号,宋体(英文Times New Roman),使用公式编辑器,居中,式号右对齐。公式行行距1.5倍,段前0行,段后0行计算式及正文中涉及的物理量和变量,应为斜体;元素符号、记号及运算符号,应为正体。3.6 表格表序:写在表题左方不加标点,空一格写表题,表题末尾不加标点,表格逐章编序,表序必须连续,如表4.4表示第四章的第四个表。表题:小五,宋体(英文Times New Roman),居中置于表上方,行距18磅,段前0行,段后0行。表格:表格上下与正文之间各空一行;采用三线表,两端与页面对齐;表中文字:小五,宋体 (英文Times New Roman),行距18磅,段前0行,段后0行。3.7 插图插图:应有图序和图题,全文插图以章分组编序号,图序必须连续,不得重复或跳缺。如图4.1表示第四章的第一幅图。图居中,上下与正文之间各空一行图题:小五,宋体(英文Times New Roman),居中置于图下方,行距18磅,段前0行,段后0行图中文字:小五,宋体(英文Times New Roman),行距1倍,段前0行,段后0行3.8 参考文献期刊:[1] 陈桂娥,樊行雪,许振良.线性滴定中稳定常数测定方法比较[J].华东理工大学学报,1996,22(5): 620-625.图书:[2] 蒋有绪,郭泉水,马娟,等.中国森林群落分类及其群落学特征[M].北京:科学出版社,1998.论文集 | 会议录:[3] 雷光春.综合湿地管理:综合湿地管理国际研讨会论文集[C].北京:海洋出版社,2012.报告:[4] 孔宪京,邹德高,徐斌,等.台山核电厂海水库护岸抗震分析与安全性评价研究报告[R].大连:大连理工大学工程抗震研究所,2009.学位论文:[5] 王燕.氨基酸–金属离子体系的测定[D].上海:同济大学,2009.专利文献:[6] 刘加林.多功能一次性压舌板:中国,92214985.2[P]. 1993-04-01.标准:[7] 全国信息与文献标准化技术委员会.文献著录:第4部分 非书资料: GB/T 3792.4-2009[S].北京:中国标准出版社,2010:3.电子文献:[8] 萧钮.出版业信息化迈人快车道[EB/OL].(2001-12-19)[2002-04-15].http://www.creader. com/news/20011219/200112190019.html.4 常见问题(Q&A)罗列一些毕业设计中常遇到的问题,欢迎留言补充Q:毕业论文写多少字比较好?A:最低字数要求是15000字,一般的字数区间在15000字~25000字,不建议为了卷而堆砌字数,要注重工程图表的表达。Q:毕业论文重复率的要求?A:关于查重的问题,可以参考下表重复率 R R R 对应R ≤ 25 % R\le25\% R≤25% 正常25 % < R ≤ 35 % 25\%< R\le35\% 25%<R≤35% 偏高,需修改35 % < R ≤ 70 % 35\%< R\le70\% 35%<R≤70% 严重偏高,需修改复检70 % < R 70\%< R 70%<R 异常,可质疑抄袭如何降低重复率?这里推荐一个近邻词汇检索网站Q:参考文献可以乱标吗?A:本着尊重学术的心,肯定不能乱标。但对于初入科研的本科毕设,一般论文的参考文献是不会仔细审查的。在实际撰写论文的时候,可以用word的尾注工具进行超链接,防止错序Q:毕设选题有什么注意事项?A:选题和立项必须进行预研。所谓预研就是对项目的技术路线、实现效果、工作量进行调研,其中重点考察的是技术路线,举例而言,对于一套机器人控制系统,在前期应该要思考清楚大概采用什么控制算法、硬件上需要什么传感器和执行机构,各个组件如何通信等,不用具体,只需要大致的框架即可。关于论文的每个部分——摘要、工作对比、实验、总结展望该怎么写,论文里好看的图表怎么画,今后再开文章讨论。🔥 更多精彩专栏:《机器人原理与技术》《ROS从入门到精通》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 混乱的Python库2 什么是Anaconda?3 Anaconda的安装3.1 Windows系统3.2 Linux系统3.3 测试4 虚拟环境管理(速查字典)1 混乱的Python库你有没有遇到过这样的问题在项目A中需要用到某个Python库PkgA,且项目A的其他库要求PkgA的版本必须为v3.0以上,你按要求安装了PkgA v3.0;过了一段时间,老板交给你一个项目B,又用到了PkgA,但这次其他库要求PkgA的版本必须为v2.0及以上,这时候你怎么办?安装PkgA v3.0则新项目B无法运行,安装PkgA v2.0则旧项目A无法运行,要想同时在一个环境里使用两个项目,必须不停地重装PkgA来更换版本。上面的例子只涉及两个项目的一个依赖库冲突,如果多个项目呢?如果多个依赖冲突呢?上面的例子说明了什么呢?其实就是Python语言的痛点:依赖网复杂Python的包非常丰富,轮子相当多,开发者在工作时难免会调用这样或那样的包,久而久之,一个功能依赖另一个功能,形成复杂的依赖网络包管理混乱通过报错信息不断安装依赖包终于解决了依赖库的问题,但随之而来的就是版本问题,也就是上面例子所体现的依赖冲突,本质上是某个包开发时的不向下兼容导致的为了解决上面的问题,更好地管理Python库,让其扬长避短,就必须使用环境管理工具,例如本文介绍的Anaconda。2 什么是Anaconda?Anaconda是一个开源的跨平台Python发行版本,支持WindowsmacOSLinux操作系统。Anaconda中包含了conda等180多个科学包及其依赖项。其中conda则是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。3 Anaconda的安装进入Anaconda下载界面选择相应的操作系统,本文主要介绍在Windows与Linux下的安装流程。3.1 Windows系统Windows有图形化的安装向导,按下面的步骤一步步安装即可运行安装向导选择I Agree选择All Users,其实选Just Me也可以,但这台主机的其他用户就无法使用Anaconda了选择安装路径保持默认选项等待安装结束配置环境变量3.2 Linux系统对于Linux系统,没有图形化的安装界面,按下面输入终端命令即可进入Anaconda安装目录并运行官方安装程序bash ./Anaconda3-2021.11-Linux-x86_64.sh添加环境变量,其中~/Project/anaconda3/bin替换成自己的安装目录echo 'export PATH="~/Project/anaconda3/bin:$PATH"' >> ~/.bashrc source ~/.bashrc3.3 测试打开cmd(Windows)或Terminal(Linux),输入conda --version如果输出版本号则说明安装成功,如下所示。注意,若运行python脚本时仍然是原环境而非Anaconda环境,则需要注意配置编辑器的python解释器路径。VSCode中,在tasks.json中的args参数中配置{ "version": "2.0.0", "tasks": [ { "label": "catkin_make:debug", "type": "shell", "command": "catkin_make", "args": ["-DPYTHON_EXECUTABLE=/home/winter/Project/anaconda3/envs/server/bin/python "], "group": {"kind":"build","isDefault":true}, "presentation": { "reveal": "always" }, "problemMatcher": "$msCompile" } ] }4 虚拟环境管理(速查字典)用Anaconda可以创建虚拟环境,虚拟环境间彼此隔离,可以解决依赖混乱的情况。虚拟环境管理主要涉及以下的命令,可以作为速查字典以备不时之需创建虚拟环境conda create -n test python=3.8创建了一个名为test的采用3.8版本Python解释器的虚拟环境切换虚拟环境conda activate test切换到名为test的虚拟环境。默认地,用户会进入Anaconda自带的base环境,注意base环境已经与安装Anaconda前的环境不同,因此第一次使用Anaconda可能会产生依赖冲突和缺失。查看虚拟环境conda env list依赖安装与卸载# 安装 conda install pkg pip install pkg # 卸载 conda remove pkg pip uninstall pkg这里推荐使用清华源加快安装速度,使用方法是pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pkg==version 即安装了名为pkg,版本为version的包如果依赖很多,建议使用requirements.txt批量配置,命令为pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt查看环境依赖conda list复制虚拟环境conda env export > test_env.yaml conda env create -f test_env.yaml常用于导出当前虚拟环境的信息或复制虚拟环境删除虚拟环境conda remove -n test --all删除名为test的虚拟环境🔥 更多精彩专栏:《机器人原理与技术》《ROS从入门到精通》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录0 拍照的死亡角度1 透视相机模型2 相机矩阵3 镜头畸变0 拍照的死亡角度拍照死亡角度一般指的是将自己脸盆子拍得特别大,拍出用鼻孔看人的狰狞面目,比如下面这张照片。在拍照的死亡角度下,镜头畸变会产生失真,让你的体态更圆润、厚实,因此部分美颜软件里人像修正中会有“去畸变”这一项功能。本文就从计算机视觉的角度谈一谈镜头畸变原理。1 透视相机模型透视相机模型描述了三维空间中的点与二维图像平面上像素间的映射关系。根据小孔成像原理,透视相机的成像点与空间点之间方向相反,如图所示。为得到与空间点方向相同的成像点,将成像面沿着光轴移动到归一化成像面,接下来的讨论以归一化成像面为准。在确定归一化成像面后,从空间点 W X ~ ^W\!\tilde{X} W X~ 到像素点 u ~ \tilde{u} u~ 的映射可分为两步:将 W X ~ ^W\!\tilde{X} W X~ 映射到归一化成像面的成像点 C x ~ ^C\!\tilde{x} C x~ ,对应的映射关系称为相机外参(Extrinsic);将 C x ~ ^C\!\tilde{x} C x~ 映射到像素平面的像素 u ~ \tilde{u} u~ ,对应的映射关系称为相机内参(Intrinsic)。上述各点均处于投影空间,采用齐次坐标。2 相机矩阵设世界坐标系 { W } \left\{ \boldsymbol{W} \right\} {W}相对于透视相机坐标系 { C } \left\{ \boldsymbol{C} \right\} {C}的位姿为W C T = [ W C R C p w 0 0 1 ] _{\boldsymbol{W}}^{\boldsymbol{C}}\!\boldsymbol{T}=\left[ \right]WC T=[ WC R0 C p w 01 ]考虑到从三维空间降维到二维平面,设世界坐标值 [ W X W Y W Z 1 ] T \left[ \right] ^T [ W X W Y W Z 1 ] T ,则C x ~ = [ 1 0 0 0 0 1 0 0 0 0 1 0 ] , W C T W X ~ = [ C X C Y C Z ] ^{\boldsymbol{C}}\!\tilde{x}=\left[ \right] {\color{white} ,}_{\boldsymbol{W}}^{\boldsymbol{C}}\!\boldsymbol{T}^{\,\,\boldsymbol{W}}\!\!\tilde{X}=\left[ \right]C x~ = ⎣⎡ 100 010 001 000 ⎦⎤ , WC T W X~ = ⎣⎡ C XC YC Z ⎦⎤在上述推导过程中,称M E = [ 1 0 0 0 0 1 0 0 0 0 1 0 ] , W C T = [ W C R C p w 0 ] \boldsymbol{M}_{\boldsymbol{E}}=\left[ \right] {\color{white} ,}_{\boldsymbol{W}}^{\boldsymbol{C}}\!\boldsymbol{T}=\left[ \right]M E = ⎣⎡ 100 010 001 000 ⎦⎤ , WC T=[ WC R C p w 0 ]为相机外参矩阵。通常令尺度因子 C Z = 1 ^{\boldsymbol{C}}\!Z=1 C Z=1, C x ~ = [ C X C Z C Y C Z 1 ] T = [ C x ^ C y ^ 1 ] T ^{\boldsymbol{C}}\!\tilde{x}=\left[ \right] ^T=\left[ \right] ^T C x~ =[ C ZC X C ZC Y 1 ] T =[ C x^ C y^ 1 ] T从 C x ~ ^C\!\tilde{x} C x~ 到像素 u ~ \tilde{u} u~ 的映射用三维内参矩阵 K K K来表示,即u ~ = K C x ~ = [ f u s c u 0 f v c v 0 0 1 ] C x ~ \tilde{u}=\boldsymbol{K}^C\!\tilde{x}=\left[ \right] \,\,^C\!\tilde{x}u~ =K C x~ = ⎣⎡ f u00 sf v0 c uc v1 ⎦⎤ C x~其中如图(a)所示,参数 c u c_u c u 、 c v c_v c v 用于中心映射——将光轴与归一化成像面的交点,即成像面中心点映射到像素平面中心,其取决于拜耳阵列与光轴如何对齐。如图(b)所示,参数 f u f_u f u 、 f v f_v f v 用于归一化——将矩形的传感器阵列映射为正方形如图©所示,参数 s s s用于正交化——当传感器阵列不正交或传感器平面与光轴不垂直时,需要引入 纠正畸变。3 镜头畸变上述透视相机模型基于针孔无限小的假设,但在现实中,光线通过镜头将使相机内部复杂化,产生明显的径向失真——场景中的线条在图像中显示为曲线。径向畸变(Radial Distortion)有两种类型:筒体畸变(Barrel Distortion)枕形失真(Pincushion Distortion)此外由于相机组装过程中,透镜不能和成像面严格平行,会引入切向畸变(Tangential Distortion)。针对具有镜头畸变的相机,不能直接应用透视相机模型,通常需要先通过多项式模型修正这种几何偏差,即:{ x ^ = x ( 1 + κ 1 r 2 + κ 2 r 4 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) y ^ = y ( 1 + κ 1 r 2 + κ 2 r 4 ) + p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y{ x^ =x(1+κ 1 r 2 +κ 2 r 4 )+2p 1 xy+p 2 (r 2 +2x 2 )y^ =y(1+κ 1 r 2 +κ 2 r 4 )+p 1 (r 2 +2y 2 )+2p 2 xy其中 r 2 = x 2 + y 2 r^2=x^2+y^2 r 2 =x 2 +y 2 , κ 1 \kappa _1 κ 1 、 κ 2 \kappa _2 κ 2 称为径向畸变参数, p 1 p_1 p 1 、 p 2 p_2 p 2 称为切向畸变参数; ( x , y ) (x,y) (x,y)为畸变坐标, ( x ^ , y ^ ) \left( \hat{x},\hat{y} \right) ( x^ , y^ )为无畸变坐标。🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《ROS从入门到精通》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
🔥 作者:Mr.Winter🔥 简介:主攻机器人与人工智能领域的理论研究和工程应用,业余丰富各种技术栈。主要涉足:【机器人(ROS)】【机器学习】【深度学习】【计算机视觉】目录第一章·语法1 数值2 字符串3 标识符4 关键字5 任务和函数6 编译引导语句7 基本数据类型第二章·建模1 结构建模2 数据流建模3 行为建模4 FSM建模第三章·案例1 循环彩灯控制器2 红外入侵传感器第一章·语法1 数值verilog中数值有以下表示方法(1) 逻辑值(2) 整数整数可以标明位数也可不标明位数,通用表示方法为:位数 ′ 基数数值 \text{位数}'\text{基数} \text{数值}位数 ′ 基数数值其中位数表明该数用二进制的几位来表示;基数为数值使用的码制——可以是二(b)、八(o)、十(d)或十六(h)进制;数值可以是所选基数的任何合法值包括不定值 X X X和高阻态 Z Z Z。例如:8’b11010001、32’H080A_FFFF、48(3) 位扩展对于所有小于位宽的数值,在其位区间内用0填充高位;除非该数值为不定值 X X X或高阻态 Z Z Z,此时高位用相应的 X X X或 Z Z Z扩展。所谓位区间就是基数码制中,一个数占用的比特数,例如二进制位区间为1,十六进制为4。2 字符串字符串常用于系统测试时,表示命令内需要显示的信息;通常不用于硬件建模。字符串是用“”括起来的一行字符,其内可以用C语言中的各种格式控制符(转义符),如“\n”、“\t”等;也可以用C语言中的各种数值型控制符,如:%b、%o、%d、%h、%t、%s等。3 标识符所谓标识别符就是用户为程序描述中的Verilog对象——如模块名、端口名、实例名等,所起的名字。标识符必须以英文字母或下划线起头,最长可以达到1023个字符。此外,Verilog语言是大小写敏感的。4 关键字关键字是Verilog语言中预设的用于描述架构的词,所有关键字都是小写字母,如:module、endmodule、always、reg、wire、if、else等。5 任务和函数任务和函数具备将程序中反复调用的语句结构聚合起来的能力,因其可在过程块中被调用(而模块不行),因此可以有效的简化程序结构。利用任务和函数可以把一个大的程序模块分解成多个小的任务和函数,利于调试。任务和函数的共同点是:语句中不能出现过程引导的语句,且其中无法描述时序电路,只能描述组合电路。任务结构基本格式task <任务名>; 端口及数据类型声明语句; 过程语句; endtask调用格式<任务名> (端口1,端口2,…,端口N)函数结构基本格式function <位宽范围声明> 函数名; 输入端口及数据类型声明语句; 过程语句; 函数名= ***; //该句是作为函数的返回值,不可缺 endfunction调用格式X = <函数名> (端口1,端口2,…,端口N)任务与函数的不同点在于:(a) 函数只能与主模块共用同一个仿真时间,而任务可以定义自己的仿真时间;(b) 函数不能调用任务,但任务能调用其他任务和函数;(c) 函数至少要有一个输入变量,而任务可以没有或有多个输入变量;(d) 函数显式返回一个值,而任务则隐式返回值——或理解为处理值;(e) 任务需要定义输入和输出,而函数只需定义输入,函数名就是输出下面给出两个实例:module TASKDEMO (S,D,C1,D1,C2,D2); input S; input[3:0] C1,D1,C2,D2; output [3:0] D; reg[3:0] out1,out2; task CMP; //任务定义,任务名CMP input [3:0] A,B; output[3:0] DOUT; begin if(A>B) DOUT=A; end endtask always @(C1) begin CMP(C1,D1,out1); //一次调用任务 CMP(C2,D2,out2); //二次调用任务 endmodule module CN( input [3:0]A, output [2:0]OUT ); function[2:0] GP; input[3:0] M; reg [2:0]CNT,N; begin CNT = 0; for(N=0;N<=3;N=N+1) if(M[N]==1) CNT=CNT+1; GP = CNT; end endfunction assign OUT = (~|A) ? 0:GP(A); endmodule此外还有一系列系统任务和函数,以符号$起头。系统任务/函数含义$time找到当前的仿真时间$display、$monitor显示和监视信号值的变化$stop暂停仿真$finish结束仿真6 编译引导语句编译引导语句用主键盘左上角小写键`起头,用于指导仿真编译器在编译时采取一些特殊处理,编译引导语句一直保持有效,直到被取消或重写。编译引导语句不会生成硬件电路。常用编译引导 含义`define 用于宏定义`include 用于包含其它模块,便于层次化系统设计`timescale 用于说明程序中的时间单位和仿真精度,必须放在模块边界前面。例如:`timescale 1ns/100ps表示下面模块中所有的时间单位都是1ns的整数倍。尽可能使仿真精度与时间单位接近,前者是由所有参加仿真模块中由`timescale指定的精度最高(即时间最短)的决定。特殊符号“#”常用来表示延迟,其延迟单位由时间单位决定。如上例中指定的模块若有# 10,则表示延迟10ns`uselib 用于定义仿真器到哪里去找库元件`resetall 用于把所有设置的编译引导恢复到缺省状态7 基本数据类型(1) Nets(网络连接) 表示器件之间的物理连接wire(缺省)、tri:对应于标准的互连线supply1、supply2:对应于电源线或接地线wor、trior:对应于有多个驱动源的线或逻辑连接wand、triand:对应于有多个驱动源的线与逻辑连接trireg:对应于有电容存在能暂时存储电平的连接tri1、tri0:对应于需要上拉或下拉的连接(2) Register(寄存器/变量) 表示抽象的储存单元reg:无符号整数变量,可以选择不同位宽integer:有符号32位宽整数变量,算术运算可产生2的补码real:有符号双精度浮点数time:无符号64位宽整数变量(Verilog-XL仿真工具用64位正数记录仿真时刻)(3) Parameters(参数)常用参数来声明运行时的常数,参数是本地的,其定义只在本模块内有效 可用字符串表示的任何地方,都可以用定义的参数来代替。第二章·建模1 结构建模描述系统的具体结构组成、由哪些子模块组合而成以及如何互连。具体而言,采用模块互相调用、组合的方式进行建模。2 数据流建模描述系统中信号/数据在线路中的流向,即一个或几个信号、数据如何从一个线路传递到另一个线路上。具体而言,采用assign连续赋值方式建模。3 行为建模按高级语言的思路对一个系统的功能或工作行为过程进行描述。具体而言,采用always或initial引导过程块方式建模。4 FSM建模在Verilog中引入FSM建模的优势在于:(a) FSM是高效的顺序控制模型——克服了纯硬件数字系统顺序方式控制不灵活的缺点;(b) 容易利用现成的EDA优化工具;(c) 性能稳定。状态机容易构成性能良好的同步时序逻辑模块;(d) 设计实现效率高。状态机的HDL表述丰富多样、程序层次分明、易读易懂;(e) 高速性能及高可靠性。第三章·案例1 循环彩灯控制器问题描述:设计一个循环彩灯控制器。要求控制红、绿、黄发光管循环发亮。红发光管亮2秒,绿发光管亮3秒,黄发光管亮1秒。设系统时钟为10MHz。//@ Attention:时钟频率10MHz module circle_lights( input clk, input rst, output reg [1:0] lights ); /******************* 变量定义区 **********************/ reg [1:0] state; reg [1:0] next_state; reg [25:0] cnt; `define S_red 2'b00 //红灯 `define S_green 2'b01 //绿灯 `define S_yellow 2'b10 //黄灯 `define sec_2 26'd2000_0000 //2s `define sec_5 26'd5000_0000 //5s `define sec_6 26'd6000_0000 //6s /***************************************************/ //state register block always @ (posedge clk) begin if(!rst||(cnt==`sec_6)) begin state<=`S_red; cnt<=0; end else begin state <= next_state; cnt <= cnt+1; end end //next state logic always @(state or cnt) begin case(state) `S_red: begin if(cnt==`sec_2) next_state = `S_green; //持续2s else next_state = `S_red; end `S_green: begin if(cnt==`sec_5) next_state = `S_yellow; //持续3s else next_state = `S_green; end `S_yellow: begin if(cnt==`sec_6) next_state = `S_red; //持续1s else next_state = `S_yellow; end endcase end //output logic always@(state or cnt) begin case(state) `S_red: lights = 2'b00; `S_green: lights = 2'b01; `S_yellow: lights = 2'b10; endcase end endmodule2 红外入侵传感器用FSM设计一个入侵警报系统 Verilog 代码:启用键,进入防护状态解除键,从其他状态返回到解除防护状态测试键,进入测试状态,报警当传感器输出为“1”持续5秒,警报器报警。//@ Attention:时钟频率10MHz module alarm( input clk, input [3:0] btn, //0->进入防护状态 1->解除防护 2->报警 3->复位 //one_hot编码 output reg beep ); /******************* 变量定义区 **********************/ reg [1:0] state; reg [1:0] next_state; reg [25:0] cnt; `define S_init 2'b00 //解除防护状态 `define S_defence 2'b01 //防护状态 `define S_alarm 2'b10 //报警状态 `define sec_5 26'd5000_0000 //5s /***************************************************/ //state register block always @ (posedge clk) begin if(btn == 4'b1000) begin state<=`S_init; cnt<=0; end else begin state <= next_state; cnt <= cnt+1; end end //next state logic always @(state or btn) begin if(btn == 4'b0001) next_state = `S_defence; else if(btn == 4'b0100) begin next_state = `S_alarm; cnt = 0; //开始5s计时 end else next_state = `S_init; end //output logic always@(state or btn) begin if(state == `S_alarm) begin if(cnt == `sec_5) begin beep = 0; next_state = `S_defence; end else beep = 1; end else beep = 0; end endmodule 测试模块代码: module test_alarm( ); reg clk; reg [3:0] btn; wire beep; alarm my_test(clk,btn,beep); always #50 clk <= ~clk; initial begin clk = 1'b0; btn <= 4'b1000; #55 btn <= 4'b0001; end endmodule🔥 更多精彩专栏:《机器人原理与技术》《ROS从入门到精通》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 分类2 邻域滤波2.1 线性滤波2.1.1 方框滤波2.1.2 高斯滤波2.2 非线性滤波3 频域滤波3.1 低通滤波3.2 高通滤波1 分类图像滤波按图像域可分为两种类型:邻域滤波(Spatial Domain Filter),其本质是数字窗口上的数学运算。一般用于图像平滑、图像锐化、特征提取(如纹理测量、边缘检测)等,邻域滤波使用邻域算子——利用给定像素周围像素值以决定此像素最终输出的一种算子频域滤波(Frequency Domain Filter),其本质是对像素频率的修改。一般用于降噪、重采样、图像压缩等。按图像频率滤除效果主要分为两种类型:低通滤波。滤除原图像的高频成分,即模糊图像边缘与细节。高通滤波。滤除原图像的低频成分,即图像锐化。导入原图和噪图import cv2,skimage import numpy as np # 原图 srcImg = cv2.imread("test.jpg") cv2.imshow("src image", srcImg) # 给图像增加高斯噪声 noiseImg = skimage.util.random_noise(srcImg, mode='gaussian') cv2.imshow("image with noise", noiseImg)其中噪声可选gaussian:高斯加性噪声localvar:高斯加性噪声,每点具有特定局部方差poisson: 泊松分布噪声salt:盐噪声,随机用1替换像素pepper:胡椒噪声,随机用0或-1替换像素s&p:椒盐噪声,呈现出黑白杂点2 邻域滤波2.1 线性滤波线性邻域滤波,指像素的输出值取决于输入区域像素的加权和,下面介绍常见的线性滤波算子。2.1.1 方框滤波方框滤波(Box Filter),其核函数为:K e r = 1 α [ 1 1 ⋯ 1 1 1 ⋯ 1 ⋮ ⋮ ⋱ ⋮ 1 1 ⋯ 1 ] Ker\,\,=\,\,\frac{1}{\alpha}\left[11⋮111⋮1⋯⋯⋱⋯11⋮111⋯111⋯1⋮⋮⋱⋮11⋯1\right]Ker= α1 ⎣⎢⎢⎢⎡ 11⋮1 11⋮1 ⋯⋯⋱⋯ 11⋮1 ⎦⎥⎥⎥⎤其中 α = { 1 K e r S i z e , N o r m a l i z e = T r u e 1 , N o r m a l i z e = F a l s e \alpha ={1KerSize,Normalize=True1,Normalize=False{1KerSize,Normalize=True1,Normalize=Falseα={ KerSize1 ,Normalize=True1,Normalize=False非归一化的方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(Dense Optical Flow Algorithms)中用到的图像倒数的协方差矩阵。归一化的方框滤波则为均值滤波(Blur),即邻域平均法——用一片图像区域各个像素的均值来代替原图像中的各个像素值。均值滤波用于图像平滑,但其在降噪的同时也破坏了图像的边缘细节,从而使图像变得模糊,降噪能力较差。进行方框滤波# 方框滤波 boxImg = cv2.boxFilter(noiseImg, ddepth = -1, ksize = (2, 2), normalize = False) cv2.imshow("box Image", boxImg) # 均值滤波 blurImg = cv2.blur(noiseImg, (6, 5)) cv2.imshow("blur image", blurImg)若将方框滤波核设为(6,5)且归一化,则效果与均值滤波相同。2.1.2 高斯滤波高斯滤波(Gauss Filter)基于二维高斯核函数G ( x , y , σ ) = 1 2 π σ 2 e − x 2 + y 2 2 σ 2 G\left( x,y,\sigma \right) =\frac{1}{2\pi \sigma ^2}e^{-\frac{x^2+y^2}{2\sigma ^2}}G(x,y,σ)= 2πσ 21 e − 2σ 2x 2 +y 2具有在保持细节的条件下进行噪声滤波的能力,因此广泛应用于图像降噪中,但其效率比均值滤波低。高斯滤波器有两个特征量:核大小,其决定了图像的平滑范围。理论上,高斯核函数应该无穷大,以达到最佳的平滑效果,但过大的卷积核会导致运算效率骤降。根据高斯函数 3 σ 3\sigma 3σ规则,可以取高斯核大小为 ( 6 σ + 1 ) × ( 6 σ + 1 ) \left( 6\sigma +1 \right) \times \left( 6\sigma +1 \right) (6σ+1)×(6σ+1);离散程度 σ \sigma σ,其决定了对高频成分的抑制程度。 σ \sigma σ越大,像素加权半径越大,平滑程度越强。# 高斯滤波 gaussImg = cv2.GaussianBlur(noiseImg, (5, 5), 0) cv2.namedWindow("gaussain image") cv2.imshow("gaussain image", gaussImg)2.2 非线性滤波虽然线性滤波器易于构造且计算效率高,但有些情况下,使用邻域像素的非线性滤波效果更好。例如,若图像具有椒盐噪声而非高斯噪声,此时对图像高斯滤波并不会去除噪声像素,只是把噪声转换为更为柔和但仍然可见的颗粒。中值滤波(Median filter)是一种基于排序统计理论的典型非线性滤波技术,核心原理是用像素点邻域灰度值中值代替该像素点的灰度值。中值滤波对脉冲噪声、椒盐噪声尤为有效,且具有边缘保护特性。中值滤波器本质上是数字窗口内的非线性取中值运算,而非线性滤波器的加权运算,因此中值滤波没有卷积核,运算效率仅有线性滤波的1/5左右。# 原图 srcImg = cv2.imread("test.jpg") cv2.imshow("src image", srcImg) # 给图像增加椒盐噪声 noiseImg = skimage.util.random_noise(srcImg, mode='s&p') cv2.imshow("image with noise", noiseImg) medImg = cv2.medianBlur(np.uint8(noiseImg * 255), 3) cv2.namedWindow("median image") cv2.imshow("median image", medImg)中值滤波对椒盐噪声效果3 频域滤波通过傅里叶变换将图像变换到频域,即可在频域进行图像处理。根据傅里叶变换的对称性以及从低频到高频的排列规则,图像原始频域图像会在四角形成低频分量区,而形成高频中心。通常为了观察方便,变换算法(例如Matlab中的fftshift)会将低频分量移动到图像中心形成低频中心图像傅里叶变换代码如下# 傅里叶变换 dft = cv2.dft(np.float32(grayImg), flags = cv2.DFT_COMPLEX_OUTPUT) # 将图像中的低频部分移动到图像的中心 dftShift = np.fft.fftshift(dft) # 计算幅频特性 magnitude = 20 * np.log(cv2.magnitude(dftShift[:, :, 0], dftShift[:, :, 1])) plt.subplot(121), plt.imshow(grayImg, cmap = 'gray') plt.title('原图'), plt.xticks([]), plt.yticks([]) plt.subplot(122), plt.imshow(magnitude, cmap = 'gray') plt.title('频谱图'), plt.xticks([]), plt.yticks([]) plt.show()3.1 低通滤波# 定义滤波掩码 def mask(img, ftype): crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2) # 求得图像的中心点位置 # 低通 if ftype == 'low': mask = np.zeros((img.shape[0], img.shape[1], 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # 高通 if ftype == 'high': mask = np.ones((img.shape[0], img.shape[1], 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 0 return mask lowImg = dftShift * mask(grayImg, 'low') lowImg = np.fft.ifftshift(lowImg) lowImg = cv2.idft(lowImg) lowImg = cv2.magnitude(lowImg[:, :, 0], lowImg[:, :, 1])3.2 高通滤波# 定义滤波掩码 def mask(img, ftype): crow, ccol = int(img.shape[0] / 2), int(img.shape[1] / 2) # 求得图像的中心点位置 # 低通 if ftype == 'low': mask = np.zeros((img.shape[0], img.shape[1], 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # 高通 if ftype == 'high': mask = np.ones((img.shape[0], img.shape[1], 2), np.uint8) mask[crow-30:crow+30, ccol-30:ccol+30] = 0 return mask highImg = dftShift * mask(grayImg, 'high') highImg = np.fft.ifftshift(highImg) highImg = cv2.idft(highImg) highImg = cv2.magnitude(highImg[:, :, 0], highImg[:, :, 1])完整代码关注下方公众号回复 CV002 获取🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《ROS从入门到精通》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录👉 什么是VSCode?👉 VSCode的下载安装👉 VSCode插件集合🍉 美观类1 CodeSnap2 :emojisense:3 Marquee4 Material Theme5 Bookmarks6 Bracket Pair Colorizer7 vscode-icons🍉 编程类(前端)1 Vetur2 Auto Close Tag3 Auto Rename Tag4 formate: CSS/LESS/SCSS formatter5 Live Server6 Debugger for Chrome🍉 编程类(C/C++)1 C/C++2 C++ Intellisense3 CMake Tools🍉 编程类(Python)1 Python2 Jupyter🍉 效率类1 Git Graph2 koroFileHeader3 Code Runner4 Better Comments5 CodeTime👉 什么是VSCode?VSCode(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全、代码重构、查看定义功能,并且内置了命令行工具和Git版本控制系统。用户可以更改主题和键盘快捷方式实现个性化设置,也可以通过内置的扩展程序商店安装扩展以拓展软件功能。VSCode默认支持非常多的编程语言,包括JavaScript、TypeScript、CSS和HTML;也可以通过下载扩展支持Python、C/C++、Java 和Go在内的其他语言。VSCode也支持调试 Node.js 程序。VSCode支持同时打开多个目录,并将信息保存在工作区中以便复用。作为跨平台的编辑器,VSCode允许用户更改文件的代码页、换行符和编程语言。👉 VSCode的下载安装Windows打开VSCode官网直接下载即可。LinuxLinux平台下安装VSCode速度很慢,可以用以下方法:通过国内源下载VSCode将压缩包解压并移动到usr/local目录:sudo mv VSCode-linux-x64 /usr/local/获取运行权限:sudo chmod +x /usr/local/VSCode-linux-x64/code安装VSCode(注意此处只能用普通用户权限): /usr/local/VSCode-linux-x64/code创建软链接: sudo ln -s /usr/local/VSCode-linux-x64/code /usr/local/bin/vscode, 之后在任意位置可通过终端输入vscode启动软件👉 VSCode插件集合🍉 美观类1 CodeSnapCodeSnap可以轻松生成高分辨率,精美的代码图片使用方式很简单:使用命令将 Codesnap 调出复制要作为输出的一部分的代码保存图像到本地2 :emojisense::emojisense:可以为MarkDown文档或命令行输出表情,让编程更有趣直接复制Emoji大全里喜欢的标签即可,例如下面我给数据集训练过程的命令行增加了表情。3 MarqueeMarquee可以将编程主屏幕改造得更实用或更美观。有了这个扩展,你可以看新闻报道,天气!此外,Marquee 支持用户将项目添加到个人待办事项列表中,以及一些快速笔记的暂存板。一些小但有用的插件功能。4 Material ThemeMaterial Theme为 VScode 安装不同的主题,允许你根据自己的喜好自定义整个编辑器的外观。下面是我个人使用的FireFly Pro主题。5 BookmarksBookmarks允许你在工作区中针对不同文件的特定行向代码添加书签。6 Bracket Pair ColorizerBracket Pair Colorizer给括号加上不同的颜色,便于区分不同的区块。7 vscode-iconsvscode-icons给VSCode文件更换更好看的图标。🍉 编程类(前端)1 VeturVetur为Vue项目提供语法高亮、智能感知等。2 Auto Close TagAuto Close Tag自动闭合HTML/XML标签。3 Auto Rename TagAuto Rename Tag自动重命名HTML/XML标签。4 formate: CSS/LESS/SCSS formatterformate: CSS/LESS/SCSS formatter格式化CSS/LESS/SCSS以增强可读性。5 Live ServerLive Server为静态和动态页面启动具有实时重新加载功能的开发本地服务器。6 Debugger for ChromeDebugger for Chrome将JS代码的调试嵌入Chrome浏览器。🍉 编程类(C/C++)1 C/C++C/C++为VSCode添加了对C/C++的语言支持,包括 IntelliSense 和Debugging等功能。2 C++ IntellisenseC++ Intellisense为VSCode添加了对C/C++的智能感知。3 CMake ToolsCMake Tools为VSCode添加了对CMake的支持与感知。🍉 编程类(Python)1 PythonPython为VSCode添加了对Python的语言支持,包括 IntelliSense 和Debugging等功能。2 JupyterJupyter为VSCode添加了对Jupyter Notebook的功能支持。🍉 效率类1 Git GraphGit Graph可视化你的Git提交流程。2 koroFileHeaderkoroFileHeader用于格式化生成文件头部注释和函数注释。3 Code RunnerCode Runner用于直接运行多种语言的代码片段或文件。4 Better CommentsBetter Comments为代码注释提供各种特定类型注释的高亮。5 CodeTimeCodeTime提供你每天敲代码时间的数据分析。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
🔥 作者:FrigidWinter🔥 简介:主攻机器人与人工智能领域的理论研究和工程应用,业余丰富各种技术栈。主要涉足:【机器人(ROS)】【机器学习】【深度学习】【计算机视觉】🔥 专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》…目录0 电脑环境1 安装Ubuntu双系统1.1 刻录系统引导盘1.2 在Windows下创建空白分区1.3 重启电脑进入安装界面1.4 图形化安装Ubuntu2 配置Ubuntu系统2.1 分辨率、亮度、无线网卡设置2.2 显卡驱动安装2.3 设置GRUB启动项2.4 安装中文输入法3 卸载Ubuntu系统3.1 查看Ubuntu分区情况3.2 删除Ubuntu分区3.3 删除开机引导项3.4 修复Windows-Grub附录附1 搜索旧版本NVIDIA驱动程序0 电脑环境512G+1T双硬盘。2060独显。主要考虑Ubuntu下装显卡驱动的问题。BIOS模式为UEFI。Win+R快捷键进入"运行",输入msinfo32回车,出现以下界面,可查看BIOS模式。BIOS模式有传统的MBR模式和新式UEFI模式,这将对安装双系统产生直接影响。若你的电脑属于传统MBR模式,强烈建议重装Windows系统来更新BIOS模式到UEFI。本文基于UEFI模式安装双系统。BIOS模式为UEFI1 安装Ubuntu双系统1.1 刻录系统引导盘(1) 下载Ubuntu镜像在Ubuntu版本发布中下载要装的系统版本映像.iso,本文下载ubuntu-20.04.3-desktop-amd64.iso(2) 打开刻录软件UltralSO找到镜像(3) 刻录引导文件UltralSO菜单栏选择启动->写入硬盘映像,界面如下。检查U盘盘符和映像文件无误后,格式化硬盘,再写入映像即可。1.2 在Windows下创建空白分区打开【磁盘管理】为ubuntu分配空间(1)单硬盘情形直接选择最后一个盘(比如CD两个盘的最后一个是D盘,以此类推,在该盘点击右键,选择压缩卷,并分配压缩空间。(2)双硬盘情形需要先在C盘分出200~500M的空白分区用来安装ubuntu的启动项,然后再在另一块硬盘选择最后一个盘分配空间。这样做的原因在于:电脑开机时会自动在系统盘所在硬盘搜索启动项以启动系统,为使ubuntu启动项也能被搜索到,需要将启动项安装在这里,我们可以在开机时手动选择进入哪个系统。分配好的情况(双硬盘情形)1.3 重启电脑进入安装界面(1)关闭securate boot若不disable会出错(2)SATA Config配置Ubuntu安装过程中,一般要设置SATA Config为AHCI模式,否则将出现读不出安装类型和磁盘,无法分区的现象,但是将SATA Config配置为AHCI模式后,将无法进入Windows系统(如果是第一次配置),这是因为在IDE模式下配置的Windows并没有AHCI的驱动。解决方案:先调回IDE模式确保能进入Windows10,然后shift+重启选择安全模式,在安全模式进系统前,提前进入BIOS将SATA Config重新配置成AHCI,这样Win10可以自动搜索并安装AHCI驱动,再次重启即可以AHCI模式进入Ubuntu和Windows10两个系统。(3)插好系统盘,重启电脑根据自己电脑按相应的键进boot manager,找到USB启动项,回车即可进入。1.4 图形化安装Ubuntu(1)选择安装(2)安装模式点击安装Ubuntu和为图形或无线硬件,以及MP3和其他媒体安装第三方软件。(3)分区选择手动分区点击空闲盘符,点击+进行分区,如下:efi:如果是单硬盘,在唯一的空闲分区上添加,大小200~500M,逻辑分区,空间起始位置,用于efi;如果是双硬盘,找到事先分好的200~500M空闲分区添加,逻辑分区,空间起始位置,用于efiswap:Ubuntu的虚拟内存,一般为电脑物理内存的2倍左右,可以将其分为8G,逻辑分区,空间起始位置,用于"swap"或"交换空间"/:Ubuntu根目录,用于安装系统和软件,相当于windows的系统盘,我们将其分为50G,主分区,空间起始位置,用于"ext4日志文件系统",挂载点为//home:相当于windows的其他盘,剩下空间全分给它,逻辑分区,空间起始位置,用于"ext4日志文件系统",挂载点为/home(4)安装启动项在分区界面下方,选择安装启动项的位置为划分的启动分区编号,例如本文选择了525M的/dev/nvme0n1p7为启动分区,这里就选这个,之后点击Install Now安装完成后重启即可。2 配置Ubuntu系统2.1 分辨率、亮度、无线网卡设置若Ubuntu的Linux内核版本太低会出现亮度无法调节、分辨率无法设置、没有WIFI图标等诡异情况,这里推荐首先升级内核。首先查看Ubuntu的Linux内核版本uname -r一般Ubuntu16的Linux内核版本都较低,此时需要升级内核;Ubuntu18、Ubuntu20则没有这些问题。进入Linux内核下载一个需要的版本,大家可以先看下文无线网卡驱动来确定内核版本,本文选择5.10内核。到ubuntu软件下载的目录下,运行sudo dpkg linux-i *.deb reboot即可成功升级内核。此时重启计算机后,一般可以解决问题,但不升级内核按网络上其他方法却很难调节。若系统还是无法出现WIFI标志,考虑安装无线网卡驱动,这里需要根据PC的显卡型号来下载,本文的无线网卡是Inter AX201 160MHz,因此到Inter无线网卡驱动官网下载可以看到该驱动要求Linux 5.2+驱动下载完毕后,到下载目录执行cp iwlwifi-*.ucode /lib/firmware接着到firmware驱动下载并安装firmware驱动,重启计算机就能看到WIFI标志。还可能遇到一个异常:检测到WIFI但始终连不上。有方案通过修改网络设置中有线网络的IP、掩码等解决,但本机实测无效。解决方案仍然是更换Linux内核版本。2.2 显卡驱动安装(1) 图形化安装适合Ubuntu20.04的用户搜索Additional Drivers直接选择驱动,点击Apply Changes安装即可。重启设备后输入nvidia-smi出现以下界面则安装成功(2) .run模式安装进入NVIDIA官网下载对应显卡版本的驱动。删除原有NVIDIA驱动#对于通过PPA以及Ubuntu自带的软件商店卸载指令如下 sudo apt-get remove --purge nvidia* sudo apt-get remove --purge nvidia-* #对于通过官网.run文件,卸载指令如下 sudo chmod +x *.run sudo ./NVIDIA-Linux-x86_64-470.57.02.run --uninstall apt-get purge nvidia* apt-get autoremove禁用nouveau驱动sudo gedit /etc/modprobe.d/blacklist.conf文末添加blacklist nouveau blacklist lbm-nouveau options nouveau modeset=0 alias nouveau off alias lbm-nouveau off更新echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf update-initramfs -u重启检查nouveau驱动是否禁用成功reboot # 验证(无信息返回即可) lsmod | grep nouveau按ctrl+alt+f1进入字符界面正式安装# 关闭图形界面 sudo service lightdm stop # 授权run文件 sudo chmod a+x NVIDIA-Linux-x86_64-xxx.xx.run # 安装(禁用opengl以避免出现循环登陆的问题) sudo ./NVIDIA-Linux-x86_64-xxx.xx.run -no-x-check -no-nouveau-check -no-opengl-files 安装过程的选项The distribution-provided pre-install script failed! Are you sure you want to continue?yesWould you like to register the kernel module souces with DKMS? This will allow DKMS to automatically build a new module, if you install a different kernel later?NoNvidia’s 32-bit compatibility libraries?NoWould you like to run the nvidia-xconfigutility to automatically update your x configuration so that the NVIDIA x driver will be used when you restart x? Any pre-existing x confile will be backed up.Yes打开图形界面并检查安装情况sudo service lightdm start nvidia-smi nvidia-settings若报错:NVIDIA-SMI has failed because it couldn‘t communicate with NVIDIA driver. Make sure that the latest driver is installed and running则sudo apt-get install dkms sudo dkms install -m nvidia -v xxx.xx2.3 设置GRUB启动项在Ubuntu中sudo gedit /etc/default/grub按照自己的需求更改GRUB_DEFALUT=x,x是希望默认的启动项,避免每次启动都进入Ubuntu系统。GRUB_TIMEOUT_STYLE,是否显示倒计时,hidden的属性表示不会显示倒计时GRUB_TIMEOUT:等待时间,单位是秒GRUB_GFXMODE:屏幕的显示像素修改完成后sudo update-grub对刚才的设置进行更新。2.4 安装中文输入法(1) 安装中文包sudo apt-get update sudo apt-get install language-pack-zh-hans(2) 安装输入法sudo apt install ibus-libpinyin sudo apt install ibus-clutter安装完成后重启系统。(3) 配置选择智能拼音3 卸载Ubuntu系统3.1 查看Ubuntu分区情况进入Ubuntu,打开终端输入sudo fdisk -l查看磁盘信息如图所示,该磁盘安装Ubuntu的部分为/dev/nvme1n1p6~/dev/nvme1n1p8,分别为Linux交换分区和文件系统。3.2 删除Ubuntu分区(1) 打开磁盘管理软件DiskGenius(2) 找到对应Ubuntu的Linux分区磁盘1的交换分区和文件系统磁盘0的启动分区(3) 右键【删除分区】,并点击【磁盘】—>【保存分区表】(4) 删除后打开【磁盘管理】检查分区情况,如下图所示为成功。多出了删除的空间3.3 删除开机引导项删除了Ubuntu系统所有分区后,Windows的EFI分区里仍然会有Ubuntu系统的引导项,不删除它的话开机时Ubuntu系统将仍然存在(虽然无法访问)。(1) win+R打开cmd命令行,输入以下命令diskpart list disk(2) 选择Windows所在磁盘并确定Windows的EFI分区本文Windows位于磁盘0select disk 0 list partition该磁盘的分区信息,EFI分区一般是260M(3) 为EFI分区分配盘符盘符不可与已有盘符重复,注意防止与U盘、驱动器等外设重名,这里分配为J盘select partition 1 assign letter=J此时打开我的电脑可以看到新的盘符(4) 通过记事本删除Ubuntu引导文件由于权限不够,不能直接打开该磁盘,用管理员权限运行记事本,左上角【文件】—>【打开】,选择刚刚新增的J盘,删除Ubuntu文件夹删除Ubuntu文件夹(5) 删除刚刚分配的盘符remove letter=J3.4 修复Windows-Grub若没有完全删除Ubuntu的相关信息,此时重启电脑将无法正常进入Windows系统,而进入Grub界面。GNU/Grub界面接下来开始修复Windows-Grub开机时(出现电脑商标之前)按F2键进入boot setup界面,通过方向键选定选择Windows Boot Manager,进入Windows系统。建议:重新通过DiskGenius筛查漏掉的ubuntu启动文件,若未果,继续尝试下面的方法。打开【磁盘管理】确认磁盘分区形式,本文为GPT对于GPT格式,下载EasyUEFI打开软件,选择【管理EFI启动项】,删除Ubuntu,重启电脑。附录附1 搜索旧版本NVIDIA驱动程序进入NVIDIA官网选择与自己的电脑显卡匹配的配置,点击开始搜索,默认结果只会出现目前最新的10个该显卡的驱动程序版本。快捷键F12打开控制台,在控制台输入如下代码后回车:SystemScanner.prototype.DriverSearch = function(psid, pfid, osID, langCode, whql, beta, dltype, numresults ) {numresults=60;this.scannerStatusUpdate(GFE_SERVER_CONNECTING);theScanner.scannedDevice.downloadInfo=new Object();var parameters='psid='+psid;parameters+='&pfid='+pfid;parameters+='&osID='+osID;parameters+='&languageCode='+langCode;parameters+='&beta='+beta;parameters+='&isWHQL='+whql;parameters+="&dltype="+dltype;parameters+="&sort1=0";parameters+="&numberOfResults="+numresults;var requestUrl=this.driverManualLookupUrl+parameters;this.driversLogUIEvent("warn","SUID:"+this.tracker.scanID+" BEGIN DriverSearch requestUrl:"+requestUrl);this.debugTrace(requestUrl);jQuery.ajax({url:requestUrl,async:false,type:'get',success:function(response){try{theScanner.debugTrace("The Driver Lookup Service Returned:\n\n("+response+")");if(response.length>0){theScanner.resetResults();var driverLookupJsonObj='('+response+')';theScanner.resultsList=new Object();theScanner.resultsList=eval(driverLookupJsonObj)}if(theScanner.resultsList.Success==0){theScanner.scannerStatus="No driver available"}else{theScanner.scannerStatus="Results Ready"}}catch(e){this.driversLogUIEvent("error"," FAIL catch DriverSearch");theScanner.resetResults();theScanner.scannerStatus="No driver available"}},error:function(response){theScanner.resetResults();theScanner.scannerStatus="AJAX Call failed"}});this.driversLogUIEvent("warn","SUID:"+this.tracker.scanID+" END DriverSearch requestUrl:"+requestUrl);}当搜索请求的驱动数据数量g过多时,就会出现请求长时间等待,甚至导致访问超时,此时需要修改numresults。然后在当前页面,再次点击开始搜索。结果就会显示出匹配显卡的全部驱动程序版本。
目录1 引例2 牛顿迭代算法求根3 牛顿迭代优化4 代码实战:Logistic回归1 引例给定如图所示的某个函数,如何计算函数零点 x 0 x_0 x 0 ?在数学上我们如何处理这个问题?最简单的办法是解方程 f ( x ) = 0 f(x)=0 f(x)=0,在代数学上还有著名的零点判定定理如果函数 y = f ( x ) y= f(x) y=f(x)在区间 [ a , b ] [a,b] [a,b]上的图象是连续不断的一条曲线,并且有 f ( a ) ⋅ f ( b ) < 0 f(a)·f(b)<0 f(a)⋅f(b)<0,那么函数 y = f ( x ) y= f(x) y=f(x)在区间 ( a , b ) (a,b) (a,b)内有零点,即至少存在一个 c ∈ ( a , b ) c∈(a,b) c∈(a,b),使得 f ( c ) = 0 f(c)=0 f(c)=0,这个 c c c也就是方程 f ( x ) = 0 f(x)= 0 f(x)=0的根。然而,数学上的方法并不一定适合工程应用,当函数形式复杂,例如出现超越函数形式;非解析形式,例如递推关系时,精确的方程解析一般难以进行,因为代数上还没发展出任意形式的求根公式。而零点判定定理求解效率也较低,需要不停试错。因此,引入今天的主题——牛顿迭代法,服务于工程数值计算。2 牛顿迭代算法求根记第 k k k轮迭代后,自变量更新为 x k x_k x k ,令目标函数 f ( x ) f(x) f(x)在 x = x k x=x_k x=x k 泰勒展开:f ( x ) = f ( x k ) + f ′ ( x k ) ( x − x k ) + o ( x ) f\left( x \right) =f\left( x_k \right) +f'\left( x_k \right) \left( x-x_k \right) +o(x)f(x)=f(x k )+f ′ (x k )(x−x k )+o(x)我们希望下一次迭代到根点,忽略泰勒余项,令 f ( x k + 1 ) = 0 f(x_{k+1})=0 f(x k+1 )=0,则x k + 1 = x k − f ( x k ) f ′ ( x k ) x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)}x k+1 =x k − f ′ (x k )f(x k )不断重复运算即可逼近根点。在几何上,上面过程实际上是在做 f ( x ) f(x) f(x)在 x = x k x=x_k x=x k 处的切线,并求切线的零点,在工程上称为局部线性化。如图所示,若 x k x_k x k 在 x 0 x_0 x 0 的左侧,那么下一次迭代方向向右。若 x k x_k x k 在 x 0 x_0 x 0 的右侧,那么下一次迭代方向向左。3 牛顿迭代优化将优化问题转化为求目标函数一阶导数零点的问题,即可运用上面说的牛顿迭代法。具体地,记第 k k k轮迭代后,自变量更新为 x k x_k x k ,令目标函数 f ( x ) f(x) f(x)在 x = x k x=x_k x=x k 泰勒展开:f ( x ) = f ( x k ) + f ′ ( x k ) ( x − x k ) + 1 2 f ′ ′ ( x k ) ( x − x k ) 2 + o ( x ) f\left( x \right) =f\left( x_k \right) +f'\left( x_k \right) \left( x-x_k \right) +\frac{1}{2}f''\left( x_k \right) \left( x-x_k \right) ^2+o(x)f(x)=f(x k )+f ′ (x k )(x−x k )+ 21 f ′′ (x k )(x−x k ) 2 +o(x)两边求导得f ′ ( x ) = f ′ ( x k ) + f ′ ′ ( x k ) ( x − x k ) f'\left( x \right) =f'\left( x_k \right) +f''\left( x_k \right) \left( x-x_k \right)f ′ (x)=f ′ (x k )+f ′′ (x k )(x−x k )令 f ′ ( x k + 1 ) = f ′ ( x k ) + f ′ ′ ( x k ) ( x k + 1 − x k ) = 0 f'\left( x_{k+1} \right) =f'\left( x_k \right) +f''\left( x_k \right) \left( x_{k+1}-x_k \right) =0 f ′ (x k+1 )=f ′ (x k )+f ′′ (x k )(x k+1 −x k )=0,从而得到x k + 1 = x k − f ′ ( x k ) f ′ ′ ( x k ) x_{k+1}=x_k-\frac{f'\left( x_k \right)}{f''\left( x_k \right)}x k+1 =x k − f ′′ (x k )f ′ (x k )对于向量 x = [ x 1 x 2 ⋯ x d ] T \boldsymbol{x}=\left[x1x2⋯xdx1x2⋯xd\right] ^T x=[ x 1 x 2 ⋯ x d ] T ,将上述迭代公式推广为x k + 1 = x k − [ ∇ 2 f ( x k ) ] − 1 ∇ f ( x k ) {\boldsymbol{x}_{k+1}=\boldsymbol{x}_k-\left[ \nabla ^2f\left( \boldsymbol{x}_k \right) \right] ^{-1}\nabla f\left( \boldsymbol{x}_k \right) }x k+1 =x k −[∇ 2 f(x k )] −1 ∇f(x k )其中 ∇ 2 f ( x k ) \nabla ^2f\left( \boldsymbol{x}_k \right) ∇ 2 f(x k )是Hessian矩阵,当其正定时可以保证牛顿优化算法往 减小的方向迭代牛顿法的特点如下:① 以二阶速率向最优点收敛,迭代次数远小于梯度下降法,优化速度快;梯度下降法的解析参考图文详解神秘的梯度下降算法原理(附Python代码)②学习率为 [ ∇ 2 f ( x k ) ] − 1 \left[ \nabla ^2f\left( \boldsymbol{x}_k \right) \right] ^{-1} [∇ 2 f(x k )] −1 ,包含更多函数本身的信息,迭代步长可实现自动调整,可视为自适应梯度下降算法;③ 耗费CPU计算资源多,每次迭代需要计算一次Hessian矩阵,且无法保证Hessian矩阵可逆且正定,因而无法保证一定向最优点收敛。在实际应用中,牛顿迭代法一般不能直接使用,会引入改进来规避其缺陷,称为拟牛顿算法簇,其中包含大量不同的算法变种,例如共轭梯度法、DFP算法等等,今后都会介绍到。4 代码实战:Logistic回归import pandas as pd import numpy as np import os import matplotlib.pyplot as plt import matplotlib as mpl from Logit import Logit ''' * @breif: 从CSV中加载指定数据 * @param[in]: file -> 文件名 * @param[in]: colName -> 要加载的列名 * @param[in]: mode -> 加载模式, set: 列名与该列数据组成的字典, df: df类型 * @retval: mode模式下的返回值 ''' def loadCsvData(file, colName, mode='df'): assert mode in ('set', 'df') df = pd.read_csv(file, encoding='utf-8-sig', usecols=colName) if mode == 'df': return df if mode == 'set': res = {} for col in colName: res[col] = df[col].values return res if __name__ == '__main__': # ============================ # 读取CSV数据 # ============================ csvPath = os.path.abspath(os.path.join(__file__, "../../data/dataset3.0alpha.csv")) dataX = loadCsvData(csvPath, ["含糖率", "密度"], 'df') dataY = loadCsvData(csvPath, ["好瓜"], 'df') label = np.array([ 1 if i == "是" else 0 for i in list(map(lambda s: s.strip(), list(dataY['好瓜']))) ]) # ============================ # 绘制样本点 # ============================ line_x = np.array([np.min(dataX['密度']), np.max(dataX['密度'])]) mpl.rcParams['font.sans-serif'] = [u'SimHei'] plt.title('对数几率回归模拟\nLogistic Regression Simulation') plt.xlabel('density') plt.ylabel('sugarRate') plt.scatter(dataX['密度'][label==0], dataX['含糖率'][label==0], marker='^', color='k', s=100, label='坏瓜') plt.scatter(dataX['密度'][label==1], dataX['含糖率'][label==1], marker='^', color='r', s=100, label='好瓜') # ============================ # 实例化对数几率回归模型 # ============================ logit = Logit(dataX, label) # 采用牛顿迭代法 logit.logitRegression(logit.newtomMethod) line_y = -logit.w[0, 0] / logit.w[1, 0] * line_x - logit.w[2, 0] / logit.w[1, 0] plt.plot(line_x, line_y, 'g-', label="牛顿迭代法") # 绘图 plt.legend(loc='upper left') plt.show()其中更新权重代码为 ''' * @breif: 牛顿迭代法更新权重 * @param[in]: None * @retval: 优化参数的增量dw ''' def newtomMethod(self): wTx = np.dot(self.w.T, self.X).reshape(-1, 1) p = Logit.sigmod(wTx) dw_1 = -self.X.dot(self.y - p) dw_2 = self.X.dot(np.diag((p * (1 - p)).reshape(self.N))).dot(self.X.T) dw = np.linalg.inv(dw_2).dot(dw_1) return dw🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 引例2 数值解法3 梯度下降算法4 代码实战:Logistic回归1 引例给定如图所示的某个函数,如何通过计算机算法编程求 f ( x ) m i n f(x)_{min} f(x) min ?2 数值解法传统方法是数值解法,如图所示按照以下步骤迭代循环直至最优:① 任意给定一个初值 x 0 x_0 x 0 ;② 随机生成增量方向,结合步长生成 Δ x \varDelta x Δx;③ 计算比较 f ( x 0 ) f\left( x_0 \right) f(x 0 )与 f ( x 0 + Δ x ) f\left( x_0+\varDelta x \right) f(x 0 +Δx)的大小,若 f ( x 0 + Δ x ) < f ( x 0 ) f\left( x_0+\varDelta x \right) <f\left( x_0 \right) f(x 0 +Δx)<f(x 0 )则更新位置,否则重新生成 Δ x \varDelta x Δx;④ 重复②③直至收敛到最优 f ( x ) m i n f(x)_{min} f(x) min 。数值解法最大的优点是编程简明,但缺陷也很明显:① 初值的设定对结果收敛快慢影响很大;② 增量方向随机生成,效率较低;③ 容易陷入局部最优解;④ 无法处理“高原”类型函数。所谓陷入局部最优解是指当迭代进入到某个极小值或其邻域时,由于步长选择不恰当,无论正方向还是负方向,学习效果都不如当前,导致无法向全局最优迭代。就本问题而言如图所示,当迭代陷入 x = x j x=x_j x=x j 时,由于学习步长 s t e p step step的限制,无法使 f ( x j ± S t e p ) < f ( x j ) f\left( x_j\pm Step \right) <f(x_j) f(x j ±Step)<f(x j ),因此迭代就被锁死在了图中的红色区段。可以看出 x = x j x=x_j x=x j 并非期望的全局最优。若出现下图所示的“高原”函数,也可能使迭代得不到更新。3 梯度下降算法梯度下降算法可视为数值解法的一种改进,阐述如下:记第 k k k轮迭代后,自变量更新为 x = x k x=x_k x=x k ,令目标函数 f ( x ) f(x) f(x)在 x = x k x=x_k x=x k 泰勒展开:f ( x ) = f ( x k ) + f ′ ( x k ) ( x − x k ) + o ( x ) f\left( x \right) =f\left( x_k \right) +f'\left( x_k \right) \left( x-x_k \right) +o(x)f(x)=f(x k )+f ′ (x k )(x−x k )+o(x)考察 f ( x ) m i n f(x)_{min} f(x) min ,则期望 f ( x k + 1 ) < f ( x k ) f\left( x_{k+1} \right) <f\left( x_k \right) f(x k+1 )<f(x k ),从而:f ( x k + 1 ) − f ( x k ) = f ′ ( x k ) ( x k + 1 − x k ) < 0 f\left( x_{k+1} \right) -f\left( x_k \right) =f'\left( x_k \right) \left( x_{k+1}-x_k \right) <0f(x k+1 )−f(x k )=f ′ (x k )(x k+1 −x k )<0若 f ′ ( x k ) > 0 f'\left( x_k \right) >0 f ′ (x k )>0则 x k + 1 < x k x_{k+1}<x_k x k+1 <x k ,即迭代方向为负;反之为正。不妨设 x k + 1 − x k = − f ′ ( x k ) x_{k+1}-x_k=-f'(x_k) x k+1 −x k =−f ′ (x k ),从而保证 f ( x k + 1 ) − f ( x k ) < 0 f\left( x_{k+1} \right) -f\left( x_k \right) <0 f(x k+1 )−f(x k )<0。必须指出,泰勒公式成立的条件是 x → x 0 x\rightarrow x_0 x→x 0 ,故 ∣ f ′ ( x k ) ∣ |f'\left( x_k \right) | ∣f ′ (x k )∣不能太大,否则 x k + 1 x_{k+1} x k+1 与 x k x_{k} x k 距离太远产生余项误差。因此引入学习率 γ ∈ ( 0 , 1 ) \gamma \in \left( 0, 1 \right) γ∈(0,1)来减小偏移度,即 x k + 1 − x k = − γ f ′ ( x k ) x_{k+1}-x_k=-\gamma f'(x_k) x k+1 −x k =−γf ′ (x k )在工程上,学习率 γ \gamma γ要结合实际应用合理选择, γ \gamma γ过大会使迭代在极小值两侧振荡,算法无法收敛; γ \gamma γ过小会使学习效率下降,算法收敛慢。对于向量 ,将上述迭代公式推广为x k + 1 = x k − γ ∇ x k {\boldsymbol{x}_{\boldsymbol{k}+1}=\boldsymbol{x}_{\boldsymbol{k}}-\gamma \nabla _{\boldsymbol{x}_{\boldsymbol{k}}}}x k+1 =x k −γ∇ x k其中 ∇ x = ( ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , ⋯ ⋯ , ∂ f ( x ) ∂ x n ) T \nabla _{\boldsymbol{x}}=\left( \frac{\partial f(\boldsymbol{x})}{\partial x_1},\frac{\partial f(\boldsymbol{x})}{\partial x_2},\cdots \cdots ,\frac{\partial f(\boldsymbol{x})}{\partial x_n} \right) ^T ∇ x =( ∂x 1∂f(x) , ∂x 2∂f(x) ,⋯⋯, ∂x n∂f(x) ) T 为多元函数的梯度,故此迭代算法也称为梯度下降算法梯度下降算法通过函数梯度确定了每一次迭代的方向和步长,提高了算法效率。但从原理上可以知道,此算法并不能解决数值解法中初值设定、局部最优陷落和部分函数锁死的问题。4 代码实战:Logistic回归import pandas as pd import numpy as np import os import matplotlib.pyplot as plt import matplotlib as mpl from Logit import Logit ''' * @breif: 从CSV中加载指定数据 * @param[in]: file -> 文件名 * @param[in]: colName -> 要加载的列名 * @param[in]: mode -> 加载模式, set: 列名与该列数据组成的字典, df: df类型 * @retval: mode模式下的返回值 ''' def loadCsvData(file, colName, mode='df'): assert mode in ('set', 'df') df = pd.read_csv(file, encoding='utf-8-sig', usecols=colName) if mode == 'df': return df if mode == 'set': res = {} for col in colName: res[col] = df[col].values return res if __name__ == '__main__': # ============================ # 读取CSV数据 # ============================ csvPath = os.path.abspath(os.path.join(__file__, "../../data/dataset3.0alpha.csv")) dataX = loadCsvData(csvPath, ["含糖率", "密度"], 'df') dataY = loadCsvData(csvPath, ["好瓜"], 'df') label = np.array([ 1 if i == "是" else 0 for i in list(map(lambda s: s.strip(), list(dataY['好瓜']))) ]) # ============================ # 绘制样本点 # ============================ line_x = np.array([np.min(dataX['密度']), np.max(dataX['密度'])]) mpl.rcParams['font.sans-serif'] = [u'SimHei'] plt.title('对数几率回归模拟\nLogistic Regression Simulation') plt.xlabel('density') plt.ylabel('sugarRate') plt.scatter(dataX['密度'][label==0], dataX['含糖率'][label==0], marker='^', color='k', s=100, label='坏瓜') plt.scatter(dataX['密度'][label==1], dataX['含糖率'][label==1], marker='^', color='r', s=100, label='好瓜') # ============================ # 实例化对数几率回归模型 # ============================ logit = Logit(dataX, label) # 采用梯度下降法 logit.logitRegression(logit.gradientDescent) line_y = -logit.w[0, 0] / logit.w[1, 0] * line_x - logit.w[2, 0] / logit.w[1, 0] plt.plot(line_x, line_y, 'b-', label="梯度下降法") # 绘图 plt.legend(loc='upper left') plt.show()🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
🔥 作者:FrigidWinter🔥 简介:主攻机器人与人工智能领域的理论研究和工程应用,业余丰富各种技术栈。主要涉足:【机器人(ROS)】【机器学习】【深度学习】【计算机视觉】🔥 专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》…目录0 写在前面1 视频帧采样2 将图片转为字符画2.1 创建像素-字符索引2.2 将图片逐像素转换为字符3 将字符图像合成视频4 完整代码5 参考0 写在前面放鞭炮贺新春,在我国有两千多年历史。关于鞭炮的起源,有个有趣的传说。西方山中有焉,长尺余,一足,性不畏人。犯之令人寒热,名曰年惊惮,后人遂象其形,以火药为之。——《神异经》当初人们燃竹而爆,是为了驱吓危害人们的山魈。据说山魈最怕火光和响声,所以每到除夕,人们便“燃竹而爆”,把山魈吓跑。这样年复一年,便形成了过年放鞭炮、点红烛、敲锣打鼓欢庆新春的年俗。新年新气象,今天就用代码来制作一个 动态鞭炮 ,效果如下所示。动态鞭炮的基本原理是:将一个录制好的鞭炮视频以字符画的形式复现,基本步骤是帧采样 → 逐帧转换为字符画 → 字符画合成视频。下面开始吧!1 视频帧采样函数如下所示,主要功能是将视频的图像流逐帧保存到特定的缓存文件夹中(若该文件夹不存在会自动创建)。函数输入vp是openCV视频句柄,输出number是转换的图片数。def video2Pic(vp): number = 0 if vp.isOpened(): r,frame = vp.read() if not os.path.exists('cachePic'): os.mkdir('cachePic') os.chdir('cachePic') else: r = False while r: number += 1 cv2.imwrite(str(number)+'.jpg',frame) r,frame = vp.read() os.chdir("..") return number2 将图片转为字符画2.1 创建像素-字符索引函数输入像素RGBA值,输出对应的字符码。其原理是将字符均匀地分布在整个灰度范围内,像素灰度值落在哪个区间就对应哪个字符码。字符码可以参考 ASCII码ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。其中:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。RGBA是代表Red(红色)、Green(绿色)、Blue(蓝色)和Alpha的色彩空间,Alpha通道一般用作不透明度参数。如果一个像素的alpha通道数值为0%,那它就是完全透明的,而数值为100%则意味着一个完全不透明的像素(传统的数字图像)。gray=0.2126 * r + 0.7152 * g + 0.0722 * b是RGB转为灰度值的经验公式,人眼对绿色更敏感。def color2Char(r,g,b,alpha = 256): imgChar= list("#RMNHQODBWGPZ*@$C&98?32I1>!:-;. ") if alpha: gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) unit = 256 / len(imgChar) return imgChar[int(gray / unit)] else: return ''2.2 将图片逐像素转换为字符核心代码如下,遍历图片的每个像素 img = Image.open(imagePath).convert('RGB').resize((imgWidth, imgHeight),Image.NEAREST) for i in range(imgHeight): for j in range(imgWidth): pixel = img.getpixel((j, i)) color.append((pixel[0],pixel[1],pixel[2])) txt = txt + color2Char(pixel[0], pixel[1], pixel[2], pixel[3]) if len(pixel) == 4 else \ txt + color2Char(pixel[0], pixel[1], pixel[2]) txt += '\n' color.append((255,255,255))3 将字符图像合成视频输入参数vp是openCV视频句柄,number是帧数,savePath是视频保存路径,函数中 MP42 是可以生成较小并且较小的视频文件的编码方式,其他类似的还有isom、mp41、avc1、qt等,表示“最好”基于哪种格式来解析当前的文件。def img2Video(vp, number, savePath): videoFourcc = VideoWriter_fourcc(*"MP42") # 设置视频编码器 asciiImgPathList = ['cacheChar' + r'/{}.jpg'.format(i) for i in range(1, number + 1)] asciiImgTemp = Image.open(asciiImgPathList[1]).size videoWritter= VideoWriter(savePath, videoFourcc, vp.get(cv2.CAP_PROP_FPS), asciiImgTemp) for imagePath in asciiImgPathList: videoWritter.write(cv2.imread(imagePath)) videoWritter.release()4 完整代码import cv2 from PIL import Image,ImageFont,ImageDraw import os from cv2 import VideoWriter, VideoWriter_fourcc ''' * @breif: 将像素颜色转换为ASCII字符 * @param[in]: 像素RGBA值 * @retval: 字符 ''' def color2Char(r,g,b,alpha = 256): imgChar = list("#RMNHQODBWGPZ*@$C&98?32I1>!:-;. ") if alpha: gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b) unit = 256 / len(imgChar) return imgChar[int(gray / unit)] else: return '' ''' * @breif: 将视频逐帧转换为图片 * @param[in]: vp -> openCV视频句柄 * @retval: number -> 转换的图片数 ''' def video2Pic(vp): number = 0 if vp.isOpened(): r,frame = vp.read() if not os.path.exists('cachePic'): os.mkdir('cachePic') os.chdir('cachePic') else: r = False while r: number += 1 cv2.imwrite(str(number)+'.jpg',frame) r,frame = vp.read() os.chdir("..") return number ''' * @breif: 将图片逐像素转换为ASCII字符 * @param[in]: imagePath -> 图片路径 * @param[in]: index -> 图片索引 * @retval: None ''' def img2Char(imagePath, index): # 初始化 txt, color, font = '', [], ImageFont.load_default().font imgWidth, imgHeight = Image.open(imagePath).size asciiImg = Image.new("RGB",(imgWidth, imgHeight), (255,255,255)) drawPtr = ImageDraw.Draw(asciiImg) imgWidth, imgHeight = int(imgWidth / 6), int(imgHeight / 15) # 对图像帧逐像素转化为ASCII字符并记录RGB值 img = Image.open(imagePath).convert('RGB').resize((imgWidth, imgHeight),Image.NEAREST) for i in range(imgHeight): for j in range(imgWidth): pixel = img.getpixel((j, i)) color.append((pixel[0],pixel[1],pixel[2])) txt = txt + color2Char(pixel[0], pixel[1], pixel[2], pixel[3]) if len(pixel) == 4 else \ txt + color2Char(pixel[0], pixel[1], pixel[2]) txt += '\n' color.append((255,255,255)) # 绘制ASCII字符画并保存 x, y = 0,0 fontW, fontH = font.getsize(txt[1]) fontH *= 1.37 for i in range(len(txt)): if(txt[i]=='\n'): x += fontH y = -fontW drawPtr.text((y,x), txt[i], fill=color[i]) y += fontW os.chdir('cacheChar') asciiImg.save(str(index)+'.jpg') os.chdir("..") ''' * @breif: 将视频转换为ASCII图像集 * @param[in]: number -> 帧数 * @retval: None ''' def video2Char(number): if not os.path.exists('cacheChar'): os.mkdir('cacheChar') img_path_list = ['cachePic' + r'/{}.jpg'.format(i) for i in range(1, number + 1)] task = 0 for imagePath in img_path_list: task += 1 img2Char(imagePath, task) ''' * @breif: 将图像合成视频 * @param[in]: vp -> openCV视频句柄 * @param[in]: number -> 帧数 * @param[in]: savePath -> 视频保存路径 * @retval: None ''' def img2Video(vp, number, savePath): videoFourcc = VideoWriter_fourcc(*"MP42") # 设置视频编码器 asciiImgPathList = ['cacheChar' + r'/{}.jpg'.format(i) for i in range(1, number + 1)] asciiImgTemp = Image.open(asciiImgPathList[1]).size videoWritter= VideoWriter(savePath, videoFourcc, vp.get(cv2.CAP_PROP_FPS), asciiImgTemp) for imagePath in asciiImgPathList: videoWritter.write(cv2.imread(imagePath)) videoWritter.release() if __name__ == '__main__': videoPath = 'test.mp4' savePath = 'new.avi' vp = cv2.VideoCapture(videoPath) number = video2Pic(vp) video2Char(number) img2Video(vp, number, savePath) vp.release()5 参考[1] B站《大二AE结课作业 《过 年》(已摆烂)》[2] https://hongcyu.cn/posts/opencv-pictovideo.html——————————————— 其他喜迎新年系列作品 ——————————————动态编织中国结
🔥 作者:FrigidWinter🔥 简介:主攻机器人与人工智能领域的理论研究和工程应用,业余丰富各种技术栈。主要涉足:【机器人(ROS)】【机器学习】【深度学习】【计算机视觉】🔥 专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》…目录0 写在前面1 中国结的组成部分2 设计中国结对象3 绘制结体4 绘制耳翼5 绘制挂耳和流苏6 完整代码,一键运行0 写在前面小年(1.25)是扫尘、祭灶的日子;随之而来的除夕则是“流浪”在外打工人忙碌一年后,难得的回家团圆的日子。博主在这里预祝大家新年万事顺意,幸福是奋斗出来的!1 中国结的组成部分中国结是一种手工编织工艺品,它身上所显示的情致与智慧正是汉族古老文明中的一个侧面。因为其外观对称精致,可以代表汉族悠久的历史,符合中国传统装饰的习俗和审美观念,故命名为中国结。中国结代表着团结幸福平安,特别是在民间,它精致的做工深受大众的喜爱。其主要组成部分如下图所示。2 设计中国结对象基于Python Turtle库实现绘制,首先设计一个中国结对象,画笔颜色就选择中国红。再定义一个__goto()函数封装turtle库对画笔移动的操作。import turtle as t class chineseKnot: ''' * @breif: 中国结 ''' def __init__(self) -> None: # 画笔初始化 self.t = t self.t.pensize(10) self.t.setup(700, 700) self.t.pencolor("red") self.t.speed(14) # 结心坐标 self.x = 0 self.y = 200 ''' * @breif: 画笔移动到指定位置 * @param[in]: x -> 画笔移动位置横坐标 * @param[in]: y -> 画笔移动位置纵坐标 * @retval: None ''' def __goto(self, x: int, y: int) -> None: self.t.penup() self.t.goto(x,y) self.t.pendown()3 绘制结体 def drawBody(self) -> None: for i in range(11): self.__goto(self.x - i * 10 * sqrt(2), self.y - i * 10 * sqrt(2)) self.t.seth(-45) self.t.fd(200) self.__goto(self.x + i * 10 * sqrt(2), self.y - i * 10 * sqrt(2)) self.t.seth(-135) self.t.fd(200)4 绘制耳翼def drawEdge(self) -> None: for i in range(4): # 左上角 self.__goto(-10 * sqrt(2) - i * 20 * sqrt(2), 200 - 10 * sqrt(2) - i * 20 * sqrt(2)) self.t.seth(135) self.t.fd(20) self.t.circle(10, 180) self.t.fd(20) # 右上角 self.__goto(10 * sqrt(2) + i * 20 * sqrt(2), 200 - 10 * sqrt(2) - i * 20 * sqrt(2)) self.t.seth(45) self.t.fd(20) self.t.circle(-10, 180) self.t.fd(20) # 左下角 self.__goto(-10 * sqrt(2) - i * 20 * sqrt(2), 200 - 190 * sqrt(2) + i * 20 * sqrt(2)) self.t.seth(-135) self.t.fd(20) self.t.circle(-10, 180) self.t.fd(20) # 右下角 self.__goto(10 * sqrt(2) + i * 20 * sqrt(2), 200 - 190 * sqrt(2) + i * 20 * sqrt(2)) self.t.seth(-45) self.t.fd(20) self.t.circle(10, 180) self.t.fd(20) # 左侧 self.t.seth(-45) self.__goto(90 * sqrt(2), 200 - 110 * sqrt(2)) self.t.circle(20,270) self.__goto(-90 * sqrt(2), 200 - 110 * sqrt(2)) self.t.circle(-20,270) # 右侧 self.__goto(80 * sqrt(2), 200 - 120 * sqrt(2)) self.t.circle(40,270) self.__goto(-80 * sqrt(2), 200 - 120 * sqrt(2)) self.t.circle(-40,270)5 绘制挂耳和流苏 def drawAdorn(self): # 上侧 self.__goto(self.x, self.y) self.t.pensize(14) self.t.seth(90) self.t.fd(60) self.__goto(0,320) self.t.seth(180) self.t.circle(30,360) # 下侧 self.__goto(0,200 - 200 * sqrt(2)) self.t.pensize(40) self.t.seth(-90) self.t.fd(20) self.t.pensize(2) for i in range(11): self.__goto(-20 + 4 * i, 200 - 200 * sqrt(2)) self.t.seth(-90) self.t.fd(200)6 完整代码,一键运行import turtle as t from math import sqrt class chineseKnot: ''' * @breif: 中国结 ''' def __init__(self) -> None: # 画笔初始化 self.t = t self.t.pensize(10) self.t.setup(700, 700) self.t.pencolor("red") self.t.speed(14) # 结心坐标 self.x = 0 self.y = 200 ''' * @breif: 画中国结 * @param[in]: None * @retval: None ''' def drawKnot(self) -> None: self.drawBody() self.drawEdge() self.drawAdorn() self.t.hideturtle() self.t.done() ''' * @breif: 画中国结主体部分 * @param[in]: None * @retval: None ''' def drawBody(self) -> None: for i in range(11): self.__goto(self.x - i * 10 * sqrt(2), self.y - i * 10 * sqrt(2)) self.t.seth(-45) self.t.fd(200) self.__goto(self.x + i * 10 * sqrt(2), self.y - i * 10 * sqrt(2)) self.t.seth(-135) self.t.fd(200) ''' * @breif: 画中国结边缘部分 * @param[in]: None * @retval: None ''' def drawEdge(self) -> None: for i in range(4): # 左上角 self.__goto(-10 * sqrt(2) - i * 20 * sqrt(2), 200 - 10 * sqrt(2) - i * 20 * sqrt(2)) self.t.seth(135) self.t.fd(20) self.t.circle(10, 180) self.t.fd(20) # 右上角 self.__goto(10 * sqrt(2) + i * 20 * sqrt(2), 200 - 10 * sqrt(2) - i * 20 * sqrt(2)) self.t.seth(45) self.t.fd(20) self.t.circle(-10, 180) self.t.fd(20) # 左下角 self.__goto(-10 * sqrt(2) - i * 20 * sqrt(2), 200 - 190 * sqrt(2) + i * 20 * sqrt(2)) self.t.seth(-135) self.t.fd(20) self.t.circle(-10, 180) self.t.fd(20) # 右下角 self.__goto(10 * sqrt(2) + i * 20 * sqrt(2), 200 - 190 * sqrt(2) + i * 20 * sqrt(2)) self.t.seth(-45) self.t.fd(20) self.t.circle(10, 180) self.t.fd(20) # 左侧 self.t.seth(-45) self.__goto(90 * sqrt(2), 200 - 110 * sqrt(2)) self.t.circle(20,270) self.__goto(-90 * sqrt(2), 200 - 110 * sqrt(2)) self.t.circle(-20,270) # 右侧 self.__goto(80 * sqrt(2), 200 - 120 * sqrt(2)) self.t.circle(40,270) self.__goto(-80 * sqrt(2), 200 - 120 * sqrt(2)) self.t.circle(-40,270) ''' * @breif: 画中国结装饰部分 * @param[in]: None * @retval: None ''' def drawAdorn(self): # 上侧 self.__goto(self.x, self.y) self.t.pensize(14) self.t.seth(90) self.t.fd(60) self.__goto(0,320) self.t.seth(180) self.t.circle(30,360) # 下侧 self.__goto(0,200 - 200 * sqrt(2)) self.t.pensize(40) self.t.seth(-90) self.t.fd(20) self.t.pensize(2) for i in range(11): self.__goto(-20 + 4 * i, 200 - 200 * sqrt(2)) self.t.seth(-90) self.t.fd(200) ''' * @breif: 画笔移动到指定位置 * @param[in]: x -> 画笔移动位置横坐标 * @param[in]: y -> 画笔移动位置纵坐标 * @retval: None ''' def __goto(self, x: int, y: int) -> None: self.t.penup() self.t.goto(x,y) self.t.pendown() if __name__ == '__main__': knot = chineseKnot() knot.drawKnot()
目录1 点算子2 线性灰度变换3 直方图均衡化4 代码实战1 点算子点算子是两个像素灰度值间的映射关系,属于像素的逐点运算,相邻像素不参与运算。点算子是最简单的图像处理手段,如:亮度调整、对比度调整、颜色变换、直方图均衡化等等。2 线性灰度变换线性灰度变换表达为:s k = T ( r k ) = a r k + b s_k=T\left( r_k \right) =ar_k+bs k =T(r k )=ar k +b其中 r k r_k r k 、 s k s_k s k 分别为输入、输出点像素灰度值。当 a > 1 a>1 a>1时,输出图像像素灰度范围扩大,图像对比度增强,当 a < 1 a<1 a<1时反之。这是因为人眼不易区分相近的灰度值,因此若图像灰度值范围较小,观感上细节不够清晰。当 a = 1 a=1 a=1、 b ≠ 0 b\ne0 b =0时,点算子使图像灰度整体上移或下移,即整体变亮或变暗。3 直方图均衡化下图再次给出了关于图像对比度的例子直方图均衡化是以累计分布函数为核心,将原始图像灰度直方图从比较集中的某个灰度区间,非线性地映射为在全部灰度范围内的较均匀分布,从而增强对比度。下面阐述直方图均衡化的数学原理。首先作原始图像灰度的概率直方图如图设输入像素灰度值为 r k r_k r k ,累计分布函数为C ( r k ) = 1 n ∑ i = 0 k n i C\left( r_k \right) =\frac{1}{n}\sum_{i=0}^k{n_i}C(r k )= n1 i=0∑k n i其中 n i n_i n i 为图像中灰度值为 r i r_i r i 的像素频数, n n n为图像像素总数。设输出像素灰度值为 s k s_k s k ,像素范围为 s m i n − s m a x s_{min}-s_{max} s min −s max 。期望输出灰度直方图是均匀分布,即P ( s ) = 1 s max − s min s min ⩽ s ⩽ s max P\left( s \right) =\frac{1}{s_{\max}-s_{\min}}\,\, s_{\min}\leqslant s\leqslant s_{\max}P(s)= s max −s min1 s min ⩽s⩽s max令 C ( s k ) = C ( r k ) C\left( s_k \right) =C\left( r_k \right) C(s k )=C(r k ),即得( C ( r k ) max − C ( r k ) min ) s k − s min s max − s min + C ( r k ) min = C ( r k ) ⇒ s k − s min s max − s min = C ( r k ) − C ( r k ) min C ( r k ) max − C ( r k ) min ⇒ s k − s min s max − s min = C ′ ( r k ) \left( C\left( r_k \right) _{\max}-C\left( r_k \right) _{\min} \right) \frac{s_k-s_{\min}}{s_{\max}-s_{\min}}+C\left( r_k \right) _{\min}=C\left( r_k \right) \\\Rightarrow \,\, \frac{s_k-s_{\min}}{s_{\max}-s_{\min}}=\frac{C\left( r_k \right) -C\left( r_k \right) _{\min}}{C\left( r_k \right) _{\max}-C\left( r_k \right) _{\min}}\\\Rightarrow \,\, \frac{s_k-s_{\min}}{s_{\max}-s_{\min}}=C'\left( r_k \right)(C(r k ) max −C(r k ) min ) s max −s mins k −s min +C(r k ) min =C(r k )⇒ s max −s mins k −s min = C(r k ) max −C(r k ) minC(r k )−C(r k ) min⇒ s max −s mins k −s min =C ′ (r k )所以最终直方图均衡化的点算子为s k = ( s max − s min ) C ′ ( r k ) + s min = T ( r k ) s_k=\left( s_{\max}-s_{\min} \right) C'\left( r_k \right) +s_{\min}=T\left( r_k \right)s k =(s max −s min )C ′ (r k )+s min =T(r k )4 代码实战按照前文的原理编写累积分布函数计算公式,以及均衡化算子# 计算累计分布函数 def C(rk): # 读取图片灰度直方图 # bins为直方图直方柱的取值向量 # hist为bins各取值区间上的频数取值 hist, bins = np.histogram(rk, 256, [0, 256]) # 计算累计分布函数 return hist.cumsum()# 计算灰度均衡化映射 def T(rk): cdf = C(rk) # 均衡化 cdf = (cdf - cdf.min()) * (255 - 0) / (cdf.max() - cdf.min()) + 0 return cdf.astype('uint8')均衡化时直接调用函数即可,下面给出完整代码import numpy as np import cv2 as cv from matplotlib import pyplot as plt # 计算累计分布函数 def C(rk): # 读取图片灰度直方图 # bins为直方图直方柱的取值向量 # hist为bins各取值区间上的频数取值 hist, bins = np.histogram(rk, 256, [0, 256]) # 计算累计分布函数 return hist.cumsum() # 计算灰度均衡化映射 def T(rk): cdf = C(rk) # 均衡化 cdf = (cdf - cdf.min()) * (255 - 0) / (cdf.max() - cdf.min()) + 0 return cdf.astype('uint8') # 读取图片 img = cv.imread('1.png', 0) # 将二维数字图像矩阵转变为一维向量 rk = img.flatten() # 原始图像灰度直方图 plt.hist(rk, 256, [0, 255], color = 'r') cv.imshow("原图像",img) # 直方图均衡化 imgDst = T(rk)[img] cv.imshow("直方图均衡化后的图像",imgDst) plt.hist(imgDst.flatten(), 256, [0, 255], color = 'b') plt.show()看看效果:均衡化前:均衡化后:🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
0 写在前面1 摩尔定律(Moore’s Law)2 阿姆达尔定律(Amdahl’s Law)3 软硬件互易性原理4 抽象与3Y原则5 局部性原理0 写在前面🔥接触人工智能领域,除了要理性地学习各种算法,还要对行业内的基本发展历程和规律有感性的认识和了解,才能对领域有更深刻的理解(和外行科普也有得聊),本文主要罗列计算机领域的几条重要定律。1 摩尔定律(Moore’s Law)Moore’s Law指IT产业的硬件或设备生产商的技术每十八个月翻一番,或者说相关技术涉及的产品每十八个月价格下降一半。与之对应,Reverse Moore’s Law指一个IT公司如果今天和十八个月前卖掉同样多的、同样的产品,它的营业额就要降一半。Moore’s Law& Reverse Moore’s Law告知所有IT行业:发展速度必须至少与摩尔定律的预测速度持平,否则整个企业就会面临亏损。这也使得IT行业与石油工业、飞机制造业等量变工业不同,IT行业必须在预测周期内寻求革命性和创新性的技术突破,否则优胜劣汰。因此两大定律促成了IT行业科技的不断进步,也避免了垄断的产生,为新兴企业的生存和发展提供了可能。Moore’s Law& Reverse Moore’s Law是整个人类社会发展速度在IT行业的反映,是指导公司和科研机构制定研究计划和市场策略的重要指导。目前Moore’s Law& Reverse Moore’s Law仍然有效,考虑如果其预测周期放缓或是失效,那么将对所有依赖于更高级芯片的行业(如AI行业)的发展换代都造成空前影响2 阿姆达尔定律(Amdahl’s Law)Amdahl’s Law指出:系统中对某一部件采用更快执行方式所能获得的系统性能改进程度,取决于这种执行方式被使用的频率,或所占总执行时间的比例。若系统由若干个单项组成,每项占比 a i a_i a i ,每个单项可提速 k i k_i k i 倍(对于不可提速的组件,其 k i = 1 k_i=1 k i =1),则整个系统的加速比为:S = 1 ∑ i a i k i S=\frac{1}{\sum_i{\frac{a_i}{k_i}}}S= ∑ i k ia i1例2.1 根据SPEC CPU2000标准,其包含25% loads、10% stores、11% branches、2% jumps、52% R-type。现对该CPU进行优化,优化前后见例表,问:(1) CPU优化后的整体提速比为多少;(2) 优化后的平均CPI为多少。指令 优化前耗时/时钟周期数 优化后耗时/时钟周期数beq, j 5 3R-Type, sw, addi 5 4lw 5 5(1) 由Amdahl’s Law知,提速比为:S = 1 ∑ i a i k i = 1 ( 25 % 5 5 + 10 % 5 4 + 11 % 5 3 + 2 % 5 3 + 52 % 5 4 ) = 1.21359 S=\frac{1}{\sum_i{\frac{a_i}{k_i}}}=\frac{1}{\left( \frac{25\%}{\frac{5}{5}}+\frac{10\%}{\frac{5}{4}}+\frac{11\%}{\frac{5}{3}}+\frac{2\%}{\frac{5}{3}}+\frac{52\%}{\frac{5}{4}} \right)}=1.21359S= ∑ i k ia i1 = ( 5525% + 4510% + 3511% + 352% + 4552% )1 =1.21359(2) 平均CPI为:A v r C P I = ( 11 % + 2 % ) × 3 + ( 52 % + 10 % ) × 4 + 25 % × 5 = 4.12 AvrCPI=\left( 11\%+2\% \right) \times 3+\left( 52\%+10\% \right) \times 4+25\%\times 5=4.12AvrCPI=(11%+2%)×3+(52%+10%)×4+25%×5=4.123 软硬件互易性原理软硬件互易性原理又称为软硬件逻辑功能等效性,是指计算机系统的某功能可以由硬件实现也可以由软件实现。如图所示,对任何一个真实的、技术可实现的计算机系统,都需要有最基础的一薄层硬件来实现,这一最基础的硬件实现了图灵机模型的要求。图灵机等价硬件之上大部分都是各种硬件加速手段。图中说明了软硬件之间的关系:(1) 硬件和软件互相依存硬件是软件赖以工作的物质基础(图灵机等价硬件部分),软件的正常工作是硬件充分发挥作用的唯一途径。(2) 硬件和软件无严格界线随着计算机技术的发展,在许多情况下计算机的某些功能既可以由硬件实现,也可以由软件来实现。硬件就是数字逻辑电路的组合,软件通过编译成机器代码即0-1电平序列作为硬件的输入,使硬件实现特定的功能,而这个过程也可以不借助软件而直接通过数字电路实现。因此,硬件与软件在一定意义上说没有绝对严格的界面,是逻辑等效的。例如MP3既可以用软件的播放器播放,也可用专用的音频解码芯片解码实现播放。(3) 硬件和软件协同发展计算机软件随硬件技术的迅速发展而发展,而软件的不断发展与完善又促进硬件的更新,两者密切地交织发展,缺一不可。对一个具体的计算机系统而言,软硬件的分割线在哪里,主要取决于性能和成本之间的折衷。由于软件实现功能需要经过编译,因此使用软件比硬件效率低,但随着技术发展,软件完全可以通过固化为硬件(如实现特定功能的芯片)来提升系统的性能,这样避免了编译环节,使硬件电路可以直接读取指令。因此如果要求高性能,那么硬件加速的部件可以多些,相应成本也不可避免会增加;如果要求低成本,那么图中曲线可以下移,即用软件完成大部分处理,但性能会有所下降。4 抽象与3Y原则下图给出了一个电子计算机系统的抽象层次,其中每个层次都列举了典型模块。抽象的意义在于便于不同类型的用户,对计算机进行使用和开发。例如普通用户可以通过计算机上网,而不用考虑电子波动;硬件工程师不用关注应用软件设计等。最底层的抽象是物理层——电子运动:电子的行为由量子力学和麦克斯韦方程描述;基于电子运动的物理规律,组成晶体管、真空管等电子器件,这些器件都有明确定义的称为端子的外部连接点,可以建立每个端子上的电压、电流关系模型。模拟电路抽象层由器件组成的放大器等组件构成;数字电路则将电压控制在离散的范围内,以便表示0-1二值;通过逻辑门等数字电路可以进行逻辑功能设计,构造更复杂的功能结构,例如加法器或存储器。体系结构层描述了程序员观点的计算机抽象,其定义了一套指令系统和寄存器,程序员可以使用这些指令和寄存器;微体系结构将逻辑层的元素组合在一起实现体系结构中定义的指令,其是逻辑和体系结构的沟通桥梁。另外,一个特定的体系结构可以有不同的微结构实现方式,以便取得在价格、性能和功耗等方面的不同折中。例如Intel Core i7、Intel 80486和AMD Athlon等都是x86体系结构的三种不同的微结构实现。除抽象外,设计者还使用三条准则来处理系统复杂性,这些原则对软硬件设计都通用。①层次化:将系统划分为若干模块,然后更进一步划分每个模块直到这些模块可以很容易理解;②模块化:所有模块有定义好的功能和接口,以便它们之间可以很容易地相互连接而不会产生意想不到的副作用;③规整化:在模块之间寻求一致,通用模块可以重复使用多次,以便减少设计不同模块的数量。5 局部性原理时间局部性原理:当CPU访问一个存储位置时,大概率在不久的将来会再次访问同一位置。程序的循环结构和过程调用体现了时间局部性。空间局部性原理:当CPU访问一个存储位置时,大概率在不久的将来会访问该位置附近的存储单元,因为计算机中的数据通常被连续存放于内存。程序的顺序结构体现了空间局部性。
智慧家居·万物互联:我的智能花盆DIY之旅0 写在前面1 架构怎么搭?1.1 系统层次1.2 MQTT是什么?1.3 项目流程2 云平台怎么用?2.1 创建设备2.2 设备开发2.3 设备管理3 软件怎么设计?3.1 依赖库配置3.2 引脚定义与连接3.3 WIFI配置3.4 MQTT配置3.5 连接云平台3.6 执行设备4 更进一步0 写在前面🔥物联网(Internet of things, IoT)就是物物相连的互联网,在智能家居、智慧城市等方面有广泛应用。这次,我从零开始搭建一个基于ESP32的智能花盆,相信读完本文,你也可以亲自实现一个物联网应用,无论是参加创客大赛还是物联网比赛,都先人一步!首先,先看看最后的实物图1 架构怎么搭?1.1 系统层次整个系统分为3部分:云端服务部分 使用任何云服务器即可,本项目使用涂鸦云平台,官网放在这涂鸦云平台控制器部分 本项目使用ESP32控制器,也可以使用STM32、树莓派等外围设备部分 即传感器、执行器等,本项目主要采用光敏电阻、DHT11温湿度传感器、灯管、风扇。也可使用舵机(做水阀)以及各种感应设备。1.2 MQTT是什么?MQTT(Message Queuing Telemetry Transport),消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。1.3 项目流程MQTT的订阅方和发布方遵守同一种开发API格式,我们根据所选云平台设计好的API进行功能设计。在项目运转时,ESP32(或其他任何控制器)通过WIFI连接到互联网,使得其能够与云平台通信,去订阅云平台发布的话题(即API),这样就能把底层传感器的数据收集并传输给平台,也能获得平台的反馈。更进一步,可将物联网应用部署到移动端、Web等。接下来就按系统层次一步步完成DIY。2 云平台怎么用?2.1 创建设备进入涂鸦云平台选择产品开发,开始创建设备。按如下图文步骤完成产品创建。产品开发:创建产品选择产品:温湿度传感器选择智能化方式:设备接入完善产品信息:添加自定义功能下面是本次实验设计的所有功能。2.2 设备开发领取免费激活码并注册一个设备,得到如下设备凭证。记住这里的设备凭证,后续配置要用!!2.3 设备管理完成上述步骤后可以在设备管理中看到创建的设备。3 软件怎么设计?3.1 依赖库配置本项目使用的DHT11驱动需要从下面两个地址下载库文件。DHT11、Adafruit_SensorMQTT库和JSON库则可以在Arduin仓库中自行下载。安装MQTT库安装Json库3.2 引脚定义与连接查看下面的ESP32引脚定义我的定义如下所示,大家可以参考#include "DHT.h" #include "WiFi.h" #include "PubSubClient.h" #include "ArduinoJson.h" GPIO/// #define DHTTYPE DHT11 // 定义温湿度传感器类型为DHT11 #define DHTPIN 15 // DHT11引脚 #define ADCPIN 32 // 光敏电阻引脚 #define LIGHTPIN 33 // 灯光控制引脚 #define FANPIN 13 // 风扇控制引脚 GPIO///根据定义的实际接线图如下:3.3 WIFI配置WIFI设置如下:WIFI/// #define WIFI_SSID "Winter" // wifi名 #define WIFI_PASSWD "913982779" // wifi密码 WIFI///3.4 MQTT配置参考大家选用云平台的协议规范,我这里参考涂鸦云MQTT协议需要配置ClientID、UserName、Password三个属性,都与前面设备凭证的DeviceId有关,其中Password需要根据设备密码用Hmac256算法加密。MQTT/// #define mqttServer "m1.tuyacn.com" #define mqttPort 1883 #define ClientId "tuyalink_6c9a1bfe77510a9904vbva" #define User "6c9a1bfe77510a9904vbva|signMethod=hmacSha256,timestamp=1639372190,securemode=1,accessType=1" #define Pass "e3b024852f65fffabbf17ccfe97b8a599b134a81037976736288df58ec88e188" #define TOPIC "tylink/6c9a1bfe77510a9904vbva/thing/property/set" MQTT///3.5 连接云平台连接WIFIWiFiClient espClient; //创建网络连接客户端 //连接WIFI相关函数 void setupWifi() { delay(10); Serial.println("Connecting WIFI"); WiFi.begin(WIFI_SSID, WIFI_PASSWD); while (!WiFi.isConnected()) { Serial.print("."); delay(500); } Serial.println("OK"); Serial.println("Wifi connected successfully!"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); }配置并连接MQTT//链接mqtt void setupMQTT() { client.setServer(mqttServer, mqttPort); client.setCallback(callback); while (!client.connected()) { Serial.println("Connecting MQTT"); if(client.connect(ClientId,User,Pass)) { Serial.println("MQTT connected successfully!"); client.subscribe(TOPIC); } else { Serial.print("Failed with state "); Serial.println(client.state()); delay(2000); } } }其中MQTT回调函数的作用:若订阅的主题有消息则触发回调获取消息// MQTT回调函数 void callback(char * topic,byte * payload,unsigned int length){ DynamicJsonDocument doc(512); char charbuffer[512]; Serial.print("Message arrived ["); Serial.print(topic); Serial.println("]"); int i = 0; for(;i<length;i++){ charbuffer[i] = (char)payload[i]; } charbuffer[i] = '\0'; DeserializationError error = deserializeJson(doc,charbuffer); if(error){ Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); return; } bool lightOn = doc["data"]["light_switch"]; bool dehumiOn = doc["data"]["fan_switch"]; if (lightOn){ digitalWrite(LIGHTPIN,HIGH); } else{ digitalWrite(LIGHTPIN,LOW); } if (dehumiOn){ digitalWrite(FANPIN,HIGH); } else{ digitalWrite(FANPIN,LOW); } }Arduino的设置函数void setup() { // put your setup code here, to run once: pinMode(LIGHTPIN,OUTPUT); Serial.begin(115200); setupWifi(); setupMQTT(); dht.begin(); }Arduino的循环函数void loop() { delay(5000); // Read humidity data int h = dht.readHumidity(); // Read temperature as Celsius (the default) int t = dht.readTemperature(); // Check if any reads failed and exit early (to try again). if (isnan(h) || isnan(t)) { Serial.println(F("Failed to read from DHT sensor!")); return; } // Read illumination data float l = analogRead(ADCPIN); int percent = 100 - l / 4096.0 * 100.0; // 串口打印 Serial.print(F("Humidity: ")); Serial.print(h); Serial.print(F("% Temperature: ")); Serial.print(t); Serial.print(F("C ")); Serial.print(F("illumination: ")); Serial.print(percent); Serial.println(F("% ")); // 封装json DynamicJsonDocument doc(512); DynamicJsonDocument jsdata(256); DynamicJsonDocument tempdata(32); DynamicJsonDocument humidata(32); DynamicJsonDocument light(32); tempdata["value"] = t; tempdata["time"] = 1639454915; humidata["value"] = h; humidata["time"] = 1639454915; illudata["value"] = percent; illudata["time"] = 1639454915; jsdata["temp_current"] = tempdata; jsdata["humidity_current"] = humidata; jsdata["light_current"] = light; doc["msgId"] = "45lkj3551234001"; doc["time"] = 1639454915; doc["data"] = jsdata; String str; serializeJson(doc, str); // Serial.println(str); // Sending to MQTT char *p = (char *)str.c_str(); if(client.publish("tylink/6c9a1bfe77510a9904vbva/thing/property/report",p) == true) Serial.println("Success sending message."); else Serial.println("Failed sending message."); client.loop(); }打开串口,成功收到连接消息。打开云平台,成功看到设备在线。同时也能获得设置的各个属性信息。3.6 执行设备由于我选择了USB灯管,但ESP32无法驱动USB(除非转接),不得不以一种不甚优雅的方式通过树莓派间接驱动这些执行设备。大家只要选型选好就不存在这种两个控制器的问题,这里把树莓派理解成一种驱动器即可,它通过读ESP32的信号来点灯和驱动风扇。下面代码仅供参考import RPi.GPIO as GPIO #------------------------------------------------------# # @breif: 执行设备 #-------------------------------------------------------# class Exe: def __init__(self): self.light = 11 # 引脚11接灯 self.fan = 13 # 引脚13接风扇 self.esp = 15 # 引脚15接ESP32 GPIO.setmode(GPIO.BOARD) GPIO.setup(self.light,GPIO.OUT) GPIO.output(self.light,GPIO.LOW) GPIO.setup(self.fan ,GPIO.OUT) GPIO.output(self.fan ,GPIO.LOW) # @breif:驱动 def run(self): if GPIO.input(self.esp): GPIO.output(self.light, GPIO.HIGH) GPIO.output(self.fan , GPIO.HIGH) else: GPIO.output(self.light, GPIO.LOW) GPIO.output(self.fan , GPIO.LOW)4 更进一步写了个简单的网页来实时监测、可视化。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》…
目录1 误差理论的基本概念2 误差的类型2.1 随机误差2.2 系统误差2.3 粗大误差(野差)3 误差的合成4 误差的传递5 误差理论例题1 误差理论的基本概念序号 概念 含义1 测量 以确定被测量为目标而进行的一组操作,是把未知被测量与已知标准量进行比对的过程2 测量值(示值或读数) 由测量器具或检测仪器指示或显示的被测参量数值,包括数值和单位3 真值 一个物理量在一定条件下所呈现的客观大小或真实数值4 测量误差 实际测量中由于测量器具不准确、测量手段不完善、各种环境或人为因素等导致的测量值与真值的偏差,有两种表示方法:绝对测量误差 ;相对测量误差5 约定真值 由国际协议、国家标准测量的某物理量值,一般可代替真值,如标准重力加速度、基准米等6 相对真值 由于无法直接和国家标准比对(如严格定义的基准米长度),在量值传递中,当高一级标准仪器的测量误差仅为低一级的1/3及以下时,可认为高一级标准仪器的测量值为低一级的相对真值7 标称值 计量或测量器具上标定的数值。由于制造、测量精度不足及环境等因素的影响,标称值并非真值,因此给出标称值的同时还要标出误差范围或准确度等级8 多次测量 在相同条件下,用同一测量仪器对同一被测量进行多次重复测量的过程。通常要求较高精密测量都须进行多次测量,如仪表的比对、校准等9 精密度 表征测量仪器输出值的分散性10 准确度 表征测量仪器输出值与真值的偏离程度11 精度(精确度) 加权综合考量精密度与准确度只要存在测量,就必然存在测量误差——测量误差不能完全消除,因此误差理论的目标就是在给定精度范围内尽可能减小测量误差。测量误差分为随机误差、系统误差、粗大误差三种,下面将分别讨论其产生原因和消除方法。2 误差的类型2.1 随机误差在相同条件下,用同一测量仪器对同一被测量进行多次重复测量,测量误差中绝对值和符号都以不可预知的方式变化的误差分量,称为随机误差或偶然误差,定量表示为ε r i = x i − E x \varepsilon _{ri}=x_i-E_xε ri =x i −E x其中 E x = lim n → ∞ 1 n ∑ i = 1 n x i E_x=\underset{n\rightarrow \infty}{\lim}\frac{1}{n}\sum_{i=1}^n{x_i} E x = n→∞lim n1 ∑ i=1n x i 为观测值数学期望。随机误差服从正态分布,一般可认为 ε r i N ( 0 , σ 2 ) \varepsilon _{ri} ~ N\left( 0,\sigma ^2 \right) ε ri N(0,σ 2 ),因此随机误差也满足 3 σ 3\sigma 3σ原则,定义 Δ = 3 σ \varDelta =3\sigma Δ=3σ为极限误差,超过 Δ \varDelta Δ范围的可以认为属于粗大误差,予以剔除。随机误差分布的标准差 σ \sigma σ表征了测量输出的精密度,标准差 σ \sigma σ越小表明仪器测量的精密度越高,相应的随机误差也越小。随机误差分布均值为0具有对称性,因此随机误差具有补偿性,定量表述为lim n → ∞ ∑ i = 1 n ε r i = lim n → ∞ ( ∑ i = 1 n x i − n E x ) = 0 \underset{n\rightarrow \infty}{\lim}\sum_{i=1}^n{\varepsilon _{ri}}=\underset{n\rightarrow \infty}{\lim}\left( \sum_{i=1}^n{x_i}-nE_x \right) =0n→∞lim i=1∑n ε ri = n→∞lim ( i=1∑n x i −nE x )=0该式在 n n n为有限值时近似但不严格为0,且并无实际意义。假设已剔除粗大误差,则测量值 x i = A 0 + ε + ε r i x_i=A_0+\varepsilon +\varepsilon _{ri} x i =A 0 +ε+ε ri 为系统真值、随机误差和系统误差的叠加,对两侧取无穷次采样的均值可得E x = A 0 + ε E_x=A_0+\varepsilonE x =A 0 +ε证明可以通过增加采样次数的方式消除随机误差。以上讨论基于理想大样本,在有限样本下不能应用数学期望,而仅能得到数学期望估计值——样本均值 x ˉ = 1 n ∑ i = 1 n x i \bar{x}=\frac{1}{n}\sum_{i=1}^n{x_i} xˉ = n1 ∑ i=1n x i ,且 x ˉ \bar{x} xˉ 为服从正态分布的随机变量。此时随机误差亦为估计值,称作样本残差 v i = x i − x ˉ v_i=x_i-\bar{x} v i =x i − xˉ 。同样,样本残差也严格遵守补偿性∑ i = 1 n v i = ∑ i = 1 n x i − n x ˉ = 0 \sum_{i=1}^n{v_i}=\sum_{i=1}^n{x_i}-n\bar{x}=0i=1∑n v i = i=1∑n x i −n xˉ =0在有限样本下造成统计数据自由度降低,需要使用贝塞尔公式修正样本标准差为σ ^ = 1 n − 1 ∑ i = 1 n v i 2 \hat{\sigma}=\sqrt{\frac{1}{n-1}\sum_{i=1}^n{v_{i}^{2}}}σ^ = n−11 i=1∑n v i2可以证明,样本均值标准差的最佳估计值为σ ^ x ˉ = σ ^ n \hat{\sigma}_{\bar{x}}=\frac{\hat{\sigma}}{\sqrt{n}}σ^ xˉ = nσ^σ ^ x ˉ \hat{\sigma}_{\bar{x}} σ^ xˉ 作为精密度 σ \sigma σ的最佳估计值,即 E x = x ˉ ± 3 σ ^ x ˉ E_x=\bar{x}\pm 3\hat{\sigma}_{\bar{x}} E x = xˉ ±3 σ^ xˉ ,若进一步消除系统误差,则真值 A 0 = x ˉ ± 3 σ ^ x ˉ A_0=\bar{x}\pm 3\hat{\sigma}_{\bar{x}} A 0 = xˉ ±3 σ^ xˉ 。当采样数 n n n不断增大时,样本标准差趋于0,即观测数据在样本均值两侧分布越来越均匀;同时样本均值标准差也趋于0,即观测数据的均值越来越趋于真实的数学期望(消除系统误差后也就越来越趋于真值),如图所示。随机误差产生的主要原因有:测量装置方面——仪器噪声、零部件摩擦、接触不良等;环境方面:温度等环境参数波动、电源电压的无规则波动、电磁干扰、振动等;测量人员感官的无规则变化造成的读数不稳等。2.2 系统误差在相同条件下,用同一测量仪器对同一被测量进行多次重复测量,测量误差中绝对值和符号都保持不变,或在测量条件改变时按一定规律变化的误差分量,称为系统误差,定量表示为ε = E x − A 0 \varepsilon =E_x-A_0ε=E x −A 0例如仪器的刻度误差和零位误差。系统误差总是存在且不能通过多次测量取平均的方式消除,当系统误差较小时可以忽略其影响,即 E x ≈ A 0 E_x\approx A_0 E x ≈A 0 。系统误差主要分为累进型和周期型如图所示。总系统误差可忽略不计的准则是:残差代数和的绝对值不超过测量结果扩展不确定度的最后一位有效数字的一半。系统误差产生的主要原因是:仪器制造、安装或使用方法不正确,环境因素(温度、湿度、电源等)影响等。消除系统误差应尽可能避免上述导致系统误差的诱因。对于无法克服的系统误差,可采用修正方法对测量结果进行补偿,修正曲线可以通过具体的误差原理得出,也可通过数值方法拟合。由于修正值本身也有误差,所以测量结果经修正后更接近真值但仍非真值。2.3 粗大误差(野差)在相同条件下,用同一测量仪器对同一被测量进行多次重复测量,测量误差中明显离群的误差分量,称为粗大误差,记为 ε e \varepsilon _e ε e 。粗大误差出现的概率很小,可按下面方法判断测量值是否为粗大误差。莱特准则本质上是 3 σ 3\sigma 3σ原则,若 ∣ v i ∣ > 3 σ ^ \left| v_i \right|>3\hat{\sigma} ∣v i ∣>3 σ^ ,则认为 v i v_i v i 为粗大误差,予以剔除。格拉布斯准则若 ∣ v i ∣ > g σ ^ \left| v_i \right|>g\hat{\sigma} ∣v i ∣>g σ^ ,则认为 v i v_i v i 为粗大误差,予以剔除。 g g g由实验次数和置信度确定。粗大误差产生原因有:测量人员的主观原因——操作失误或错误记录;客观外界条件的原因:测量条件意外改变、受较大的电磁干扰,或测量仪器偶然失效等。3 误差的合成剔除粗大误差后,测量误差可表示为δ = ε ± e ± Δ \delta =\varepsilon \pm e\pm \varDeltaδ=ε±e±Δ其中 ε \varepsilon ε为已定系统误差, e e e为未定系统误差, Δ \varDelta Δ为随机误差的极限误差。已定系统误差,指系统误差中大小、方向和变化规律可以完全掌握的误差分量。已定系统误差的合成采用代数方法,即ε = ∑ i = 1 n ε i \varepsilon =\sum_{i=1}^n{\varepsilon _i}ε= i=1∑n ε i由于可对已定系统误差进行直接修正,一般最后测量结果不应含有已定系统误差分量。未定系统误差,指系统误差中不可预测或难以掌握的误差分量。通常未定系统误差多以极限误差的形式给出误差的最大变化范围,其合成采用{ L 1 合成: ε = ± ∑ i = 1 m ∣ ε i ∣ , m < 10 L 2 合成: ε = ± ∑ i = 1 m ε i 2 , m > 10{L1合成:ε=±∑mi=1|εi|,m<10L2合成:ε=±∑mi=1ε2i−−−−−−√,m>10{L1合成:ε=±∑i=1m|εi|,m<10L2合成:ε=±∑i=1mεi2,m>10{ L 1 合成:ε=±∑ i=1m ∣ε i ∣,m<10L 2 合成:ε=± ∑ i=1m ε i2 ,m>10随机误差的合成采用 L 2 L_2 L 2 范数合成,即σ = ∑ i = 1 m σ i 2 ⇔ Δ = ∑ i = 1 m Δ i 2 \sigma =\sqrt{\sum_{i=1}^m{\sigma _{i}^{2}}}\Leftrightarrow \varDelta =\sqrt{\sum_{i=1}^m{\varDelta _{i}^{2}}}σ= i=1∑m σ i2 ⇔Δ= i=1∑m Δ i2 4 误差的传递有些物理量无法直接测量而采用间接测量推算法,例如串联电阻值、板材硬度值等,用 y = f ( x 1 , x 2 , ⋯ , x n ) y=f\left( x_1,x_2,\cdots ,x_n \right) y=f(x 1 ,x 2 ,⋯,x n )描述。已知间接测量函数关系及各个测量值误差,可以计算间接被测量的误差。更进一步,可以根据各个直接测量量对间接误差的影响,确定最佳测量条件,使间接误差达到最小。将多元函数泰勒展开并略去高阶项可得绝对误差Δ y = ∑ i = 1 m ∂ f ∂ x i Δ x i \varDelta y=\sum_{i=1}^m{\frac{\partial f}{\partial x_i}\varDelta x_i}Δy= i=1∑m ∂x i∂f Δx i以及相对误差γ y = Δ y y = ∑ i = 1 m ∂ f ∂ x i Δ x i y \gamma _y=\frac{\varDelta y}{y}=\sum_{i=1}^m{\frac{\partial f}{\partial x_i}\frac{\varDelta x_i}{y}}γ y = yΔy = i=1∑m ∂x i∂f yΔx i对直接测量量的已定分量可按上式计算间接测量量的绝对误差和相对误差,未定分量和随机分量则按误差合成公式计算。5 误差理论例题基于误差与测量理论回答下面问题:如图所示用弓高弦长法间接测量直径 D D D,其中直接测得弓高 h h h和弦长 l l l,通过函数关系 D = s 2 4 h + h D=\frac{s^2}{4h}+h D= 4hs 2 +h计算。若弓高与弦长测量值及其系统误差分别为 h = 50 m m − 0.1 m m h=50mm-0.1mm h=50mm−0.1mm、 s = 500 m m + 1 m m s=500mm+1mm s=500mm+1mm,求直径测量结果。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
1 什么是内核2 什么是操作系统?3 操作系统和内核的区别?4 Linux内核1 什么是内核内核是操作系统的核心。内核是操作系统执行的第一道程序,被率先加载到内存中开始系统行为。内核始终保持在主内存中直到系统被关闭。内核将用户输入的命令转换成计算机硬件能理解的机器语言。内核是系统应用软件和硬件的桥梁。内核直接与硬件联系,并告之它由应用软件发起的请求。操作系统不能脱离内核工作,内核是系统正常运行最重要的程序。内核的主要职责是:进程管理、磁盘管理、任务调度、内存管理等,具体如下:文件管理为了更有效地搜索和使用文件,内核使用文件系统(file system)来组织文件,并通过文件系统保持对文件数据存储、文件状态、访问设置的监视。进程管理在多进程环境下,内核决定哪一道进程被CPU优先运行,以及分配的运行时间片长度是多少,称为进程调度。当进程不再被需要的时候,将被内核自动销毁。内存管理内核检测内存空间——生成或销毁内存,以确保应用程序被正确执行。内核分为单核(Monolithic Kernel)和微核(Microkernel)两种。对于单核,所有操作系统服务运行在单核的主线程中,单核提供了对系统硬件的广泛访问。对于微核,其提供的是硬件抽象,意味着操作系统必须在系统调用(system calls)和原语(primitives)的帮助下工作。2 什么是操作系统?操作系统(operating system)是用来管理计算机系统资源的软件,内核是用户和系统硬件的桥梁。操作系统提供的接口允许用户直接看到其输入命令的响应结果,例如Window的命令行cmd和Linux的Shell终端。没有操作系统,系统就不可能运行,部分嵌入式系统看似没有操作系统,但仍然对硬件作了一层简单封装,也可理解为Tiny OS。操作系统的主要职责是创建应用软件可以运行的环境。操作系统同样是运行在计算机系统中的持久化程序,直至系统关闭。它是计算机系统运行的第一道程序,一旦操作系统被加载到内存,计算机就做好了执行用户程序的准备。在操作系统中,内核是最重要的程序。除了内核的职责外,操作系统额外负责安全性与隐私、中断与挂起等服务,具体如下:安全性为了保护用户数据安全,操作系统对计算机进行了密码保护,保护程序不被非法途径泄露。工作分析操作系统跟踪资源的使用情况,这些分析数据可以用来监视、反映资源对特定用户或用户群体的利用率,便于系统调整。与用户和其他软件合作操作系统也向用户分配解释器、汇编、编译器和其他系统级软件,便于用户和其他应用调用接口。控制系统性能为控制系统性能,操作系统时刻监视其运行状态,最主要是测量应用发起服务接口请求,和系统返回响应之间的时间。在操作系统的帮助下,通过提供解决问题的关键性信息可以提供系统性能。错误自检操作系统密切监测系统漏洞来防止运行崩溃。设备管理操作系统保持对所有接入计算机的硬件设备的监视和跟踪,决定了每个外设是否可以访问计算机资源以及访问的允许时长是多少。3 操作系统和内核的区别?序号 内核 操作系统1 系统级软件,操作系统的一部分 系统级软件2 应用软件和硬件的接口 用户和硬件的接口3 运行操作系统必须具有内核 运行计算机系统必须有操作系统4 分为单核和微核两种 分为分布式OS、实时OS、单核OS、微核OS、多处理OS5 是操作系统执行的第一道程序 是计算机系统执行的第一道程序6 主要负责进程管理、磁盘管理、任务调度、内存管理等核心任务 主要负责安全性与隐私、中断与挂起等其他任务4 Linux内核20世纪80年代,受限于Unix 版权,赫尔辛基大学Andrew S. Tanenbaum教授仿UNIX开发了MINIX操作系统,并开放全部源代码给大学教学和研究工作。Linus Torvalds在该学校读书时也使用了MINIX系统,但由于无法忍受MINIX带来的不便,决定自行开发操作系统,并于1991年9月发布了第一版Linux操作系统内核。Linux内核发布时,市面上已有许多GNU组件,唯独缺少底层内核。由于Linus本人是GNU计划的忠实拥护者,因此他将内核开源到GNU计划中完善GNU生态。基于Linux内核,并使用GNU各种工具和应用程序打造的操作系统称为GNU/Linux。将Linux内核与其他应用工具打包形成的操作系统称为Linux发行版,其中包含开源的GNU/Linux,也有商业闭源的操作系统。
Python视频制作引擎Manim安装教程2021版0 写在前面1 效果展示2 安装教程(Windows)2.1 安装ffmpeg2.2 安装Latex2.3 安装dvisvgm2.4 安装Manim3 测试与开发0 写在前面相信很多同学就算没听过3Blue1Brown,也一定曾看过他们出品的视频,其从独特的视觉角度解说各种数学概念,内容包括线性代数、微积分、神经网络、傅里叶变换以及四元数等晦涩难懂的知识点。例如最火的《线性代数本质》系列视频。那么这些视频是如何制作的呢?这里需要引入的是Python的Manim视频支持引擎——专门用于支持数学可视化的媒体引擎,通过Manim并结合Python编程就可以实现3Blue1Brown的视频效果。本文给出Manim最新发行版的安装教程,因为网上的教程基本都过时了,容易踩坑。1 效果展示动画1:动画2:(动图始终超过大小,放不上来)2 安装教程(Windows)2.1 安装ffmpeg进入ffmpeg官网,点击如图所示的按钮接着下载安装包下载完后直接解压,并设置环境变量2.2 安装Latex进入官网MikTex官网,下载对应操作系统的安装包。解压后运行安装程序.exe即可(环境变量会自动配置)2.3 安装dvisvgm进入官网dvisvgm下载相应操作系统的安装包,解压后运行安装程序即可。2.4 安装Manim通过git bash运行下面命令git clone https://github.com/3b1b/manim.git cd manim # 安装python依赖 pip install -e . python -m pip install -r requirements.txt2021版重点:错误复现如下LaTeX Error! Not a worry, it happens to the best of us. Traceback (most recent call last): File "D:\Program Files\Python3\Scripts\manimgl-script.py", line 33, in <module> sys.exit(load_entry_point('manimgl', 'console_scripts', 'manimgl')()) File "d:\public\manim\manimlib\__main__.py", line 17, in main scene.run() File "d:\public\manim\manimlib\scene\scene.py", line 75, in run self.construct() File "example_scenes.py", line 29, in construct IntegerMatrix(matrix, include_background_rectangle=True), File "d:\public\manim\manimlib\mobject\matrix.py", line 81, in __init__ self.add_brackets() File "d:\public\manim\manimlib\mobject\matrix.py", line 111, in add_brackets bracket_pair = Tex("".join([ File "d:\public\manim\manimlib\mobject\svg\tex_mobject.py", line 167, in __init__ super().__init__(full_string, **kwargs) File "d:\public\manim\manimlib\mobject\svg\tex_mobject.py", line 42, in __init__ filename = tex_to_svg_file(full_tex) File "d:\public\manim\manimlib\utils\tex_file_writing.py", line 54, in tex_to_svg_file tex_to_svg(tex_file_content, svg_file) File "d:\public\manim\manimlib\utils\tex_file_writing.py", line 62, in tex_to_svg svg_file = dvi_to_svg(tex_to_dvi(tex_file)) File "d:\public\manim\manimlib\utils\tex_file_writing.py", line 97, in tex_to_dvi with open(log_file, "r") as file: FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\Tex\\cf5d7f9f2e57398a.log'该问题的最终解决方案是配置manim/manimlib/default_config.yml的缓存路径,其中"D:\\AIProject\\test\\manim\\tex"是manim目录下新建的一个空文件夹,用来存放tex输出文件。3 测试与开发进入manim目录下运行:manimgl example_scenes.py OpeningManimExample即可得到动画2的效果。新建main.py文件,运行下面代码from manimlib import * class GraphExample(Scene): def construct(self): axes = Axes((-3, 10), (-1, 8)) axes.add_coordinate_labels() self.play(Write(axes, lag_ratio=0.01, run_time=1)) # Axes.get_graph will return the graph of a function sin_graph = axes.get_graph( lambda x: 2 * math.sin(x), color=BLUE, ) # By default, it draws it so as to somewhat smoothly interpolate # between sampled points (x, f(x)). If the graph is meant to have # a corner, though, you can set use_smoothing to False relu_graph = axes.get_graph( lambda x: max(x, 0), use_smoothing=False, color=YELLOW, ) # For discontinuous functions, you can specify the point of # discontinuity so that it does not try to draw over the gap. step_graph = axes.get_graph( lambda x: 2.0 if x > 3 else 1.0, discontinuities=[3], color=GREEN, ) # Axes.get_graph_label takes in either a string or a mobject. # If it's a string, it treats it as a LaTeX expression. By default # it places the label next to the graph near the right side, and # has it match the color of the graph sin_label = axes.get_graph_label(sin_graph, "\\sin(x)") relu_label = axes.get_graph_label(relu_graph, Text("ReLU")) step_label = axes.get_graph_label(step_graph, Text("Step"), x=4) self.play( ShowCreation(sin_graph), FadeIn(sin_label, RIGHT), ) self.wait(2) self.play( ReplacementTransform(sin_graph, relu_graph), FadeTransform(sin_label, relu_label), ) self.wait() self.play( ReplacementTransform(relu_graph, step_graph), FadeTransform(relu_label, step_label), ) self.wait() parabola = axes.get_graph(lambda x: 0.25 * x**2) parabola.set_stroke(BLUE) self.play( FadeOut(step_graph), FadeOut(step_label), ShowCreation(parabola) ) self.wait() # You can use axes.input_to_graph_point, abbreviated # to axes.i2gp, to find a particular point on a graph dot = Dot(color=RED) dot.move_to(axes.i2gp(2, parabola)) self.play(FadeIn(dot, scale=0.5)) # A value tracker lets us animate a parameter, usually # with the intent of having other mobjects update based # on the parameter x_tracker = ValueTracker(2) f_always( dot.move_to, lambda: axes.i2gp(x_tracker.get_value(), parabola) ) self.play(x_tracker.animate.set_value(4), run_time=3) self.play(x_tracker.animate.set_value(-2), run_time=3) self.wait()即可得到动画1的效果。
目录1 数据规范化1.1 最值归一化1.2 Z-Score规范化2 类别平衡化2.1 阈值移动2.2 欠采样法(undersampling)2.3 过采样法(oversampling)3 连续值离散化4 缺失值处理5 哑言编码6 正则化6.1 L1正则6.2 L2正则7 数据降维1 数据规范化量纲,指将一个物理导出量用若干基本量的乘方之积表示出来的表达式。数据的比较需要关注两点——绝对数值和量纲,而特征间因为量纲的存在导致无法直接通过绝对数值比较大小,也就无法判断特征间的重要性。例如若某个特征的方差比其他特征大几个数量级,那么它就会在学习算法中占据主导位置而弱化了其他特征,甚至导致模型无法收敛。无量纲化Nondimensionalization)的数据预处理方式可以让特征间拥有相同权重——从绝对数值比较转换为相对数值比较,不再受量纲影响,从而提高模型精度、稳定性,加快收敛速度。无量纲化的主要方式是规范化(Standardization),即将不同数值变化范围的分布映射到相同的固定范围。特别地,当映射到0-1区间时称为归一化(Normalization)。1.1 最值归一化核心是通过样本特征最值,将特征数值线性映射到0-1区间,且不破坏分布情况,转化函数如下:X s c a l e = X − X min X max − X min X_{scale}=\frac{X-X_{\min}}{X_{\max}-X_{\min}}X scale = X max −X minX−X min其特点是:算法过程简单直观;新数据加入可能导致最值变化,需要重新定义;对奇异值(Outlier)非常敏感,因为其直接影响最值。故最值归一化只适用于数据在一个范围内分布而不会出现Outlier的情况,如人的身高数据、考试成绩数据等1.2 Z-Score规范化核心是将所有数据映射到均值为0,方差为1的分布中(但并不限制在 区间内),转化函数如下:X s c a l e = X − μ σ X_{scale}=\frac{X-\mu}{\sigma}X scale = σX−μ其特点是:很好地契合本身就服从正态分布的数据;即使原数据集中有Outlier,规范化的数据集依然满足均值为0,不会形成有偏数据;对Outlier敏感度较低,但在计算方差和均值的时候Outliers仍会影响计算。所以在出现Outliers的情况下可能会出现转换后,数据的不同维度分布完全不同的情况。2 类别平衡化类别不平衡(class-imbalance)指分类任务中不同类别的训练样本数目差别很大的情况,例如训练集中有998个反例,但正例只有2个。类别不平衡下训练处的学习器往往没有价值,因为其只需始终判断为大样本数的类别就能取得很小的训练误差。解决类别不平衡问题的策略主要有:2.1 阈值移动亦称再平衡(rebalance)或再缩放(rescaling)策略。设训练集中有 m + m^+ m + 个正例和 m − m^- m − 个反例,学习器输出预测正例的概率为 y y y。假设训练集是真实样本空间的采样,则对于任意测试样本,令y ′ 1 − y ′ = y 1 − y × m − m + {\frac{y'}{1-y'}=\frac{y}{1-y}\times \frac{m^-}{m^+}}1−y ′y ′ = 1−yy × m +m −通过比较 y ′ y' y ′ 与0.5的大小来判定正、反例。2.2 欠采样法(undersampling)核心原理是去除样本数较多类别中的部分样例达到类别平衡。欠采样法因丢失样例而减小了时间开销,但要注意防止欠拟合。欠采样的代表性算法是EasyEnsemble。2.3 过采样法(oversampling)核心原理是增加样本数较少类别中的部分样例达到类别平衡。过采样法因增加样例而增大了时间开销,但要注意防止过拟合。过采样法的代表性算法是SMOTE。3 连续值离散化连续属性离散化(Discretization of Continuous Attributes)是指将连续数据分段为一系列离散化区间,每个区间对应一个属性值。连续属性离散化的主要原因如下:算法要求,例如分类决策树等基于分类属性的算法;提高特征在知识层面的表达能力,例如年龄5岁和65岁两个特征,对于连续型需要进行数值层面的大小比较,但若将其映射为“幼年”和“老年”则更直观;离散化数据对异常离群值有更强的鲁棒性,能提高模型稳定性。连续属性离散化的主要方法阐述如下。无监督离散方法等距离散化,即将连续属性划分为若干有限区间,每个区间长度相等。等频离散化,即将连续属性划分为若干有限区间,每个区间样本数相同。有监督离散方法信息增益法,是一种二分法(bi-partition),核心是以离散前后信息增益最大的点为二分位点。4 缺失值处理侦测成本过高、隐私保护、无效数据、信息遗漏等情况都会造成实际应用时数据集属性缺失,因此缺失值处理不可避免。缺失值处理的主要方式阐述如下。插值填充,即用已有数据的分布来推测缺失值。例如均值填充(主要针对连续型属性)、众数填充(主要针对离散型属性)、回归填充(基于已有属性值建立回归方程估计缺失值)等。相似填充,即用和缺失属性样本相似的若干样本推测缺失值。例如热卡填充(Hot Deck Imputation),基于某种相似度度量选择数据集中与之最相似的样本属性代替缺失值;聚类填充,基于聚类分析选择数据集中与之最相似的样本子集进行插值填充。C4.5方法,直接使用缺失属性样本,用加权方式衡量样本对结果的影响,主要用于决策树算法。决策树算法可参考 Python 机器学习实战(一):手撕决策树的原理、构造、剪枝、可视化5 哑言编码哑言编码(Dummy Encode)面向离散型特征,是将一组定性离散特征的特征值以0-1方式向量化、定量化的编码方式。哑言编码的优势在于:稀疏化数据,稀疏向量运算速度快,且优化方式多;提高模型表达能力,哑言编码相当于为模型引入非线性环节,提高模型容量;无量纲化与定量化,将不同类型特征都量化为0-1向量进行形式化表达,便于推理和演算。哑言编码的缺点在于:由于不同特征的哑言编码相互堆叠,最终形成的特征向量会导致特征空间产生维数灾难(The Curse of Dimensionality),因此一般可用PCA降维配合哑言编码。具体地,哑言编码有两种形式,如图1.2.8所示采用 N N N位状态寄存器来对 N N N个状态进行编码,每个状态都有独立的寄存器位,并且在任意时候只有一位有效的编码方式称为独热编码(One-Hot Encode),若减少一个自由度则是一般的哑言编码。6 正则化正则化(Regularization)是在模型经验风险(empirical risk)最小化的基础上引入结构风险(structural risk)最小化的策略,正则化为引入领域先验知识、训练意图提供途径,也是常用避免过拟合的惩罚函数方法。正则化的一般表达式如下:min λ f Ω ( f ) + ∑ i = 1 m ℓ ( f ( x i ) , y i ) \underset{f}{\min \lambda}\Omega \left( f \right) +\sum_{i=1}^m{\ell \left( f\left( \boldsymbol{x}_i \right) ,y_i \right)}fminλ Ω(f)+ i=1∑m ℓ(f(x i ),y i )其中 Ω ( f ) \Omega \left( f \right) Ω(f)为正则化项,用于描述模型 的某些性质以降低结构风险; ∑ i = 1 m ℓ ( f ( x i ) , y i ) \sum\nolimits_{i=1}^m{\ell \left( f\left( \boldsymbol{x}_i \right) ,y_i \right)} ∑ i=1m ℓ(f(x i ),y i )为经验风险项,用于描述模型与训练数据的契合程度;常数 λ \lambda λ表示对结构风险和经验风险的偏好。总结常用的正则化方式:6.1 L1正则由于 L 1 L^1 L 1 范数正则下,最优解多出现于正则项的棱边棱角处产生稀疏性,故 范数正则也称稀疏规则算子(Lasso Regularization),可引入对稀疏参数的偏好,有利于突出重点特征以进行特征选择。事实上, L 0 L^0 L 0 范数也可实现特征选择和稀疏化,但其相对 L 1 L^1 L 1 范数而言不易于求解优化,因此实际应用中更倾向于使用 L 1 L^1 L 1 范数。6.2 L2正则L 2 范数正则也称为权重衰减(Weight Decay),其偏好于保留更多特征,且这些特征更均匀(趋于0)。在回归分析中, L 1 L^1 L 1 范数正则化的代价函数称为岭回归(Ridge Regression)。如图所示,仅考虑两个特征。若 λ → 0 \lambda→0 λ→0即正则约束小,则正则项圆锥高减小,正则解趋近于最小二乘解;若增大 λ \lambda λ即正则约束大,则正则项圆锥高增加,正则解偏离最小二乘解,解的位置越来越靠近 轴,参数越来越小。7 数据降维主要介绍PCA降维。如图所示,数据点大部分都分布在 x 2 x_2 x 2 方向上,在 x 1 x_1 x 1 方向上的取值近似相同,那么对于某些问题就可以直接去除 x 1 x_1 x 1 坐标,而只保留 x 2 x_2 x 2 坐标值即可。但是有些情况下不能直接这样处理,例如图中数据在 x 1 x_1 x 1 和 x 2 x_2 x 2 方向上分布较均匀,任意去除一个维度可能对结果都会有很大影响。此时需要通过PCA原理,找出某个使数据分布最分散——方差最大的维度,即图中的红色坐表系以便达到降维的目的。从上面的实例中可以归纳出PCA算法的优化目标:选择的特征维度间相关性要尽可能小——降低计算维度,减少计算成本;保留的特征维度要尽量反映数据的本质——该维度方差最大;这两个优化目标可以用协方差矩阵统一起来:C = [ C o v ( α 1 , α 1 ) C o v ( α 1 , α 2 ) ⋯ C o v ( α 1 , α n ) C o v ( α 2 , α 1 ) C o v ( α 2 , α 2 ) ⋯ C o v ( α 2 , α n ) ⋮ ⋮ ⋱ ⋮ C o v ( α n , α 1 ) C o v ( α n , α 2 ) ⋯ C o v ( α n , α n ) ] → 降维后理想的协方差矩阵 [ δ 1 δ 2 ⋱ δ n ] C=\left[ \right] \xrightarrow{\text{降维后理想的协方差矩阵}}\left[ \right]C= ⎣⎢⎢⎢⎡ Cov(α 1 ,α 1 )Cov(α 2 ,α 1 )⋮Cov(α n ,α 1 ) Cov(α 1 ,α 2 )Cov(α 2 ,α 2 )⋮Cov(α n ,α 2 ) ⋯⋯⋱⋯ Cov(α 1 ,α n )Cov(α 2 ,α n )⋮Cov(α n ,α n ) ⎦⎥⎥⎥⎤ 降维后理想的协方差矩阵 ⎣⎢⎢⎡ δ 1 δ 2 ⋱ δ n ⎦⎥⎥⎤基于此,设 X m × n X_{m\times n} X m×n 为样本中心化矩阵, P r × m P_{r\times m} P r×m 为PCA降维矩阵, Y r × n = P X Y_{r\times n}=PX Y r×n =PX为降维后的样本矩阵, C X C_X C X 、 C Y C_Y C Y 分别为原样本和降维后样本的协方差矩阵。因为这里考虑不同特征间的相关性,所以将矩阵统一写为行向量组的形式: Y r × n = [ β 1 β 2 ⋯ β r ] T Y_{r\times n}=\left[ \right] ^T Y r×n =[ β 1 β 2 ⋯ β r ] T ,则:max t r ( C Y ) s . t . P T P = I \max tr\left( C_Y \right) \,\, s.t. P^TP=Imaxtr(C Y )s.t.P T P=I前者体现优化目标(a),后者体现优化目标(b)。下面简单推导PCA降维矩阵的条件。max t r ( C Y ) = max t r [ 1 n − 1 P X ( P X ) T ] = max t r [ P C X P T ] \max tr\left( C_Y \right) =\max tr\left[ \frac{1}{n-1}PX\left( PX \right) ^T \right] \\=\max tr\left[ PC_XP^T \right]maxtr(C Y )=maxtr[ n−11 PX(PX) T ]=maxtr[PC X P T ]由拉格朗日乘数法,设 f ( P ) = t r ( P C X P T ) + λ ( P T P − I ) f\left( P \right) =tr\left( PC_XP^T \right) +\lambda \left( P^TP-I \right) f(P)=tr(PC X P T )+λ(P T P−I)则:∂ f ( P ) ∂ P = ∂ t r ( P C X P T ) ∂ P + λ ∂ ( P T P ) ∂ P = P C X T + λ P \frac{\partial f\left( P \right)}{\partial P}=\frac{\partial tr\left( PC_XP^T \right)}{\partial P}+\lambda \frac{\partial \left( P^TP \right)}{\partial P}\\=PC_{X}^{T}+\lambda P∂P∂f(P) = ∂P∂tr(PC X P T ) +λ ∂P∂(P T P)=PC XT +λP令导数为0,则C X P T = − λ P T C_XP^T=-\lambda P^TC X P T =−λP T即降维矩阵 P P P是以原始样本协方差矩阵 C X C_X C X 的前 r r r个特征向量为行向量组的正交矩阵。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 什么是Openpose?2 Windows下配置Openpose2.1 下载openpose2.2 配置第三方库与模型2.3 编译Openpose2.4 生成项目与测试3 Python调用Openpose4 附录: Ubuntu下配置Openpose4.1 下载Openpose并配置第三方4.2 编译openpose4.3 测试1 什么是Openpose?OpenPose是在单图像上联合检测人体、手、面部和脚关键点(总共135个关键点)的实时多人系统。抖音“尬舞”机就应用了人体关键点检测技术:就像玩真实的跳舞机一样,屏幕里伴随着音乐会不断出现不同动作图形,用户需要及时摆出对应的动作才能得分,随着动作的进程系统会发出perfect 、good以及连击音效。应用openpose检测出人体关键点,再和预设舞蹈的关键点作比较,就可以判定得分,实现上述功能。2 Windows下配置Openpose2.1 下载openpose新建空白目录,使用git bash运行git clone https://github.com/CMU-Perceptual-Computing-Lab/openpose.git也可以到openpose仓库直接下载开源压缩包到本地。2.2 配置第三方库与模型下载openpose完毕后进入openpose\3rdparty\windows依次点击下面的.bat文件安装第三方库。安装完毕如下所示:进入openpose\model点击getModels.bat下载模型,下载完成后如下2.3 编译Openpose在openpose根目录下新建文件夹build,打开CMake GUI,源文件选择openpose根目录,编译文件选择build文件夹,如下在Build中勾选下面的选项:其中BUILD_CAFFE用于编译caffe库,BUILD_PYTHON用于编译Python接口,后面想使用Python调用Openpose务必要勾选(有的教程怕出错不勾选)。CUDA方面之前配置过会自动链接并显示版本号的,没有GPU的同学可以勾选CPU_ONLY用CPU来跑这个模型,但是运行不流畅。没有安装CUDA的同学先行安装CUDA以及对应版本的Cudnn。之后点击Configure按钮,在Windows下请选择对应Visual Studio版本号和PC位数;在Ubuntu下请选择Unix MakeFileConfigure完成后如下:因为勾选了BUILD_PYTHON,所以CMake为链接到本机上的Python编译器。最后点击Generate即可。2.4 生成项目与测试点击CMake中的Open Project进入VS,右键项目重新生成解决方案。经过一段时间的编译后,会在build/x64/Release下生成可执行文件。把build\bin中的*.dll以及根目录下的model复制到build/x64/Release中,即可测试实例。3 Python调用Openpose再新建一个文件夹用于测试Python API。将openpose\build\bin、openpose\models、openpose\build\x64\Release目录复制进来,把Openpose\build\python\openpose的三个文件复制进来,否则会报错。新建一个test.py文件用于测试接口,下面的例程需要插入一个USB摄像机,没有设备的话可以把cv2.imread()替换成自己的图片。import sys import cv2 import os from sys import platform # Import Openpose (Windows/Ubuntu/OSX) dir_path = os.path.dirname(os.path.realpath(__file__)) try: # Windows Import if platform == "win32": os.environ['PATH'] = os.environ['PATH'] + ';' + dir_path + './Release;' + dir_path + './bin;' import pyopenpose as op else: # Change these variables to point to the correct folder (Release/x64 etc.) sys.path.append('../../python'); from openpose import pyopenpose as op except ImportError as e: print('Error: OpenPose library could not be found. Did you enable `BUILD_PYTHON` in CMake and have this Python script in the right folder?') raise e if __name__ == "__main__": # 设置Openpose模型并初始化 params = dict() params["model_folder"] = "./models/" opWrapper, datum = op.WrapperPython(), op.Datum() opWrapper.configure(params) opWrapper.start() # 启动摄像头 capture= cv2.VideoCapture(0) while(1): ref,frame = capture.read() # 载入当前帧到Openpose datum.cvInputData = frame opWrapper.emplaceAndPop(op.VectorDatum([datum])) # 模型输出图像 process = datum.cvOutputData # 显示 cv2.imshow("test", process) if not ref: break key = cv2.waitKey(1) & 0xFF if key == 27: print("Escape hit, closing...") break显示效果如下:在这个基础上可以进一步二次开发。4 附录: Ubuntu下配置Openpose4.1 下载Openpose并配置第三方下载openposegit clone https://github.com/CMU-Perceptual-Computing-Lab/openpose cd openpose/ git submodule update --init --recursive --remote配置opencv、caffe以及一些依赖sudo apt-get install libopencv-dev cd openpose sudo bash ./scripts/ubuntu/install_deps.sh4.2 编译openpose打开CMake-GUIcd {OpenPose_folder} mkdir build/ cd build/ cmake-gui ..其余部分同Windows过程4.3 测试# 视频 ./build/examples/openpose/openpose.bin --video examples/media/video.avi --net_resolution '160x80' # 图片 ./build/examples/openpose/openpose.bin --image_dir examples/media/ --net_resolution '160x80'🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录0 🌲写在前面1 🌲什么是决策树?2 🌲常见决策树算法2.1 👉 ID3算法2.2 👉 C4.5算法2.3 👉 CART算法3 🌲Python实现ID3决策树算法3.1 🍉架构设计3.2 🍉信息熵与信息增益计算3.3 🍉生成决策树3.4 🍉决策树可视化3.5 🍉决策树剪枝0 🌲写在前面Python 机器学习实战专题旨在基于Python实现机器学习的经典算法,例如线性回归LR、决策树DT、神经网络、支持向量机SVM等,所有源代码获取方式见文末,如有需要自行下载,🔥欢迎关注作者!Reference: 周志华老师的《机器学习》西瓜书📖1 🌲什么是决策树?决策树(decision tree, DT)模拟人类在面临决策问题时的系列判断处理机制,基于树结构对属性分而治之(divide-and-conquer)学习。一般地,决策树包含若干分支节点和叶节点,最顶层的分支节点称为根节点。分支节点进行属性划分,叶节点给出分类预测结果。决策树算法的基本形式如表所示。解释算法中的几个关键点:决策树算法中递归返回情形(2)用后验分布作为当前叶节点的分布规律;情形(3)则用父节点的先验分布作为当前叶节点的分布规律。根据 a ∗ = g e t B e s t ( A ) a_*=getBest\left( \boldsymbol{A} \right) a ∗ =getBest(A)策略的构造分为不同子算法。必须指出,若当前节点划分属性为连续属性,则该属性仍可作为子节点划分属性。暂时看不明白也没关系,下面代码实战的时候会指出每步的过程。2 🌲常见决策树算法注:下面所有算法的公式与西瓜书一致以避免参考不同资料造成的歧义性和不变性。2.1 👉 ID3算法ID3决策树算法核心原理是基于信息增益(information gain)筛选最优划分属性:a ∗ = a r g max a ∈ A G a i n ( X , a ) {a_*=\underset{a\in A}{\mathrm{arg}\max}\,\,Gain\left( \boldsymbol{X}, a \right) }a ∗ = a∈Aargmax Gain(X,a)信息增益定义为用属性 a a a对训练集 X X X进行划分后信息熵的减量,或称 X X X样本类别集合纯度的增量:G a i n ( X , a ) = E n t ( X ) − ∑ v = 1 V ∣ X v ∣ ∣ X ∣ E n t ( X v ) Gain\left( \boldsymbol{X}, a \right) =Ent\left( \boldsymbol{X} \right) -\sum_{v=1}^V{\frac{\left| \boldsymbol{X}^v \right|}{\left| \boldsymbol{X} \right|}Ent\left( \boldsymbol{X}^v \right)}Gain(X,a)=Ent(X)− v=1∑V ∣X∣∣X v ∣ Ent(X v )其中信息熵度量样本集合的类别纯度:E n t ( X ) = − ∑ k = 1 ∣ Y ∣ p k log 2 p k Ent\left( \boldsymbol{X} \right) =-\sum_{k=1}^{\left| \mathcal{Y} \right|}{p_k\log _2p_k}Ent(X)=− k=1∑∣Y∣ p k log 2 p k接下来的算法实战就是基于ID3算法2.2 👉 C4.5算法C4.5决策树算法的核心原理是基于增益率(gain ratio)筛选最优划分属性,相当于对信息增益进行关于属性 a a a粒度——即可取值数目的启发式加权,以避免信息增益偏好可能带来的不利影响:a ∗ = a r g max a ∈ A G a i n _ r a t i o ( X , a ) {a_*=\underset{a\in A}{\mathrm{arg}\max}\,\,Gain\_ratio\left( \boldsymbol{X}, a \right) }a ∗ = a∈Aargmax Gain_ratio(X,a)信息增益率定义为:G a i n _ r a t i o ( X , a ) = G a i n ( X , a ) I V ( a ) Gain\_ratio\left( \boldsymbol{X}, a \right) =\frac{Gain\left( \boldsymbol{X}, a \right)}{IV\left( a \right)}Gain_ratio(X,a)= IV(a)Gain(X,a)其中属性固有值(intrinsic value)I V ( a ) = − ∑ v = 1 V ∣ X v ∣ ∣ X ∣ log 2 ∣ X v ∣ ∣ X ∣ IV\left( a \right) =-\sum_{v=1}^V{\frac{\left| \boldsymbol{X}^v \right|}{\left| \boldsymbol{X} \right|}\log _2\frac{\left| \boldsymbol{X}^v \right|}{\left| \boldsymbol{X} \right|}}IV(a)=− v=1∑V ∣X∣∣X v ∣ log 2 ∣X∣∣X v ∣ 2.3 👉 CART算法CART决策树算法的核心原理是基于基尼系数(Gini index)筛选最优划分属性a ∗ = a r g max a ∈ A G i n i _ i n d e x ( X , a ) {a_*=\underset{a\in A}{\mathrm{arg}\max}\,\,Gini\_index\left( \boldsymbol{X}, a \right) }a ∗ = a∈Aargmax Gini_index(X,a)基尼系数定义为G i n i _ i n d e x ( X , a ) = ∑ v = 1 V ∣ X v ∣ ∣ X ∣ G i n i ( X v ) Gini\_index\left( \boldsymbol{X}, a \right) =\sum_{v=1}^V{\frac{\left| \boldsymbol{X}^v \right|}{\left| \boldsymbol{X} \right|}Gini\left( \boldsymbol{X}^v \right)}Gini_index(X,a)= v=1∑V ∣X∣∣X v ∣ Gini(X v )其中基尼值G i n i ( X v ) = ∑ k = 1 ∣ Y ∣ ∑ k ′ ≠ k p k p k ′ = 1 − ∑ k = 1 ∣ Y ∣ p k 2 Gini\left( \boldsymbol{X}^v \right) =\sum_{k=1}^{\left| \mathcal{Y} \right|}{\sum_{k'\ne k}{p_kp_{k'}}}=1-\sum_{k=1}^{\left| \mathcal{Y} \right|}{p_{k}^{2}}Gini(X v )= k=1∑∣Y∣ k ′ =k∑ p k p k ′ =1− k=1∑∣Y∣ p k2 3 🌲Python实现ID3决策树算法3.1 🍉架构设计主要分为两个模块:决策树生成模块和决策树绘制模块,便于将机器学习算法逻辑和绘制分离,便于维护。为实现决策树生成模块,可以预定义一般树模块并设计接口,决策树由一般树派生,实现面向接口编程。树中的节点再定义一个类来封装。# 树节点 class TreeNode:... # 树 class Tree(ABC):... # 绘制树 class PlotTree(ABC):... # 决策树节点 class DTreeNode(TreeNode):... # 决策树 class DT(Tree):... # 绘制决策树 class PlotDT(PlotTree):...3.2 🍉信息熵与信息增益计算计算信息熵''' * @breif: 获得样本集的信息熵 * @param[in]: data -> 样本集, required: 最后一列为标签列 * @retval: 信息熵 ''' def __getEntory(self, data: DataFrame) -> float: ent, label = 0, data.iloc[:, -1] for i in list(label.value_counts().index): pk = label.value_counts()[i] / label.index.size ent = ent - pk * np.log2(pk) return ent计算信息增益''' * @breif: ID3决策树划分准则——信息增益 * @param[in]: data -> 样本集, required: 最后一列为标签列 * @param[in]: A -> 样本属性与可取属性值字典 * @retval: 最优划分属性, 连续属性最佳离散分位点(如果该属性是连续属性) ''' def getAttrByInfoGain(self, data: DataFrame, A: dict): # 信息增益, 最优划分属性, 连续属性最佳离散分位点 gainInfo, bestA, bestIndex = -9999, None, None for attr, attrValDict in A.items(): tempGainInfo = self.__getEntory(data) # 若是离散属性 if not attrValDict['isContinuous']: for attrVal in attrValDict['val']: subSet = self.__getSubsetByAttr(attr, attrVal, data) tempGainInfo = tempGainInfo - self.__getEntory( subSet) * subSet.index.size / data.index.size # 若是连续属性 else:... if tempGainInfo > gainInfo: gainInfo = tempGainInfo bestA = attr bestIndex = tempBestIndex if attrValDict[ 'isContinuous'] else None return bestA, bestIndex为便于展示代码逻辑,未贴出连续属性的情况。3.3 🍉生成决策树样本数据集:编号,色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率,好瓜 1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46,是 2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,0.774,0.376,是 3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,0.634,0.264,是 4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,0.608,0.318,是 5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,0.556,0.215,是 6,青绿,稍蜷,浊响,清晰,稍凹,软粘,0.403,0.237,是 7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,0.481,0.149,是 8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,0.437,0.211,是 9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,0.666,0.091,否 10,青绿,硬挺,清脆,清晰,平坦,软粘,0.243,0.267,否 11,浅白,硬挺,清脆,模糊,平坦,硬滑,0.245,0.057,否 12,浅白,蜷缩,浊响,模糊,平坦,软粘,0.343,0.099,否 13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,0.639,0.161,否 14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,0.657,0.198,否 15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,0.36,0.37,否 16,浅白,蜷缩,浊响,模糊,平坦,硬滑,0.593,0.042,否 17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,0.719,0.103,否规定样本数据集用dataFrame格式存取,给出生成决策树的接口:''' * @breif: 生成决策树 * @param[in]: data -> 样本数据集矩阵, required: 最后一列为标签列 * @param[in]: A -> 样本属性与可取属性值字典 * @param[in]: depth -> 生成节点的深度 * @param[in]: func -> 最优属性划分函数 * @param[in]: parent -> 父节点对象 * @retval: 完整决策树 ''' def generateTree(self, data: DataFrame, A: dict, depth: int, func, parent=None):这里func是函数指针,到时传入信息增益计算函数即可。按照第一节的算法流程一步步实现:生成节点: # 生成节点 root = DTreeNode() root.parent = parent root.depth = depth递归返回情形 # 样本全属于同一类别C,则将当前节点标记为C类叶节点 if data.iloc[:, -1].nunique() == 1: return root # A = ∅,则将当前节点标记为样本数最多的类叶节点 if len(A) == 0: return root获得最优划分属性并递归生成# 获得最优划分属性 root.a, root.isContinuous = func(data, A) # 遍历最优划分属性的可取属性值 if not root.isContinuous: for a in A[root.a]['val']: # 获得取值为a的样本子集 subData = self.__getSubsetByAttr(root.a, a, data) if subData.empty: child = self.__setChildLeafNode(root, root.label, a) else: _A = A.copy() _A.pop(root.a) # 移除该属性 child = self.generateTree(subData, _A, root.depth + 1, func, parent=root) child.aVal = a root.child.append(child)这里为了不至于混淆,仍没把连续属性的处理粘贴出来,但实际上需要分开处理。3.4 🍉决策树可视化决策树可视化的逻辑很简单,这里不赘述,直接看代码,都给出了注释。class PlotDT(PlotTree): def __init__(self, hide=False, graphSize=10) -> None: super().__init__(hide=hide, graphSize=graphSize) ''' * @breif: 绘制决策树 * @param[in]: tree -> 决策树根节点 * @retval: None ''' def plotTree(self, tree): tree.pos = (0, self.graphSize - 1) # 指定根节点位置 self.creatPlot(tree) plt.show() ''' * @breif: 创建决策树视图 * @param[in]: tree -> 决策树根节点 * @retval: None ''' def creatPlot(self, tree): deltaX, deltaY = 3, 4 # 绘图时节点的X, Y偏置量 if tree.child: num = len(tree.child) # 指定子节点起始位置 startPos = (tree.pos[0] - num // 2 * deltaX, tree.pos[1] - deltaY) if num % 2 == 1 else ( tree.pos[0] - (num // 2 - 0.5) * deltaX, tree.pos[1] - deltaY) self.__poltNode(tree, tree.a, self.branchNodeStyle) for i in range(num): tree.child[i].pos = (startPos[0] + i * deltaX, startPos[1]) self.creatPlot(tree.child[i]) else: self.__poltNode(tree, tree.label, self.leafNodeStyle) ''' * @breif: 绘制决策树节点 * @param[in]: node -> 节点对象 * @param[in]: nodeText -> 节点文本 * @param[in]: nodeType -> 节点类型 * @retval: None ''' def __poltNode(self, node, nodeText, nodeType) -> None: if node.parent: self.plotNode(nodeText, node.pos, node.parent.pos, nodeType) midPos = ((node.parent.pos[0] + node.pos[0]) / 2 - 0.5, (node.parent.pos[1] + node.pos[1]) / 2) self.plotText(midPos, node.aVal) else: self.plotNode(nodeText, node.pos, node.pos, nodeType)3.5 🍉决策树剪枝决策树学习算法很容易产生过拟合现象,表现为树的尺寸过大且分支过多。不同最优属性划分准则对决策树泛化性能的影响十分有限,但剪枝(pruning)的策略和程度对防止过拟合、改善泛化性能的作用相当显著。决策树剪枝算法主要分为预剪枝(prepruning)和后剪枝(postpruning)。前者是在决策树生成过程中,划分每个结点前先估计当前结点的划分能否提升泛化性能,若不能则停止划分并将当前结点标记为叶结点;后者是先从训练集生成一棵完整的决策树,然后自底向上遍历分支节点,判决能否提升泛化性能,若不能则将该分支节点标记为叶节点。在算法实现上主要分为两步:分支节点排序和判断剪枝性能。分支节点按深度排序,从浅到深即为预剪枝,反之为后剪枝。判断剪枝性能即是在验证集上判断精度,剪枝后精度提升就保留剪枝结果,否则不剪。''' * @breif: 决策树剪枝 * @param[in]: validData -> 验证集, required: 最后一列为标签列 * @param[in]: ptype -> 剪枝类型 post:后剪枝 pre:预剪枝 * @retval: None ''' def pruning(self, validData: DataFrame, ptype="post") -> None: assert ptype in ('post', 'pre') _tree = copy.deepcopy(self.tree) branchNodeDict = {i: i.depth for i in self.getBranchNode(_tree)} if ptype == "post": branchNodeDict = sorted(branchNodeDict.items(), key=lambda x: x[1], reverse=True) elif ptype == "pre": branchNodeDict = sorted(branchNodeDict.items(), key=lambda x: x[1], reverse=False) for _node, depth in branchNodeDict: # 剪枝前的预测准确率 acc = self.calPredictAcc(validData, self.tree) # 缓存节点的子代并剪枝 temp = _node.child _node.child = [] # 剪枝后的预测准确率 postacc = self.calPredictAcc(validData, _tree) if postacc > acc: del self.tree self.tree = copy.deepcopy(_tree) else: _node.child = temp剪枝前剪枝后本文完整的工程代码请关注下方公众号,回复“ML002”获取。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 从一个问题开始2 频率和概率3 先验概率和后验概率4 什么是几率?5 回到问题6 什么是似然?7 写在最后1 从一个问题开始机器学习中,著名的Logistic分类回归算法,使用概率还是几率建模?事实上国内没有很好的文章辨析概率和几率的区别,请各位带着问题往下看,相信会有所收获。2 频率和概率先给出定义。频率(frequency)是当前已完成的有限次实验中,事件 X X X发生的次数占总实验次数的比例,是经验值。根据伯努利大数定律:当实验次数足够大时,事件 X X X发生的频率将趋于稳定值,此稳定值称为概率(Probability),概率是给定模型参数后,对样本合理性的度量,而不涉及任何观测数据。对未来的预测来源于过往经验,而沟通经验与未来的工具就是概率——所谓事件 X X X发生可能性大小,就是事件 X X X在历史中发生的频率大小!引例1.1:抛掷一枚质地均匀的硬币并统计其向上、向下的次数。统计结果显示该硬币在100次抛掷实验中有60次向上,40次向下,求再掷1次硬币正面朝上的概率。引例1.1中频率 f = 60 % f=60\% f=60%而概率 P = 50 % P=50\% P=50%。3 先验概率和后验概率引例1.2:如图所示,假设每次实验时随机从某个盒子里挑出一个球。随机变量 A A A表示挑出的盒子,且设 P ( A = b l u e ) = 0.6 P\left( A=blue \right) =0.6 P(A=blue)=0.6;随机变量 B B B表示挑中的球,问:(a) P ( A = r e d ) = ? P\left( A=red \right) =? P(A=red)=? ;(b) 在挑中蓝色盒子的条件下, P ( B = g r e e n ) = ? P\left( B=green \right) =? P(B=green)=?;(c) 已知挑出绿色的球,则其是从红色盒子中挑出的概率有多大?可以得到如下答案:(a) P ( A = r e d ) = 0.4 P\left( A=red \right) =0.4 P(A=red)=0.4(b) P ( B = g r e e n ∣ A = b l u e ) = P ( B = g r e e n , A = b l u e ) P ( A = b l u e ) = 3 4 P\left( B=green|A=blue \right) =\frac{P\left( B=green,A=blue \right)}{P\left( A=blue \right)}=\frac{3}{4} P(B=green∣A=blue)= P(A=blue)P(B=green,A=blue) = 43(c) P ( A = r e d ∣ B = g r e e n ) = P ( B = g r e e n ∣ A = r e d ) P ( A = r e d ) P ( B = g r e e n ) = 2 11 P\left( A=red|B=green \right) =\frac{P\left( B=green|A=red \right) P\left( A=red \right)}{P\left( B=green \right)}=\frac{2}{11} P(A=red∣B=green)= P(B=green)P(B=green∣A=red)P(A=red) = 112先验概率也称边缘概率(marginal probability),指根据以往经验和分析,在实验或采样前就可以得到而不依赖于其他任何事件的概率,例如 P ( A = r e d ) = 0.4 P\left( A=red \right) =0.4 P(A=red)=0.4,其不依赖于任何事件(如取出绿球等),在实验前就可得出。后验概率也称条件概率(conditional probability),指某事件(如实验或采样)已经发生,而与此相因果事件的概率,例如(Ab)(Ac)。4 什么是几率?对样本合理性的度量有两种方式:(1) 以频度为核心的度量方式,关注频率的极限,此时称为概率,记为 P P P;(2) 以信念(belief)为核心的度量方式,此时称为几率(odds),记为 O O O。所谓信念,是相比于样本不合理而言,有多大把握认为样本合理的直观度量,即O = P 1 − P O=\frac{P}{1-P}O= 1−PP引例1.1中几率 O = 1 : 1 O=1:1 O=1:1说明无论再投掷多少次,仍然没有把握认为一定正面向上,此时若引入质地不均匀的硬币产生几率 O = 10 : 1 O=10:1 O=10:1,则可以直观地反映硬币正面向上比反面向上的可能性大得多。概率或几率越大表明事件越有可能发生,因为它们对刻画样本合理性的本质相同。引入几率是因为其具有很多特殊性质,例如:(1) 概率取值在0到1,但几率取值在0到正无穷,因此对几率取对数可以获得负无穷到正无穷范围的对称定义域,此时称为对数几率(log odds)。这种对称性被广泛应用于统计机器学习中,例如著名的Logistic回归分析;(2) 几率更直观地反映了样本合理与不合理之间的置信差距,因此博彩行业引入赔率的概念来反应下注者对参赛者获胜的信心。5 回到问题Logistic分类回归算法,使用概率还是几率建模?有了前面的铺垫,这个问题就很显然了。Logistic回归是联系函数取Sigmoid函数时的广义线性模型,化简后即得f ( x ^ ( i ) ) = g − 1 ( w ^ T x ^ ( i ) ) ⇔ w ^ T x ^ ( i ) = ln f ( x ^ ( i ) ) 1 − f ( x ^ ( i ) ) f\left( \boldsymbol{\hat{x}}^{\left( i \right)} \right) =g^{-1}\left( \boldsymbol{\hat{w}}^T\boldsymbol{\hat{x}}^{\left( i \right)} \right) \Leftrightarrow \boldsymbol{\hat{w}}^T\boldsymbol{\hat{x}}^{\left( i \right)}=\ln \frac{f\left( \boldsymbol{\hat{x}}^{\left( i \right)} \right)}{1-f\left( \boldsymbol{\hat{x}}^{\left( i \right)} \right)}f( x^ (i) )=g −1 ( w^ T x^ (i) )⇔ w^ T x^ (i) =ln 1−f( x^ (i) )f( x^ (i) )对这个式子不熟悉的话可以等我后面进行Logistic分类回归算法的详解。将上式中的 f ( x ) f(x) f(x)看作后验概率 p ( y i = 1 ∣ x ^ ( i ) ) p\left( y_i=1|\boldsymbol{\hat{x}}^{\left( i \right)} \right) p(y i =1∣ x^ (i) )则等式右边就是对数几率,这是因为用几率可以获得负无穷到正无穷的对称定义域。所以回答文章开始的问题:Logistic分类回归算法,使用几率建模。6 什么是似然?概率与似然是站在两个角度上看待问题。假设样本集为 X X X,环境参数为 θ \theta θ(1) 当环境参数 θ \theta θ已知时为概率问题:概率即是给定模型参数后,对样本合理性的描述,而不涉及任何观测数据。(2) 当环境参数 θ \theta θ未知时为似然问题:似然即是给定样本观测数据,从而推测可能产生这个结果的环境参数。似然问题也称为逆概(Converse Probability)问题。引例1.3:(a) 假设掷一枚硬币正面朝上概率为 θ = 0.5 \theta =0.5 θ=0.5,求掷10次硬币中有7次正面朝上的概率;(b) 有一个硬币有 θ \theta θ的概率正面朝上,为确定 θ \theta θ做如下实验:掷10次硬币得到一个正反序列——HHTTHTHHHH。显然,(Qa)可以不依赖任何观测数据计算出 ,即最终的观测数据在0.117附近则认为是合理的;(Qb)则是通过观测数据构造似然函数来反推环境参数:事实上,这符合人类认识的规律——即在不断地实践中获得观测数据,再从观测数据中总结经验规律(对应于模型参数)。在机器学习中,也往往是需要机器根据已有的数据学到相应的分布——即确定由 θ \theta θ决定的模型。似然认为 θ \theta θ是随机变量其实是贝叶斯学派的观点,关于贝叶斯方法的具体分析可以参考机器学习:详解贝叶斯网络+例题分析。7 写在最后本文完整地辨析了统计机器学习理论中常用的频率、概率、几率、似然概念,帮助各位读者今后学习AI、ML理论知识。另外,本专栏后续会开放Python机器学习实战系列,基于周志华老师的西瓜书,对课后的编程题(例如LDA、DT等)逐一给出完整源码,欢迎订阅本专栏,也欢迎关注作者。
目录0 效果展示1 系统架构设计2 软件架构设计3 特征匹配4 生成特征描述子5 图像拼接与配准6 图像平滑与美化0 效果展示待拼接图片:拼接后图片:1 系统架构设计对项目所要求达成的目标,将图像拼接系统的整体流程分为三个阶段:特征工程。该阶段主要实现对图像边缘、角点等特征位置的检测;对检测的局部特征生成具有鲁棒性、独特性、多量性、可扩展性的定量描述——特征描述子基于特征描述子使用数值计算方法,结合重投影误差等匹配性能指标筛选匹配特征点对。该阶段还会对不同的特征检测算法、特征匹配策略进行性能比较、评估与分析。图像配准。该阶段主要实现对无匹配点的滤除——增强抗噪能力与鲁棒性;图像映射的单应性估计;基于单应性矩阵将图像进行透视变换与拷贝,保持待拼接图像间坐标描述的一致性。图像美化。该阶段主要用于消除图像配准阶段产出图像的明显拼接缝隙和毛刺,主要实现图像均衡化——基于像素调整平衡不同拼接图像间的光照强度差异;基于加权平滑图像拼接的缝隙处;基于拉普拉斯金字塔方法进行图像融合,使拼接图像更自然、美观。2 软件架构设计3 特征匹配主要采用:暴力匹配、最值匹配、交叉匹配、KNN匹配、RANSAC匹配单纯靠特征匹配是不够的,因为会引入大量误匹配点,需要进行匹配优化Lowe’s匹配优化:Lowe’s 匹配优化算法核心原理是:通过 K K K近邻查找算法,选择 K K K个和目标特征点最相似的点,若这 K K K个匹配对间的区别足够大,则选择最相似的作为目标特征点的匹配点,否则舍弃。由于本项目针对的目标图像尺寸较小,因此通常选择 K = 2 K=2 K=2,即最近邻匹配。 2个点间的区别用距离比率α = d ( F d s t , F a ) d ( F d s t , F b ) \alpha =\frac{d\left( F_{dst},F_a \right)}{d\left( F_{dst},F_b \right)}α= d(F dst ,F b )d(F dst ,F a )来衡量,比率阈值根据不同的特征检测算法有所不同,达到此比率则认为目标特征点的两个匹配对区别足够大,可以加入优秀匹配点集。此外上述的距离相似度度量 d d d对不同的检测算法亦有区别,本项目中对于ORB、BRISK等二进制描述算法采用汉明距离度量,在保证精度的情况下大幅提高匹配效率;对于SIFT、SURF等浮点型描述算法则采用 L 2 L2 L2范数度量。值得注意的是,在算法开始前需要比较两个输入特征描述子的尺寸,因为在特征匹配时匹配器有两个参数——查询索引(Query Index)与匹配索引(Train Index),当使用特征较多的描述子去查询特征较少的描述子时可能发生向量越界的情况,因此算法统一使用小特征描述子去查询大特征描述子中对应的匹配点。可以看出,优化的效果还是很明显的4 生成特征描述子主要采用了下面四种方法从实验结果看,SIFT算法性能最佳5 图像拼接与配准方法是计算相邻两张待拼接图片间的单应性矩阵,然后将其中一张图片通过单应性矩阵映射到另一张图片的坐标系即可。考虑到RANSAC算法能够对计算单应性矩阵选取的参数进行迭代,并根据内集合的评判结果选取效果更好的匹配关键点计算得到的单应性矩阵,最终计算得到的单应性矩阵能适应最大数量的关键点,因此使用RANSAC算法计算得到的单应性矩阵应用于图像拼接后能得到最好的效果。自定义RANSAC单应性矩阵算法应用于图像拼接的结果与使用findHomography库函数的RANSAC方法的结果对比如图所示。同时对比代码的运行结果如图所示,可以看出自定义的RANSAC求解算法和库函数中直接调用的RANSAC计算方式会产生约为0.5%的误差,可以视为算法的误差,可能由在单应性矩阵计算过程中变量类型转换过程中,变量在float和double类型之间转换产生的数据溢出和对于置信度取值不同等原因导致,在实际应用中可忽略不计。6 图像平滑与美化方法:拉普拉斯金字塔融合最终效果:限于篇幅,本文的完整工程代码请关注下方公众号回复“CV005”获取🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
1 什么是模块化编程?2 __init__.py文件的作用3 Python如何import第三方库1 什么是模块化编程?工程模块化是指将具有一定共性的功能封装成一个模块,并对外暴露应用接口,方便其他工程直接调用而无需关注底层实现的思想,工程模块化可以避免工程中各种功能函数相互交杂、定义混乱不堪的情形,有助于提高系统可维护性。在C/C++中,工程模块化的基础是函数头文件.h,其是专门存放函数声明的文件,这些函数声明的具体实现则分离到函数源文件.cpp或.c中,若干个头文件和源文件组成一个模块。在Python中,工程模块化的基础则是 __init__.py文件2 init.py文件的作用__init__.py文件有如下作用:组织包,是Package的标识文件# 目录结构 app |- pkg_1 |- __init__.py |- moduleA.py # fun1() |- moduleB.py |- pkg_2 |- __init__.py |- test.py # pkg_1 __init__.py from pkg_1 import moduleA # test.py中引用 import pkg_1 pkg_1.moduleA.fun1() # 运行成功 # 删除__init__.py运行失败没有__init__.py就无法导入包在__init__.py中定义__all__列表控制着包的导入行为这里解释模糊导入的概念。模糊导入的句式为:from pkg import **为通配符,即导入包中的所有模块。若希望为包中的模块设置访问权限,即在模糊导入作用下选择性地引入模块,则需要在__init__.py中定义__all__列表。例如上例中,在pkg_1->__init__.py中定义__all__=["moduleA"],则在模糊导入时只引入模块moduleA而不会导入moduleB。因此__all__的作用是:在模块级别暴露接口,提供了公开(Public)接口的约定。在__init__.py中导入其他包或模块,方便组织管理各个模块之间的引用3 Python如何import第三方库前面说到,Python包以__init__.py为标志,用于实现工程模块化,假设包组织结构的实例如下:package |- subpackage1 |- __init__.py |- moduleA.py #fun1() fun2() |- subpackage2 |- __init__.py |- moduleA.py #fun1() |- moduleB.py #fun3() fun4()用虚拟文件夹的方式理解Python包。所有的包都可视作文件夹,其下包含模块或子包(子文件夹),模块中包含函数、类、变量等属性。当前路径位置可视作一个空白文件夹,关键字from理解为“打开”,关键字import理解为“导入”,必须指出:所有import相关操作都要落实到模块或属性。一般地,导入有如下方式:import subpackage1.moduleA此方式相当于把一个名为subpackage1的文件夹复制粘贴到当前路径下,文件夹只包含模块moduleA,即使subpackage1中可能还有其他模块,引用moduleA中的func1()需要subpackage1.moduleA.fun1(),即打开subpackage1文件夹,再使用模块moduleA中的属性fun1()。注意,如果仅import subpackage1,相当于只引入了一个空文件夹,此时无法调用fun1(),除非在__init__.py中提前导入了模块。from subpackage1 import moduleA此方式相当于打开一个名为subpackage1的文件夹,再将其中的模块moduleA复制粘贴到当前空白文件夹下,引用moduleA的fun1()需要moduleA.fun1()。这种方式下,还有from subpackage1 import *的句式可以引入包中的所有模块。from subpackage.moduleA import fun1()此方式相当于打开一个名为subpackage1的文件夹下的模块moduleA,再将其中的fun1()复制粘贴到当前空白文件夹,引用fun1()只需fun1()即可。除了应用上述导入句式外,还需要注意当前文件的运行路径,如下所示为一个忽略路径因素造成的导入包报错,因为运行目录app\pkg_2\下没有文件pkg_1且环境变量中也不存在pkg_1。app |- pkg_1 |- __init__.py |- moduleA.py # fun1() |- pkg_2 |- __init__.py |- test.py # test.py中引用 # from pkg_1.moduleA import fun1 # 执行如下 >>> python app\pkg_2\test.py >>> from pkg_1.moduleA import fun1 ModuleNotFoundError: No module named 'pkg_1'若需要保持运行目录不变,必须进行环境变量配置,在import pkg_1前先添加父级目录到python解释器的运行环境变量中,在pkg_2的父级目录app下可访问到pkg_1,具体实现上依赖于sys和os包import sys, os sys.path.append(os.path.realpath('..'))综上所述,包的导入需要考虑两个因素:从哪里导入,即运行路径和环境变量的配置问题;如何导入,即使用何种import句式。
目录1 理想双目视觉系统2 立体校正3 实例1 理想双目视觉系统如图1所示为理想双目视觉系统:两像机成像面共面行对齐,极点处于无限远处——像点 ( x 0 , y 0 ) \left( x_0,y_0 \right) (x 0 ,y 0 )对应的极线为 y = y 0 y=y_0 y=y 0 。关于极点、极线方面的内容可以参考之前的博客:计算机视觉系列教程1-4:对极几何基本原理图解取定左相机坐标系为标准系,由相似关系,物点在左成像面的坐标为[ L x L y ] = [ f X Z f Y Z ] \left[ \right] =\left[ \right][ L xL y ]=[ f ZXf ZY ]设双目系统的间距为 b x b_x b x ,则双目系统相机位姿关系为L R T = [ 1 − b x 1 0 1 0 1 ] _{L}^{R}\boldsymbol{T}=\left[ \right]LR T= ⎣⎢⎢⎡ 1 1 1 −b x001 ⎦⎥⎥⎤因此物点在右相机坐标系下为 R X = L R T L X = [ X − b x Y Z ] T ^R\!\!\:\boldsymbol{X}=_{L}^{R}\boldsymbol{T}\!\:^L\!\!\:\!\!\:\boldsymbol{X}=\left[ \right] ^T R X= LR T L X=[ X−b x Y Z ] T ,同样由相似原理得[ R x R y ] = [ f X − b x Z f Y Z ] \left[ \right] =\left[ \right][ R xR y ]=[ f ZX−b xf ZY ]由于行对齐,因此同一物点在两成像面上形成立体视差d = L x − R x = f b x Z d=^L\!\!\:x-^R\!\!\:x=f\frac{b_x}{Z}d= L x− R x=f Zb x所谓立体视差就是同一个物点在两个相机成像面上相点之差。从而可以从成像面坐标还原三维坐标,并转换为像素尺度:[ X Y Z ] = [ L x b x d L y b x d f b x d ] = [ ( L u − L c u ) b x d u ( L v − L c v ) b x d u f u b x d u ] \left[ \right] =\left[ \right] =\left[ \right]⎣⎡ XYZ ⎦⎤ = ⎣⎡ L x db xL y db xf db x ⎦⎤ = ⎣⎢⎡ ( L u− L c u ) d ub x( L v− L c v ) d ub xf u d ub x ⎦⎥⎤其中 Z Z Z即为图像深度信息。将上述方程改写为线性形式:[ X Y Z 1 ] = [ 1 0 0 − L c u 0 1 0 − L c v 0 0 0 f u 0 0 1 b x R c u − L c u b x ] [ L u L v d u 1 ] ⇔ L X = Q L u \left[ \right] =\left[ \right] \left[ \right] \Leftrightarrow { ^L\!\boldsymbol{X}=\boldsymbol{Q}^L\!\!\:\boldsymbol{u}}⎣⎢⎢⎡ XYZ1 ⎦⎥⎥⎤ = ⎣⎢⎢⎡ 1000 0100 000b x1 − L c u− L c vf ub xR c u − L c u ⎦⎥⎥⎤ ⎣⎢⎢⎡ L uL vd u1 ⎦⎥⎥⎤ ⇔ L X=Q L u其中 Q Q Q称为重投影矩阵, R c u − L c u b x \frac{^R\!\!\:c_u-^L\!\!\:c_u}{b_x} b xR c u − L c u 表征了两成像平面中心的像素偏差。2 立体校正实际应用时并不能直接使用上面的模型,因为没有任何硬件可以真正达到理想双目系统的条件,如图2所示。将实际双目系统变换为理想双目系统的过程称为立体校正,下面详细阐述Bouguet立体校正算法,其核心原理是通过像素平面透视变换,使左右图像重投影误差最小,使双目系统最接近理想状态。定义左右两相机间的变换关系为L R T = [ R t 0 1 ] _{\boldsymbol{L}}^{\boldsymbol{R}}\boldsymbol{T}=\left[ \right]LR T=[ R0 t1 ]通过旋转矩阵 R R R先将右相机坐标系旋转到与左相机坐标系平行,如图3所示。此时双目系统平行但不共面,需要构造一个校准矩阵 R r e c t R_{rect} R rect 将两相机坐标系旋转到同一成像面上,实现共面行对齐校正,如图4所示。设 R r e c t = [ r 1 T r 2 T r 3 T ] T \boldsymbol{R}_{rect}=\left[ \right] ^T R rect =[ r 1T r 2T r 3T ] T ,其构造过程如下:① r 1 r_1 r 1 是旋转后坐标系的 x ′ x' x ′ 相对于原坐标系三个轴的方向余弦,为保证旋转后两成像面共面,需要将原坐标系 x x x轴旋转至基线 − t -t −t方向,即r 1 = − t ∥ t ∥ \boldsymbol{r}_1=\frac{-\boldsymbol{t}}{\left\| \boldsymbol{t} \right\|}r 1 = ∥t∥−t② r 2 r_2 r 2 、 r 3 r_3 r 3 事实上可以任意给出,只需满足右手系方向即可。一般地,取{ r 2 = − t × [ 0 0 1 ] T ∥ − t × [ 0 0 1 ] T ∥ r 3 = r 1 × r 2⎩⎨⎧ r 2 = ∥∥ −t×[ 0 0 1 ] T ∥∥−t×[ 0 0 1 ] Tr 3 =r 1 ×r 2使 y ′ y' y ′ 垂直于原光轴方向。综合上述步骤得到{ R L = R r e c t R R = R r e c t R T{ R L =R rectR R =R rect R T接下来进行像素平面的映射。在不考虑畸变的条件下,相机坐标系未旋转时有 u = K x \boldsymbol{u}=\boldsymbol{Kx} u=Kx,旋转后则为 u ′ = K x ′ = K R x \boldsymbol{u}'=\boldsymbol{Kx}'=\boldsymbol{KRx} u ′ =Kx ′ =KRx,因此旋转前后像素坐标的关系为u ′ = K R K − 1 u = H u { \boldsymbol{u}'=\boldsymbol{KRK}^{-1}\boldsymbol{u}=\boldsymbol{Hu}}u ′ =KRK −1 u=Hu像素立体校正后,即可使用理想双目系统模型进行场景几何的估计。总结Bouguet立体校正算法流程:(1) 基于相机几何信息 R R R、 t t t 构造 R L R_L R L 、 R R R_R R R ;(2) 基于旋转前后像素坐标关系得到单应性矩阵 H L = K R L K − 1 \boldsymbol{H}_L=\boldsymbol{KR}_L\boldsymbol{K}^{-1} H L =KR L K −1 、 H R = K R R K − 1 \boldsymbol{H}_R=\boldsymbol{KR}_R\boldsymbol{K}^{-1} H R =KR R K −1 ;(3) 归一化齐次坐标。映射后 u ′ = [ u v w ] \boldsymbol{u}'=\left[ \right] u ′ =[ u v w ],需归一化为 u ′ = [ u / w v / w 1 ] \boldsymbol{u}'=\left[ \right] u ′ =[ u/w v/w 1 ]。关于单应性矩阵方面的知识可以参考计算机视觉系列教程1-2:单应性矩阵估计3 实例🚀 计算机视觉基础教程说明章号 内容 0 色彩空间与数字成像 1 计算机几何基础 2 图像增强、滤波、金字塔 3 图像特征提取 4 图像特征描述 5 图像特征匹配 6 立体视觉 7 项目实战🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 平面2R机器人概述2 运动学建模2.1 正运动学模型2.2 逆运动学模型2.3 机器人运动学仿真3 动力学建模3.1 计算动能3.2 势能计算与动力学方程3.3 动力学仿真1 平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标 θ 1 \theta _1 θ 1 为连杆1与 y y y轴负半轴的夹角,逆时针为正; θ 2 \theta _2 θ 2 为连杆2与连杆1延长线的夹角,逆时针为正;连杆1、2的关节力矩分别定义为 τ 1 \tau_1 τ 1 、 τ 2 \tau_2 τ 2 。设连杆质量均匀分布,长度都为 l l l,质量都为 M M M,末端执行器笛卡尔坐标为 P ( x , y ) P(x,y) P(x,y)。2 运动学建模2.1 正运动学模型指数积方法的核心原理是基于运动旋量的指数积公式:T A T ( θ ) = e [ V 1 ] θ 1 e [ V 2 ] θ 2 ⋯ e [ V n ] θ n T A T ( 0 ) _{\boldsymbol{T}}^{\boldsymbol{A}}\boldsymbol{T}\left( \boldsymbol{\theta } \right) =e^{\left[ \boldsymbol{V}_1 \right] \theta _1}e^{\left[ \boldsymbol{V}_2 \right] \theta _2}\cdots {e^{\left[ \boldsymbol{V}_n \right] \theta _n}}_{\boldsymbol{T}}^{\boldsymbol{A}}\boldsymbol{T}\left( 0 \right)TA T(θ)=e [V 1 ]θ 1 e [V 2 ]θ 2 ⋯e [V n ]θ n TA T(0)可以证明,连杆变换之积(DH法)等价于指数积。对平面2R机器人,确定各个关节轴线的线矢量如图2所示,不必建立连杆坐标系。图2各轴线方向如下:ω 1 = ω 2 = [ 0 0 1 ] , [ ω 1 ] = [ ω 2 ] = [ 0 − 1 0 1 0 0 0 0 0 ] \omega _1=\omega _2=\left[001001\right] ,\mathrm{ }\left[ \omega _1 \right] =\left[ \omega _2 \right] =\left[010−1000000−10100000\right]ω 1 =ω 2 = ⎣⎡ 001 ⎦⎤ ,[ω 1 ]=[ω 2 ]= ⎣⎡ 010 −100 000 ⎦⎤取各个运动轴上的一点:r 1 = [ 0 0 0 ] , r 2 = [ 0 − l 0 ] r_1=\left[000000\right] ,\mathrm{ }r_2=\left[0−l00−l0\right]r 1 = ⎣⎡ 000 ⎦⎤ ,r 2 = ⎣⎡ 0−l0 ⎦⎤可得各个运动旋量的指数形式e [ V 1 ] θ 1 = [ cos θ 1 − sin θ 1 0 0 sin θ 1 cos θ 1 0 0 0 0 1 0 0 0 0 1 ] , e [ V 2 ] θ 2 = [ cos θ 2 − sin θ 2 0 − l sin θ 2 sin θ 2 cos θ 2 0 − l ( 1 − cos θ 2 ) 0 0 1 0 0 0 0 1 ] e^{\left[ \boldsymbol{V}_1 \right] \theta _1}=\left[cosθ1sinθ100−sinθ1cosθ10000100001cosθ1−sinθ100sinθ1cosθ10000100001\right] , e^{\left[ \boldsymbol{V}_2 \right] \theta _2}=\left[cosθ2sinθ200−sinθ2cosθ2000010−lsinθ2−l(1−cosθ2)01cosθ2−sinθ20−lsinθ2sinθ2cosθ20−l(1−cosθ2)00100001\right]e [V 1 ]θ 1 = ⎣⎢⎢⎡ cosθ 1sinθ 100 −sinθ 1cosθ 100 0010 0001 ⎦⎥⎥⎤ ,e [V 2 ]θ 2 = ⎣⎢⎢⎡ cosθ 2sinθ 200 −sinθ 2cosθ 200 0010 −lsinθ 2−l(1−cosθ 2 )01 ⎦⎥⎥⎤关节变量 θ = [ θ 1 θ 2 ] \boldsymbol{\theta }=\left[θ1θ2θ1θ2\right] θ=[ θ 1 θ 2 ]为0时即为初始位姿:T B T ( 0 ) = [ 1 0 0 0 0 1 0 − 2 l 0 0 1 0 0 0 0 1 ] _{T}^{B}\boldsymbol{T}\left( 0 \right) =\left[1000010000100−2l011000010−2l00100001\right]TB T(0)= ⎣⎢⎢⎡ 1000 0100 0010 0−2l01 ⎦⎥⎥⎤所以由 T B T ( θ ) = e [ V 1 ] θ 1 e [ V 2 ] θ 2 T S T ( 0 ) _{T}^{B}\boldsymbol{T}\left( \boldsymbol{\theta } \right) =e^{\left[ \boldsymbol{V}_1 \right] \theta _1}e^{\left[ \boldsymbol{V}_2 \right] \theta _2}\!\:_{T}^{S}\boldsymbol{T}\left( 0 \right) TB T(θ)=e [V 1 ]θ 1 e [V 2 ]θ 2 TS T(0)即得T B T ( θ ) = [ cos ( θ 1 + θ 2 ) − sin ( θ 1 + θ 2 ) 0 l sin ( θ 1 + θ 2 ) + l sin θ 1 sin ( θ 1 + θ 2 ) cos ( θ 1 + θ 2 ) 0 − l cos ( θ 1 + θ 2 ) − l cos θ 1 0 0 1 0 0 0 0 1 ] _{T}^{B}\boldsymbol{T}\left( \boldsymbol{\theta } \right) =\left[cos(θ1+θ2)sin(θ1+θ2)00−sin(θ1+θ2)cos(θ1+θ2)000010lsin(θ1+θ2)+lsinθ1−lcos(θ1+θ2)−lcosθ101cos(θ1+θ2)−sin(θ1+θ2)0lsin(θ1+θ2)+lsinθ1sin(θ1+θ2)cos(θ1+θ2)0−lcos(θ1+θ2)−lcosθ100100001\right]TB T(θ)= ⎣⎢⎢⎡ cos(θ 1 +θ 2 )sin(θ 1 +θ 2 )00 −sin(θ 1 +θ 2 )cos(θ 1 +θ 2 )00 0010 lsin(θ 1 +θ 2 )+lsinθ 1−lcos(θ 1 +θ 2 )−lcosθ 101 ⎦⎥⎥⎤可以验证,基于运动旋量法的机器人正运动学模型,与基于D-H法的正运动学模型相同,可交叉验证正运动学模型的理论正确性。2.2 逆运动学模型以图3所示的象限为例,以几何方法进行机器人运动学反解。对于位形一,在 Δ O A B \varDelta OAB ΔOAB中运用余弦定理可得 2 l 2 cos ( π − θ 2 ) = l 2 + l 2 − L 2 2l^2\cos \left( \pi -\theta _2 \right) =l^2+l^2-L^2 2l 2 cos(π−θ 2 )=l 2 +l 2 −L 2 ,其中 L 2 = x 2 + y 2 L^2=x^2+y^2 L 2 =x 2 +y 2 ,显然末端执行器坐标需要满足 0 < x 2 + y 2 ⩽ 2 l 0<\sqrt{x^2+y^2}\leqslant 2l 0< x 2 +y 2 ⩽2l解得:θ 2 = a r c cos ( x 2 + y 2 2 l 2 − 1 ) \theta _2=\mathrm{arc}\cos \left( \frac{x^2+y^2}{2l^2}-1 \right)θ 2 =arccos( 2l 2x 2 +y 2 −1)有两个解,位形一对应正解,位形二对应负解。同样在 Δ O A B \varDelta OAB ΔOAB中运用余弦定理可得ψ = a r c cos ( x 2 + y 2 2 l ) \psi =\mathrm{arc}\cos \left( \frac{\sqrt{x^2+y^2}}{2l} \right)ψ=arccos( 2lx 2 +y 2 )为方便起见,这里总是取 ψ \psi ψ的正解。再根据β = ∣ a r c tan ( y x ) ∣ \beta =\left| \mathrm{arc}\tan \left( \frac{y}{x} \right) \right|β= ∣∣∣ arctan( xy ) ∣∣∣可得位形一下的关节参数 θ 1 = π 2 − ( β + ψ ) \theta _1=\frac{\pi}{2}-\left( \beta +\psi \right) θ 1 = 2π −(β+ψ)。当机械臂运动到其他象限时同理,求得平面2R机器人运动学反解为:{ θ 1 = π 2 − ( β ± ψ ) , θ 2 > 0 时取 + θ 1 = π 2 + ( β ± ψ ) , θ 2 > 0 时取 − θ 1 = 3 π 2 − ( β ± ψ ) , θ 2 > 0 时取 + θ 1 = 3 π 2 + ( β ± ψ ) , θ 2 > 0 时取 −⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪θ1=π2−(β±ψ),θ2>0时取+θ1=π2+(β±ψ),θ2>0时取−θ1=3π2−(β±ψ),θ2>0时取+θ1=3π2+(β±ψ),θ2>0时取−{θ1=π2−(β±ψ),θ2>0时取+θ1=π2+(β±ψ),θ2>0时取−θ1=3π2−(β±ψ),θ2>0时取+θ1=3π2+(β±ψ),θ2>0时取−⎩⎪⎪⎪⎨⎪⎪⎪⎧ θ 1 = 2π −(β±ψ),θ 2 >0时取+θ 1 = 2π +(β±ψ),θ 2 >0时取−θ 1 = 23π −(β±ψ),θ 2 >0时取+θ 1 = 23π +(β±ψ),θ 2 >0时取− 2.3 机器人运动学仿真基于Matlab Robotics工具箱进行平面2R机器人的运动学仿真验证。验证过程如下:分别选取位于四个象限的关节参数[ θ 1 θ 2 ] = [ 35 ° 55 ° ] , [ θ 1 θ 2 ] = [ 95 ° 25 ° ] , [ θ 1 θ 2 ] = [ 230 ° 20 ° ] , [ θ 1 θ 2 ] = [ 300 ° 70 ° ] \left[θ1θ2θ1θ2\right] =\left[35\degree55\degree35\degree55\degree\right] , \left[θ1θ2θ1θ2\right] =\left[95\degree25\degree95\degree25\degree\right] , \left[θ1θ2θ1θ2\right] =\left[230\degree20\degree230\degree20\degree\right] , \left[θ1θ2θ1θ2\right] =\left[300\degree70\degree300\degree70\degree\right][ θ 1θ 2 ]=[ 35°55° ],[ θ 1θ 2 ]=[ 95°25° ],[ θ 1θ 2 ]=[ 230°20° ],[ θ 1θ 2 ]=[ 300°70° ]进行正运动的验证,而后基于四组末端执行器的坐标进行运动学反解,得到该坐标下的另一个位形对应的关节参数,将该参数代入Matlab机器人模型,验证此时末端执行器的坐标是否与位形一下的重合。将四组参数分别代入上式,得到[ x y ] = [ 1.57 − 0.82 ] , [ x y ] = [ 1.86 0.59 ] , [ x y ] = [ − 1.71 0.99 ] , [ x y ] = [ − 0.69 − 1.48 ] \left[xyxy\right] =\left[1.57−0.821.57−0.82\right] , \left[xyxy\right] =\left[1.860.591.860.59\right] , \left[xyxy\right] =\left[−1.710.99−1.710.99\right] , \left[xyxy\right] =\left[−0.69−1.48−0.69−1.48\right][ xy ]=[ 1.57−0.82 ],[ xy ]=[ 1.860.59 ],[ xy ]=[ −1.710.99 ],[ xy ]=[ −0.69−1.48 ]将这四组笛卡尔坐标值代入式,得到另一位形的关节参数值[ θ 1 θ 2 ] = [ 90 ° − 55 ° ] , [ θ 1 θ 2 ] = [ 120 ° − 25 ° ] , [ θ 1 θ 2 ] = [ 249 ° − 20 ° ] , [ θ 1 θ 2 ] = [ 10 ° − 70 ° ] \left[θ1θ2θ1θ2\right] =\left[90\degree−55\degree90\degree−55\degree\right] , \left[θ1θ2θ1θ2\right] =\left[120\degree−25\degree120\degree−25\degree\right] , \left[θ1θ2θ1θ2\right] =\left[249\degree−20\degree249\degree−20\degree\right] , \left[θ1θ2θ1θ2\right] =\left[10\degree−70\degree10\degree−70\degree\right][ θ 1θ 2 ]=[ 90°−55° ],[ θ 1θ 2 ]=[ 120°−25° ],[ θ 1θ 2 ]=[ 249°−20° ],[ θ 1θ 2 ]=[ 10°−70° ]3 动力学建模3.1 计算动能取定连杆上一质量微元 d m \mathrm{d}m dm,其位置坐标为 ( x , y ) (x,y) (x,y),采用极坐标表示即为连杆I { x = r sin θ 1 y = − r cos θ 1 连杆II { x = l sin θ 1 + r sin ( θ 1 + θ 2 ) y = − l cos θ 1 − r cos ( θ 1 + θ 2 ) \text{连杆I}{x=rsinθ1y=−rcosθ1{x=rsinθ1y=−rcosθ1\,\, \text{连杆II}{x=lsinθ1+rsin(θ1+θ2)y=−lcosθ1−rcos(θ1+θ2){x=lsinθ1+rsin(θ1+θ2)y=−lcosθ1−rcos(θ1+θ2)连杆I{ x=rsinθ 1y=−rcosθ 1 连杆II{ x=lsinθ 1 +rsin(θ 1 +θ 2 )y=−lcosθ 1 −rcos(θ 1 +θ 2 )对时间求导,得到连杆I { x ˙ = r θ ˙ 1 cos θ 1 y ˙ = r θ ˙ 1 sin θ 1 连杆II { x ˙ = l θ ˙ 1 cos θ 1 + r ( θ ˙ 1 + θ ˙ 2 ) cos ( θ 1 + θ 2 ) y ˙ = l θ ˙ 1 sin θ 1 + r ( θ ˙ 1 + θ ˙ 2 ) sin ( θ 1 + θ 2 ) \text{连杆I}{x˙=rθ˙1cosθ1y˙=rθ˙1sinθ1{x˙=rθ˙1cosθ1y˙=rθ˙1sinθ1\,\, \text{连杆II}⎧⎩⎨⎪⎪x˙=lθ˙1cosθ1+r(θ˙1+θ˙2)cos(θ1+θ2)y˙=lθ˙1sinθ1+r(θ˙1+θ˙2)sin(θ1+θ2){x˙=lθ˙1cosθ1+r(θ˙1+θ˙2)cos(θ1+θ2)y˙=lθ˙1sinθ1+r(θ˙1+θ˙2)sin(θ1+θ2)连杆I{ x˙ =r θ˙ 1 cosθ 1y˙ =r θ˙ 1 sinθ 1 连杆II ⎩⎨⎧ x˙ =l θ˙ 1 cosθ 1 +r( θ˙ 1 + θ˙ 2 )cos(θ 1 +θ 2 )y˙ =l θ˙ 1 sinθ 1 +r( θ˙ 1 + θ˙ 2 )sin(θ 1 +θ 2 )所以质量微元速度为{ 连杆I: v 1 2 = x ˙ 2 + y ˙ 2 = r 2 θ ˙ 1 2 连杆II: v 2 2 = x ˙ 2 + y ˙ 2 = l 2 θ ˙ 1 2 + r 2 ( θ ˙ 1 + θ ˙ 2 ) 2 + 2 l r θ ˙ 1 ( θ ˙ 1 + θ ˙ 2 ) cos θ 2⎧⎩⎨⎪⎪连杆I:v21=x˙2+y˙2=r2θ˙21连杆II:v22=x˙2+y˙2=l2θ˙21+r2(θ˙1+θ˙2)2+2lrθ˙1(θ˙1+θ˙2)cosθ2{连杆I:v12=x˙2+y˙2=r2θ˙12连杆II:v22=x˙2+y˙2=l2θ˙12+r2(θ˙1+θ˙2)2+2lrθ˙1(θ˙1+θ˙2)cosθ2⎩⎨⎧ 连杆I:v 12 = x˙ 2 + y˙ 2 =r 2 θ˙ 12连杆II:v 22 = x˙ 2 + y˙ 2 =l 2 θ˙ 12 +r 2 ( θ˙ 1 + θ˙ 2 ) 2 +2lr θ˙ 1 ( θ˙ 1 + θ˙ 2 )cosθ 2因此机器人动能为E k = 1 2 ∫ m d m ⋅ v 2 = 1 2 ρ [ ∫ 0 l r 2 θ ˙ 1 2 d r + ∫ 0 l l 2 θ ˙ 1 2 + r 2 ( θ ˙ 1 + θ ˙ 2 ) 2 + 2 l r θ ˙ 1 ( θ ˙ 1 + θ ˙ 2 ) cos θ 2 d r ] = 1 2 m l 2 [ ( 5 3 + cos θ 2 ) θ ˙ 1 2 + ( 2 3 + cos θ 2 ) θ ˙ 1 θ ˙ 2 + 1 3 θ ˙ 2 2 ] E_k=\frac{1}{2}\int_m{\mathrm{d}m\cdot v^2}\\=\frac{1}{2}\rho \left[ \int_0^l{r^2\dot{\theta}_{1}^{2}\mathrm{d}r}+\int_0^l{l^2\dot{\theta}_{1}^{2}+r^2\left( \dot{\theta}_1+\dot{\theta}_2 \right) ^2+2lr\dot{\theta}_1\left( \dot{\theta}_1+\dot{\theta}_2 \right) \cos \theta _2\mathrm{d}r} \right] \\=\frac{1}{2}ml^2\left[ \left( \frac{5}{3}+\cos \theta _2 \right) \dot{\theta}_{1}^{2}+\left( \frac{2}{3}+\cos \theta _2 \right) \dot{\theta}_1\dot{\theta}_2+\frac{1}{3}\dot{\theta}_{2}^{2} \right]E k = 21 ∫ m dm⋅v 2= 21 ρ[∫ 0l r 2 θ˙ 12 dr+∫ 0l l 2 θ˙ 12 +r 2 ( θ˙ 1 + θ˙ 2 ) 2 +2lr θ˙ 1 ( θ˙ 1 + θ˙ 2 )cosθ 2 dr]= 21 ml 2 [( 35 +cosθ 2 ) θ˙ 12 +( 32 +cosθ 2 ) θ˙ 1 θ˙ 2 + 31 θ˙ 22 ]3.2 势能计算与动力学方程以基坐标系 x x x轴为零势能面,则系统总势能为E p = − m g l [ 3 2 cos θ 1 + 1 2 cos ( θ 1 + θ 2 ) ] E_p=-mgl\left[ \frac{3}{2}\cos \theta _1+\frac{1}{2}\cos \left( \theta _1+\theta _2 \right) \right]E p =−mgl[ 23 cosθ 1 + 21 cos(θ 1 +θ 2 )]对于任何机械系统.拉格朗日函数定义为系统总动能和势能之差,即L = E k − E p = 1 2 m l 2 [ ( 5 3 + cos θ 2 ) θ ˙ 1 2 + ( 2 3 + cos θ 2 ) θ ˙ 1 θ ˙ 2 + 1 3 θ ˙ 2 2 ] + m g l [ 3 2 cos θ 1 + 1 2 cos ( θ 1 + θ 2 ) ] L=E_k-E_p\\=\frac{1}{2}ml^2\left[ \left( \frac{5}{3}+\cos \theta _2 \right) \dot{\theta}_{1}^{2}+\left( \frac{2}{3}+\cos \theta _2 \right) \dot{\theta}_1\dot{\theta}_2+\frac{1}{3}\dot{\theta}_{2}^{2} \right] +mgl\left[ \frac{3}{2}\cos \theta _1+\frac{1}{2}\cos \left( \theta _1+\theta _2 \right) \right]L=E k −E p= 21 ml 2 [( 35 +cosθ 2 ) θ˙ 12 +( 32 +cosθ 2 ) θ˙ 1 θ˙ 2 + 31 θ˙ 22 ]+mgl[ 23 cosθ 1 + 21 cos(θ 1 +θ 2 )]基于拉格朗日函数可得{ ∂ L ∂ θ 1 = − m g l [ 3 2 sin θ 1 + 1 2 sin ( θ 1 + θ 2 ) ] ∂ L ∂ θ ˙ 1 = 1 2 m l 2 [ ( 10 3 + 2 cos θ 2 ) θ ˙ 1 + ( 2 3 + cos θ 2 ) θ ˙ 2 ] d d t ( ∂ L ∂ θ ˙ 1 ) = 1 2 m l 2 [ ( 10 3 + 2 cos θ 2 ) θ ¨ 1 − 2 θ ˙ 1 θ ˙ 2 sin θ 2 + ( 2 3 + cos θ 2 ) θ ¨ 2 − θ ˙ 2 2 sin θ 2 ]⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪∂L∂θ1=−mgl[32sinθ1+12sin(θ1+θ2)]∂L∂θ˙1=12ml2[(103+2cosθ2)θ˙1+(23+cosθ2)θ˙2]ddt(∂L∂θ˙1)=12ml2[(103+2cosθ2)θ¨1−2θ˙1θ˙2sinθ2+(23+cosθ2)θ¨2−θ˙22sinθ2]{∂L∂θ1=−mgl[32sinθ1+12sin(θ1+θ2)]∂L∂θ˙1=12ml2[(103+2cosθ2)θ˙1+(23+cosθ2)θ˙2]ddt(∂L∂θ˙1)=12ml2[(103+2cosθ2)θ¨1−2θ˙1θ˙2sinθ2+(23+cosθ2)θ¨2−θ˙22sinθ2]⎩⎪⎪⎨⎪⎪⎧ ∂θ 1∂L =−mgl[ 23 sinθ 1 + 21 sin(θ 1 +θ 2 )]∂ θ˙ 1∂L = 21 ml 2 [( 310 +2cosθ 2 ) θ˙ 1 +( 32 +cosθ 2 ) θ˙ 2 ]dtd ( ∂ θ˙ 1∂L )= 21 ml 2 [( 310 +2cosθ 2 ) θ¨ 1 −2 θ˙ 1 θ˙ 2 sinθ 2 +( 32 +cosθ 2 ) θ¨ 2 − θ˙ 22 sinθ 2 ]{ ∂ L ∂ θ 2 = − 1 2 m g l sin ( θ 1 + θ 2 ) − 1 2 m l 2 ( θ ˙ 1 2 + θ ˙ 1 θ ˙ 2 ) sin θ 2 ∂ L ∂ θ ˙ 2 = 1 2 m l 2 [ ( 2 3 + cos θ 2 ) θ ˙ 1 + 2 3 θ ˙ 2 ] d d t ( ∂ L ∂ θ ˙ 2 ) = 1 2 m l 2 [ ( 2 3 + cos θ 2 ) θ ¨ 1 − θ ˙ 1 θ ˙ 2 sin θ 2 + 2 3 θ ¨ 2 ]⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪∂L∂θ2=−12mglsin(θ1+θ2)−12ml2(θ˙21+θ˙1θ˙2)sinθ2∂L∂θ˙2=12ml2[(23+cosθ2)θ˙1+23θ˙2]ddt(∂L∂θ˙2)=12ml2[(23+cosθ2)θ¨1−θ˙1θ˙2sinθ2+23θ¨2]{∂L∂θ2=−12mglsin(θ1+θ2)−12ml2(θ˙12+θ˙1θ˙2)sinθ2∂L∂θ˙2=12ml2[(23+cosθ2)θ˙1+23θ˙2]ddt(∂L∂θ˙2)=12ml2[(23+cosθ2)θ¨1−θ˙1θ˙2sinθ2+23θ¨2]⎩⎪⎪⎪⎨⎪⎪⎪⎧ ∂θ 2∂L =− 21 mglsin(θ 1 +θ 2 )− 21 ml 2 ( θ˙ 12 + θ˙ 1 θ˙ 2 )sinθ 2∂ θ˙ 2∂L = 21 ml 2 [( 32 +cosθ 2 ) θ˙ 1 + 32 θ˙ 2 ]dtd ( ∂ θ˙ 2∂L )= 21 ml 2 [( 32 +cosθ 2 ) θ¨ 1 − θ˙ 1 θ˙ 2 sinθ 2 + 32 θ¨ 2 ]根据拉格朗日方程{ τ 1 = d d t ( ∂ L ∂ θ ˙ 1 ) − ∂ L ∂ θ 1 τ 2 = d d t ( ∂ L ∂ θ ˙ 2 ) − ∂ L ∂ θ 2⎧⎩⎨⎪⎪τ1=ddt(∂L∂θ˙1)−∂L∂θ1τ2=ddt(∂L∂θ˙2)−∂L∂θ2{τ1=ddt(∂L∂θ˙1)−∂L∂θ1τ2=ddt(∂L∂θ˙2)−∂L∂θ2⎩⎨⎧ τ 1 = dtd ( ∂ θ˙ 1∂L )− ∂θ 1∂Lτ 2 = dtd ( ∂ θ˙ 2∂L )− ∂θ 2∂L求得机器人动力学模型为τ = D 1 ( θ ) θ ¨ ⏟ 惯性力项 + D 2 ( θ ) θ ˙ 2 ⏟ 向心力项 + D 3 ( θ ) θ ˙ i j ⏟ 科氏力项 + D 4 ( θ ) ⏟ 重力项 ⇔ [ τ 1 τ 2 ] = D 1 ( θ ) [ θ ¨ 1 θ ¨ 2 ] + D 2 ( θ ) [ θ ˙ 1 2 θ ˙ 2 2 ] + D 3 ( θ ) [ θ ˙ 1 θ ˙ 2 θ ˙ 1 θ ˙ 2 ] + D 4 ( θ ) \boldsymbol{\tau }=\underset{\text{惯性力项}}{\underbrace{\boldsymbol{D}_1\left( \boldsymbol{\theta } \right) \boldsymbol{\ddot{\theta}}}}+\underset{\text{向心力项}}{\underbrace{\boldsymbol{D}_2\left( \boldsymbol{\theta } \right) \boldsymbol{\dot{\theta}}^2}}+\underset{\text{科氏力项}}{\underbrace{\boldsymbol{D}_3\left( \boldsymbol{\theta } \right) \boldsymbol{\dot{\theta}}_{ij}}}+\underset{\text{重力项}}{\underbrace{\boldsymbol{D}_4\left( \boldsymbol{\theta } \right) }}\\\Leftrightarrow \left[τ1τ2τ1τ2\right] =\boldsymbol{D}_1\left( \boldsymbol{\theta } \right) \left[θ¨1θ¨2θ¨1θ¨2\right] +\boldsymbol{D}_2\left( \boldsymbol{\theta } \right) \left[θ˙21θ˙22θ˙12θ˙22\right] +\boldsymbol{D}_3\left( \boldsymbol{\theta } \right) \left[θ˙1θ˙2θ˙1θ˙2θ˙1θ˙2θ˙1θ˙2\right] +\boldsymbol{D}_4\left( \boldsymbol{\theta } \right)τ= 惯性力项D 1 (θ) θ¨ + 向心力项D 2 (θ) θ˙ 2 + 科氏力项D 3 (θ) θ˙ ij + 重力项D 4 (θ)⇔[ τ 1τ 2 ]=D 1 (θ)[ θ¨ 1θ¨ 2 ]+D 2 (θ)[ θ˙ 12θ˙ 22 ]+D 3 (θ)[ θ˙ 1 θ˙ 2θ˙ 1 θ˙ 2 ]+D 4 (θ)其中{ D 1 ( θ ) = m l 2 [ 5 3 + cos θ 2 1 3 + 1 2 cos θ 2 1 3 + 1 2 cos θ 2 1 3 ] D 2 ( θ ) = m l 2 [ 0 − 1 2 sin θ 2 1 2 sin θ 2 0 ] D 3 ( θ ) = m l 2 [ − sin θ 2 0 0 0 ] D 4 ( θ ) = m g l [ 3 2 sin θ 1 + 1 2 sin ( θ 1 + θ 2 ) 1 2 sin ( θ 1 + θ 2 ) ]⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪D1(θ)=ml2[53+cosθ213+12cosθ213+12cosθ213]D2(θ)=ml2[012sinθ2−12sinθ20]D3(θ)=ml2[−sinθ2000]D4(θ)=mgl[32sinθ1+12sin(θ1+θ2)12sin(θ1+θ2)]{D1(θ)=ml2[53+cosθ213+12cosθ213+12cosθ213]D2(θ)=ml2[0−12sinθ212sinθ20]D3(θ)=ml2[−sinθ2000]D4(θ)=mgl[32sinθ1+12sin(θ1+θ2)12sin(θ1+θ2)]⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧ D 1 (θ)=ml 2 [ 35 +cosθ 231 + 21 cosθ 2 31 + 21 cosθ 231 ]D 2 (θ)=ml 2 [ 021 sinθ 2 − 21 sinθ 20 ]D 3 (θ)=ml 2 [ −sinθ 20 00 ]D 4 (θ)=mgl[ 23 sinθ 1 + 21 sin(θ 1 +θ 2 )21 sin(θ 1 +θ 2 ) ] 3.3 动力学仿真初始状态为 [ θ 1 θ 2 ] = [ 180 ° 0 ° ] \left[θ1θ2θ1θ2\right] =\left[180\degree0\degree180\degree0\degree\right] [ θ 1θ 2 ]=[ 180°0° ]、 [ θ ˙ 1 θ ˙ 2 ] = [ 0 0 ] \left[θ˙1θ˙2θ˙1θ˙2\right] =\left[0000\right] [ θ˙ 1θ˙ 2 ]=[ 00 ],输入力矩为 [ τ 1 τ 2 ] = [ 2 0.5 ] \left[τ1τ2τ1τ2\right] =\left[20.520.5\right] [ τ 1τ 2 ]=[ 20.5 ] 该初始条件下,平面2R机器人的角度、角速度、角加速度、能量、雅克比矩阵行列式数值波形图以及机器人末端执行器运动轨迹图如图所示🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 问题描述2 AI算法2.1 局部贪婪搜索2.2 随机行走爬山法2.3 首选爬山法2.4 随机重启爬山法2.5 模拟退火算法2.6 局部束搜索2.7 随机束搜索2.8 遗传算法3 结果展示1 问题描述八皇后问题——在8×8格的国际象棋盘上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。在八皇后问题中,不关心求解的路径,而只关注是否达到目标状态——即重要的是最终皇后在棋盘上的布局,而不是皇后加入的先后次序。由于棋盘中棋子状态数呈指数增长,因此八皇后问题的状态空间庞大且未知,利用普通搜索技术——如BFS、DFS、UCS等无法完成既定目标,因为这些算法需要系统地遍历状态空间。因此选择局部搜索算法,其只探索当前状态的邻域,在邻域中选择若干个状态继续探索它们的邻域,直到找到目标。2 AI算法2.1 局部贪婪搜索经典的爬山算法也称为局部贪婪搜索(Greedy Local Search),其在算法层的原理为:随机生成初始状态;探索初始状态的邻域 K K K,在 K K K中选择代价最小(或价值最高)的状态;若此状态代价小于当前,则选择此状态为下一个状态;否则算法停止;重复(ii)(iii)直至最优或算法停止。此算法的实现过程简单,本质上是一种梯度下降算法,因此容易陷入局部最优状态。为了解决爬山法受限于局部最优状态的问题,提出了其几个变种。2.2 随机行走爬山法随机行走爬山法(Random Hill Climbing),其在算法层的原理为:随机生成初始状态;探索初始状态的邻域 K K K,在 K K K中随机选择状态;以一定的概率接受此状态,此概率可以关联“陡峭”程度;不断重复(ii)(iii)直至最优。随机行走爬山法的优点在于,对所有邻域状态都有概率接受,只不过离最优值近的被选中的概率大,这样可以避免在局部最优点停滞不前。随机行走爬山法的收敛速度通常慢于局部贪婪搜索,但在某些状态空间地形图上可以找到更优解。2.3 首选爬山法首选爬山法,是随机行走爬山法和经典爬山法的折中,其在算法层的原理为:随机生成初始状态;探索初始状态的邻域 K K K,在 K K K中随机选择状态;若该状态优于当前,则接受;否则重新选择;不断重复(ii)(iii)直至最优。首选爬山法既避免了随机行走爬山法的“漫无目的”,也防止快速陷入局部最优,其在邻域状态很庞大的情况下效率比随机行走更高,但是其依然存在陷入局部最优的可能性。2.4 随机重启爬山法随机重启爬山法(Random Restart Hill Climbing),其在算法层的原理为:随机生成初始状态;探索初始状态的邻域 K K K,在 K K K中选择代价最小(或价值最高)的状态;若此状态优于当前,则选择此状态为下一个状态;否则随机生成下一个状态;重复(ii)(iii)直至最优。随机重启爬山法是对经典爬山算法受限于局部最优状态的本质改良,但可能因为不断重启影响效率。2.5 模拟退火算法模拟退火算法(Simulated Annealing,SA)类似于随机行走爬山法,只不过SA随机因素的引入并非关联“陡峭”程度,而是模拟了退火这一物理过程。首先解释热力学上的退火过程。如图所示,首先物体处于非晶体的稳定状态。现将其加温至充分高,此时分子热运动加剧,物体的状态充满随机性——几乎可以向任何方向运动;再让其徐徐冷却——即退火,粒子趋近于能量最小,也就可能达到处于晶体的稳态。基于具体退火的物理过程,引入了退火概率转移函数:p = { 1 , i f E n e w b e t t e r t h a n E o l d exp ( − Δ E / T ) i f E n e w w o r s e t h a n E o l d p={1,ifEnewbetterthanEoldexp(−ΔE/T)ifEnewworsethanEold{1,ifEnewbetterthanEoldexp(−ΔE/T)ifEnewworsethanEoldp={ 1,ifE new betterthanE oldexp(−ΔE/T)ifE new worsethanE old在SA的初期,认为是高温 T T T,使得SA趋于完全的随机行走——因为其对于劣于当前状态的接受率也趋于1;随着搜索次数的迭代,温度不断降低,此时SA倾向于接收离最优值更近的状态。其中 T T T的降温过程可以用最简单的指数降温: T ∗ = α T ( α = 0.8 0.99 ) T^*=\alpha T\left( \alpha =0.8~0.99 \right) T ∗ =αT(α=0.8 0.99),或者采用其他的下降方式,如 T ∗ = T ( 1 + n ) ( n 为迭代次数 ) T^*=\frac{T}{\left( 1+n \right)}\,\,\left( n\text{为迭代次数} \right) T ∗ = (1+n)T (n为迭代次数)。综上所述,SA在算法层的原理为:随机生成初始状态;探索初始状态的邻域 K K K,在 K K K中随机选择状态;若此状态优于当前,则选择此状态为下一个状态;否则以概率转移函数为基础接受或拒绝此状态;重复(ii)(iii)直至最优。值得注意的是,SA算法依然可能陷入局部最优,但大多数情况下具有良好的表现。2.6 局部束搜索到目前为止的局部搜索算法都是单线的,即每次只探索一个状态。束搜索的思想是:内存虽然有限,但每次只保留一个状态信息又过于极端。因此束搜索算法下,每次搜索都将在内存中保留 k k k个状态, k k k称为集束宽度局部束搜索(Local Beam Search)在算法层的原理为:随机生成 k k k个初始状态;分别探索 k k k个初始状态的邻域 K i ( i = 1 , 2 , ⋯ k ) K_i\left( i=1,2,\cdots k \right) K i (i=1,2,⋯k),在所有邻域的并集中选择 k k k个最优的状态;重复(ii)直至最优。在局部束搜索中,有用的信息在并行的搜索线程间传递,算法将很快放弃没有成果的搜索而把资源都用在取得最大进展的路径上。但如果是最简单形式的局部束搜索,那么由于这 个状态缺乏多样性,它们很快会聚集到状态空间中的一小块区域内,甚至使得搜索代价比高昂的爬山法版本还要多。2.7 随机束搜索如随机行走爬山法向爬山法中添加随机因子,解决受限于局部最优的问题一样,随机束搜索(Stochastic Beam Search)向局部束搜索中添加随机因子,防止搜索空间的状态聚合,以保持多样性。其在算法层的原理为:随机生成 k k k个初始状态;分别探索 k k k个初始状态的邻域 K i ( i = 1 , 2 , ⋯ k ) K_i\left( i=1,2,\cdots k \right) K i (i=1,2,⋯k),在所有邻域的并集中随机选择 k k k个状态,随机选择概率可以关联于“陡峭”程度或其他因素;重复(ii)直至最优。2.8 遗传算法遗传算法(Genetic Algorithm, GA)类似束搜索,不同在于其选择 个状态的原则,既不是局部束搜索的“贪婪式选择”,也不是随机束搜索简单的概率选择,而是模拟了自然进化演变过程。其在算法层的原理是:随机生成 k k k个初始状态形成初始种群;采用适应度函数对种群进行自然选择,增加种群中距离最优值近的个体的比例;对自然选择后的种群进行杂交,生成子代种群;对子代随机进行变异,以增加种群多样性,防止状态聚合;重复(ii)~(iv)直至最优。注意:上面原理可以配合源码学习,加深理解3 结果展示完整代码关注下方公众号回复ML001获取🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 仿生分析2 模型假设2.1 单脚支撑与双脚支撑阶段2.2 落地碰撞阶段3 模型建立4 参考文献1 仿生分析仿人机器人是基于仿生原理,在机械结构和运动过程上模仿人的行为以达到预期性能的机电装置。如图1.1所示,一般地,仿人机器人结构可分为七连杆,主导运动的腿部主要由髋关节、膝关节、踝关节组成,其余为上肢。与人类似,在机器人行走运动过程中分为三个阶段:单脚支撑、落地碰撞与双脚支撑阶段。机器人处于单脚支撑阶段时,摆动腿膝关节被动摆动,但在落地碰撞进入双脚支撑阶段之前,摆动腿会绷直以更好地支撑身体重量 [ 1 ] ^{[1]} [1] 。有研究指出 [ 2 ] ^{[2]} [2] ,髋关节、膝关节在机器人行走、跑步等常规运动——即单脚支撑与双脚支撑阶段占有主导地位;而踝关节的分析在这两个阶段中并不必要,其作用体现在落地碰撞瞬间的驱动力上。因此,仿人机器人运动系统模型可以分为两个方面:单脚支撑与双脚支撑阶段,此时不考虑踝关节驱动,机器人简化为五连杆模型,如图1.2所示;落地碰撞阶段,此时踝关节提供驱动并产生自由度变化。仿人机器人的综合模型可视为上述两个方面的叠加 [ 3 ] ^{[3]} [3] 。2 模型假设2.1 单脚支撑与双脚支撑阶段
1 状态观测器2 状态滤波器3 卡尔曼滤波器4 具体案例:船舶GPS定位1 状态观测器在工程上,部分状态参数测量的成本过高,或是用现有仪器无法测得,此时需要引入状态观测器。状态观测器以原系统输入、输出为输入,输出估计的状态变量。原系统状态描述:{ x k =Ax k−1 +Bu k−1y k =Cx k状态观测器状态描述:{ x ~ k = A x ~ k − 1 + B u k − 1 + f [ y k − 1 − y ~ k − 1 ] y ~ k = C x ~ k{x~k=Ax~k−1+Buk−1+f[yk−1−y~k−1]y~k=Cx~k{x~k=Ax~k−1+Buk−1+f[yk−1−y~k−1]y~k=Cx~k{ x~ k =A x~ k−1 +Bu k−1 +f[y k−1 − y~ k−1 ]y~ k =C x~ k定义误差向量 e k = x k − x ~ k e_k=x_k-\tilde{x}_k e k =x k − x~ k ,则两系统状态方程相减得系 统 误 差 传 递 方 程 e k = ( A − f C ) e k − 1 系统误差传递方程e_k=\left( A-fC \right) e_{k-1}系统误差传递方程e k =(A−fC)e k−1其通解为矩阵指数函数,所以当 A − f C A-fC A−fC特征值小于0时,误差向量 e k e_k e k 各分量均趋于0,即状态观测器能直接估计原系统状态。本质上,状态观测器是针对状态空间方程描述的确定系统,从错误的系统状态估计值不断收敛到正确的系统状态估计值的数学模型。2 状态滤波器实际的系统往往是随机过程,状态描述为:{ x k = A x k − 1 + B u k − 1 + w k − 1 y k = C x k + v k{xk=Axk−1+Buk−1+wk−1yk=Cxk+vk{xk=Axk−1+Buk−1+wk−1yk=Cxk+vk{ x k =Ax k−1 +Bu k−1 +w k−1y k =Cx k +v k其中 w k w_k w k 称为过程噪声,主要由系统运行过程中的外力导致,如旋翼飞行器运动过程中受到的阵风; v k v_k v k 称为测量噪声,主要由系统测量过程中测量仪器误差、精度不足导致。本质上,状态滤波器是针对随机状态空间方程描述的随机系统,从不确定观测中提取信息,得到系统状态最优估计的数学模型。当 w k w_k w k 与 v k v_k v k 为高斯白噪声且相互独立时,状态滤波器为卡尔曼滤波器。状态滤波器的核心是通过贝叶斯原理不断调整滤波器增益矩阵,以减小随机干扰,使估计的系统状态趋近于真实状态;而状态观测器的核心是通过极点配置确定观测器增益矩阵,使估计的状态跟踪当前系统的状态。一般地,状态滤波器性能优于状态观测器,但在实际系统中,若随机噪声非高斯噪声,或有若干非线性环节,按常规噪声模型建立的状态滤波器可能发散,此时则更需要状态观测器的稳定性。3 卡尔曼滤波器机器学习:详解贝叶斯网络+例题分析曾提到贝叶斯方法的思考模式:参数先验信息 π ( θ ) + 样本观测数据 X = 后验分布 P ( θ ∣ X ) \text{参数先验信息}\pi \left( \theta \right) +\text{样本观测数据}X=\text{后验分布}P\left( \theta |X \right)参数先验信息π(θ)+样本观测数据X=后验分布P(θ∣X)即在得到新的样本信息之前,人们对模型的认知是先验分布 π ( θ ) \pi \left( \theta \right) π(θ),在得到新的样本信息 X X X后,人们对模型的认知为后验分布 P ( θ ∣ X ) P\left( \theta |X \right) P(θ∣X)。由此引出卡尔曼滤波器。考虑一个随机系统:{ x k = A x k − 1 + B u k − 1 + w k − 1 z k = C x k + v k{xk=Axk−1+Buk−1+wk−1zk=Cxk+vk{xk=Axk−1+Buk−1+wk−1zk=Cxk+vk{ x k =Ax k−1 +Bu k−1 +w k−1z k =Cx k +v k其中 w k N ( 0 , Q ) w_k~N\left( 0, Q \right) w k N(0,Q)、 v k N ( 0 , R ) v_k~N\left( 0, R \right) v k N(0,R),且 w k w_k w k 与 v k v_k v k 相互独立。忽略噪声可建立该系统的先验数学模型(噪声仅满足随机分布,无法建模):状态预测方程 { x ^ k − = A x ^ k − 1 + B u k − 1 z ^ k − = C x ^ k {\text{状态预测方程}{x^−k=Ax^k−1+Buk−1z^−k=Cx^k{x^k−=Ax^k−1+Buk−1z^k−=Cx^k}状态预测方程{ x^ k− =A x^ k−1 +Bu k−1z^ k− =C x^ k基于上述系统,从贝叶斯方法引出卡尔曼滤波器状态更新方程:状态更新方程 : x ^ k = x ^ k − + K k ( z k − z ^ k − ) {\text{状态更新方程}: \hat{x}_k=\hat{x}_{k}^{-}+K_k\left( z_k-\hat{z}_{k}^{-} \right) }状态更新方程: x^ k = x^ k− +K k (z k − z^ k− )其中 x ^ k \hat{x}_k x^ k 是后验状态估计,即经过一次修正后,当前系统状态的最优估计值; x ^ k − \hat{x}_{k}^{-} x^ k− 为系统状态的先验估计; z k z_k z k 为当前状态的测量样本值; z ^ k − \hat{z}_{k}^{-} z^ k− 为当前状态的先验测量值; z k − z ^ k − z_k-\hat{z}_{k}^{-} z k − z^ k− 代表实际系统与估计系统间随机误差的影响; K k K_k K k 权衡先验模型与实测数据间对后验分布的关系,称为卡尔曼增益,显然, z k − z ^ k − z_k-\hat{z}_{k}^{-} z k − z^ k− 越小表明实际与预测越接近,则后验分布越趋近于先验分布, z k − z ^ k − z_k-\hat{z}_{k}^{-} z k − z^ k− 越大表明预测越不可信,需要通过实测来大幅修正先验分布。下面要确定 K k K_k K k 使后验模型的修正效果最佳,即 x ^ k \hat{x}_k x^ k 越接近真实值 x k {x}_k x k 。定义误差向量{ 后验误差向量 e k = x k − x ^ k 先验误差向量 e k − = x k − x ^ k −{后验误差向量ek=xk−x^k先验误差向量e−k=xk−x^−k{后验误差向量ek=xk−x^k先验误差向量ek−=xk−x^k−{ 后验误差向量e k =x k − x^ k先验误差向量e k− =x k − x^ k−定义后验误差协方差矩阵为:P k = E ( e k e k T ) = [ σ e 1 2 σ e 1 σ e 2 ⋯ σ e 1 σ e n σ e 2 σ e 1 σ e 2 2 ⋯ σ e 2 σ e n ⋮ ⋮ ⋱ ⋮ σ e n σ e 1 σ e n σ e 2 ⋯ σ e n 2 ] P_k=E\left( e_ke_{k}^{T} \right) =\left[σ2e1σe2σe1⋮σenσe1σe1σe2σ2e2⋮σenσe2⋯⋯⋱⋯σe1σenσe2σen⋮σ2enσe12σe1σe2⋯σe1σenσe2σe1σe22⋯σe2σen⋮⋮⋱⋮σenσe1σenσe2⋯σen2\right]P k =E(e k e kT )= ⎣⎢⎢⎢⎡ σ e12σ e2 σ e1⋮σ en σ e1 σ e1 σ e2σ e22⋮σ en σ e2 ⋯⋯⋱⋯ σ e1 σ enσ e2 σ en⋮σ en2 ⎦⎥⎥⎥⎤同理也有先验误差协方差矩阵 P k − P_{k}^{-} P k− 。根据最小方差估计原理,设损失函数为 t r ( P k ) \mathrm{tr}\left( P_k \right) tr(P k ),即要求K k = a r g min [ t r ( P k ) ] K_k=\mathrm{arg}\min \left[ \mathrm{tr}\left( P_k \right) \right]K k =argmin[tr(P k )]将 P k P_k P k 展开为:上面运用了状态更新方程与状态预测方程,考虑到 e k − e_{k}^{-} e k− 与 v k v_k v k 相互独立,则进一步:P k =(I−K k C)E(e k− e k− T )(I−K k C) T +K k E(v k v kT )K kT=(I−K k C)P k− (I−K k C) T +K k RK kT令 ∂ t r ( P k ) ∂ K k = 0 \frac{\partial \mathrm{tr}\left( P_k \right)}{\partial K_k}=0 ∂K k∂tr(P k ) =0,即卡尔曼增益调整方程 K k = P k − C T C P k − C T + R {\text{卡尔曼增益调整方程}K_k=\frac{P_{k}^{-}C^T}{CP_{k}^{-}C^T+R}}卡尔曼增益调整方程K k = CP k− C T +RP k− C T将 K k K_k K k 代入后验误差协方差矩阵表达式,即得协方差更新方程 P k = ( I − K k H ) P k − {\text{协方差更新方程}P_k=\left( I-K_kH \right) P_{k}^{-}}协方差更新方程P k =(I−K k H)P k−要更新 K k K_k K k ,则只需要确定先验误差协方差矩阵 P k − P_{k}^{-} P k− 。同样地,将 P k − P_{k}^{-} P k− 展开为P k − = E ( e k − e k − T ) = E [ ( x k − x ^ k − ) ( x k − x ^ k − ) T ] = E [ ( A e k − 1 + w k − 1 ) ( A e k − 1 + w k − 1 ) T ] = A E ( e k − 1 e k − 1 T ) A T + E ( w k − 1 w k − 1 T ) P_{k}^{-}=E\left( e_{k}^{-}e_{k}^{-T} \right) \\=E\left[ \left( x_k-\hat{x}_{k}^{-} \right) \left( x_k-\hat{x}_{k}^{-} \right) ^T \right] \\=E\left[ \left( Ae_{k-1}+w_{k-1} \right) \left( Ae_{k-1}+w_{k-1} \right) ^T \right] \\=AE\left( e_{k-1}e_{k-1}^{T} \right) A^T+E\left( w_{k-1}w_{k-1}^{T} \right)P k− =E(e k− e k−T )=E[(x k − x^ k− )(x k − x^ k− ) T ]=E[(Ae k−1 +w k−1 )(Ae k−1 +w k−1 ) T ]=AE(e k−1 e k−1T )A T +E(w k−1 w k−1T )考虑到 e k e_k e k 与 w k w_k w k 相互独立,进一步得到协方差预测方程 P k − = A P k − 1 A T + Q {\text{协方差预测方程}P_{k}^{-}=AP_{k-1}A^T+Q}协方差预测方程P k− =AP k−1 A T +Q至此,得到卡尔曼滤波的五大基本公式,其中核心方程为基于最小方差估计的卡尔曼增益调整方程,具体工作流程如图所示。4 具体案例:船舶GPS定位有一船舶出港沿某直线方向航行,辅助北斗卫星进行定位和测速。假设① 船舶加速度 a ( t ) a\left( t \right) a(t)=机动加速度 u ( t ) u\left( t \right) u(t)+随机加速度 w ( t ) w\left( t \right) w(t),其中 w ( t ) w\left( t \right) w(t)符合高斯分布;② GPS观测噪声 v ( t ) v\left( t \right) v(t)符合高斯分布。要求用卡尔曼滤波器估计真实运动轨迹。解决方案首先建立随机系统的真实模型。以码头出发点为原点,采样周期(雷达扫描周期)为 T T T,用 s ( k ) s(k) s(k)表示船舶在采样时刻 k T kT kT处的真实位置,用 z ( k ) z\left( k \right) z(k)表示在时刻 k T kT kT处的GPS定位观测值,则真实系统输出方程为 z ( k ) = s ( k ) + v ( k ) z(k)=s(k)+v(k) z(k)=s(k)+v(k)。记在 k T kT kT时刻处船舶速 s ˙ ( k ) \dot{s}\left( k \right) s˙ (k),加速度为 a ( k ) a\left( k \right) a(k),由匀加速公式有:s ( k + 1 ) = s ( k ) + s ˙ ( k ) T + 1 2 a ( k ) T 2 ⇒ s ˙ ( k + 1 ) = s ˙ ( k ) + T a ( k ) s\left( k+1 \right) =s\left( k \right) +ṡ\left( k \right) T+\frac{1}{2}a\left( k \right) T^2\\\Rightarrow \dot{s}\left( k+1 \right) =ṡ\left( k \right) +Ta\left( k \right)s(k+1)=s(k)+ s˙ (k)T+ 21 a(k)T 2⇒ s˙ (k+1)= s˙ (k)+Ta(k)其中 a ( k ) = u ( k ) + w ( k ) a(k)=u(k)+w(k) a(k)=u(k)+w(k)。定义在采样时刻 k T kT kT系统状态 x ( k ) x\left( k \right) x(k)为船舶的位置和速度,即 x ( k ) = [ s ( k ) s ˙ ( k ) ] T x(k)=\left[s(k)s˙(k)s(k)s˙(k)\right] ^T x(k)=[ s(k) s˙ (k) ] T 可得到船舶运动的状态空间模型{ [ s ( k + 1 ) s ˙ ( k + 1 ) ] = [ 1 T 0 1 ] [ s ( k ) s ˙ ( k ) ] + [ 0.5 T T ] u ( k ) + [ 0.5 T T ] w ( k ) z ( k ) = [ 1 0 ] [ s ( k ) s ˙ ( k ) ] + v ( k )⎧⎩⎨⎪⎪⎪⎪⎪⎪[s(k+1)s˙(k+1)]=[10T1][s(k)s˙(k)]+[0.5TT]u(k)+[0.5TT]w(k)z(k)=[10][s(k)s˙(k)]+v(k){[s(k+1)s˙(k+1)]=[1T01][s(k)s˙(k)]+[0.5TT]u(k)+[0.5TT]w(k)z(k)=[10][s(k)s˙(k)]+v(k)⎩⎪⎪⎨⎪⎪⎧ [ s(k+1)s˙ (k+1) ]=[ 10 T1 ][ s(k)s˙ (k) ]+[ 0.5TT ]u(k)+[ 0.5TT ]w(k)z(k)=[ 1 0 ][ s(k)s˙ (k) ]+v(k)在不考虑机动目标的动力因素即 u ( k ) = 0 u(k)=0 u(k)=0时,将匀速直线运动的船舶系统扩展到四维,状态包含水平和纵向的位置和速度,则系统方程可化为[ x ( k ) x ˙ ( k ) y ( k ) y ˙ ( k ) ] = [ 1 T 0 0 0 1 0 0 0 0 1 T 0 0 0 1 ] [ x ( k − 1 ) x ˙ ( k − 1 ) y ( k − 1 ) y ˙ ( k − 1 ) ] + [ 0.5 T 2 T 0.5 T 2 T ] [ w 1 ( k ) w 2 ( k ) w 3 ( k ) w 4 ( k ) ] z ( k ) = [ 1 0 0 0 0 0 1 0 ] [ x ( k ) x ˙ ( k ) y ( k ) y ˙ ( k ) ] + [ v 1 ( k ) v 2 ( k ) ] \left[x(k)x˙(k)y(k)y˙(k)x(k)x˙(k)y(k)y˙(k)\right] =\left[1000T100001000T11T000100001T0001\right] \left[x(k−1)x˙(k−1)y(k−1)y˙(k−1)x(k−1)x˙(k−1)y(k−1)y˙(k−1)\right] +\left[0.5T2T0.5T2T0.5T2T0.5T2T\right] \left[w1(k)w2(k)w3(k)w4(k)w1(k)w2(k)w3(k)w4(k)\right] \\z(k)=\left[1000010010000010\right] \left[x(k)x˙(k)y(k)y˙(k)x(k)x˙(k)y(k)y˙(k)\right] +\left[v1(k)v2(k)v1(k)v2(k)\right]⎣⎢⎢⎡ x(k)x˙ (k)y(k)y˙ (k) ⎦⎥⎥⎤ = ⎣⎢⎢⎡ 1000 T100 0010 00T1 ⎦⎥⎥⎤ ⎣⎢⎢⎡ x(k−1)x˙ (k−1)y(k−1)y˙ (k−1) ⎦⎥⎥⎤ + ⎣⎢⎢⎡ 0.5T 2 T 0.5T 2 T ⎦⎥⎥⎤ ⎣⎢⎢⎡ w 1 (k)w 2 (k)w 3 (k)w 4 (k) ⎦⎥⎥⎤z(k)=[ 10 00 01 00 ] ⎣⎢⎢⎡ x(k)x˙ (k)y(k)y˙ (k) ⎦⎥⎥⎤ +[ v 1 (k)v 2 (k) ]现假设初始位置为(-100m,200m),水平运动初速度为2m/s,垂直运动初速度为20m/s,雷达扫描周期 T = 1 s T=1s T=1s,则系统状态空间进一步化为{ x ( k + 1 ) = A x ( k ) + B u ( k ) + Γ w ( k ) z ( k ) = H x ( k ) + v ( k ){x(k+1)=Ax(k)+Bu(k)+Γw(k)z(k)=Hx(k)+v(k){x(k+1)=Ax(k)+Bu(k)+Γw(k)z(k)=Hx(k)+v(k){ x(k+1)=Ax(k)+Bu(k)+Γw(k)z(k)=Hx(k)+v(k)其中 A = [ 1 1 0 0 0 1 0 0 0 0 1 1 0 0 0 1 ] A=\left[10001100001000111100010000110001\right] A= ⎣⎢⎢⎡ 1000 1100 0010 0011 ⎦⎥⎥⎤ , B = Γ = [ 0.5 1 0.5 1 ] B=\varGamma =\left[0.510.510.510.51\right] B=Γ= ⎣⎢⎢⎡ 0.5 1 0.5 1 ⎦⎥⎥⎤ , H = [ 1 0 0 0 0 0 1 0 ] H=\left[1000010010000010\right] H=[ 10 00 01 00 ] 。设过程噪声 w ∼ N ( 0 , 0. 1 2 ) w\sim N(0,0.1^2) w∼N(0,0.1 2 ),其四个分量独立同分布,则Q ∗ = E ( w 4 × 1 w 4 × 1 T ) = σ w 2 [ 1 1 1 1 ] Q^*=E(w_{4\times 1}w_{4\times 1}^{T})=\sigma _{w}^{2}\left[11111111\right]Q ∗ =E(w 4×1 w 4×1T )=σ w2 ⎣⎢⎢⎡ 1 1 1 1 ⎦⎥⎥⎤考虑噪声矩阵 Γ \varGamma Γ的加权得到过程噪声协方差矩阵Q = Q ∗ ⋅ Γ = σ w 2 [ 0.5 1 0.5 1 ] Q=Q^*\cdot \varGamma =\sigma _{w}^{2}\left[0.510.510.510.51\right]Q=Q ∗ ⋅Γ=σ w2 ⎣⎢⎢⎡ 0.5 1 0.5 1 ⎦⎥⎥⎤观测噪声 v ∼ N ( 0 , 1 0 2 ) v\sim N(0,10^2) v∼N(0,10 2 ),其两个分量独立同分布,则观测噪声协方差矩阵R = σ v 2 [ 1 1 ] R=\sigma _{v}^{2}\left[1111\right]R=σ v2 [ 1 1 ]依此建立卡尔曼滤波器模型。设置最优估计初值 x ^ ( 1 ) = [ − 100 2 − 200 20 ] T \hat{x}(1)=\left[−1002−20020−1002−20020\right] ^T x^ (1)=[ −100 2 −200 20 ] T 、先验协方差矩阵 P 1 − = [ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 ] P_{1}^{-}=\left[10000100001000011000010000100001\right] P 1− = ⎣⎢⎢⎡ 1000 0100 0010 0001 ⎦⎥⎥⎤ ,并以此开始迭代,得到实验图像如图所示。在使用卡尔曼滤波器时,应至少保证系统模型或系统测量至少一个有足够的精度,否则卡尔曼滤波器无法从中提取到正确的估计信息。这是因为卡尔曼滤波调整的是对先验模型值与仪器测量值间的信任权重,若二者都不准确则卡尔曼跟踪的数值亦不准确。为验证这一结论,下面保持测量噪声不变,调整过程噪声方差,如图所示。完整代码关注下方公众号回复“EE001”获取。🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
解决方案:在Windows中使用文件之前, 先在Ubuntu中使用FFMPEG使用常规编解码器对MP4文件进行编码。sudo apt-get install ffmpeg也可以在Windows中执行此操作, 但需要手动下载FFMPEG的安装程序, 然后将目录添加到PATH环境变量中。安装该工具后,可以使用以下命令轻松地将文件转换为常规MP4文件(替换input_file.mp4和output_file.mp4):ffmpeg -y -i input_file.mp4 -c:v libx264 -c:a aac -strict experimental -tune fastdecode -pix_fmt yuv420p -b:a 192k -ar 48000 output_file.mp4输出的mp4文件在Windows系统下运行良好。
1 下载稠密点云工程2 修改工程2.1 增加RGB点云2.2 增加点云保存功能3 编译工程3.1 普通模式3.2 ROS模式4 测试工程4.1 普通模式4.2 ROS模式前文:从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(一):安装与配置从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(二):相机测试与标定从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(三):使用USB相机运行ORBSLAM从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(四):ORBSLAM评估工具EVO的使用特别感谢:https://www.jianshu.com/p/5e7b8358893f提供的指导1 下载稠密点云工程cd pointCloud git clone https://github.com/gaoxiang12/ORBSLAM2_with_pointcloud_map.git将原 ORB SLAM2 中的 Vocabulary及其内ORBvoc.txt.tar.gz拷贝到pointCloud的ORB_SLAM2_modified 文件夹下,删除ORB_SLAM2_modified/Thirdparty/DBoW2/build 和 ORB_SLAM2_modified/Thirdparty/g2o/build2 修改工程2.1 增加RGB点云若不进行修改,生成的稠密点云只有灰度,修改步骤如下:1.在Tracking.h中声明RGB矩阵Frame mCurrentFrame; cv::Mat mImRGB; //new declared cv::Mat mImGray;2.在Tracking.cc中定义RGB矩阵cv::Mat Tracking::GrabImageRGBD(const cv::Mat &imRGB,const cv::Mat &imD, const double &timestamp) { mImRGB = imRGB; // new mImGray = imRGB; ......3.在Tracking.cc中定义RGB点云映射mpPointCloudMapping->insertKeyFrame( pKF, this->mImGray, this->mImDepth );//change the mImGray to mImRGB as next row mpPointCloudMapping->insertKeyFrame( pKF, this->mImRGB, this->mImDepth );// new2.2 增加点云保存功能1.修改pclsudo gedit ORB_SLAM2_modified/src/pointcloudmapping.cc加入头文件#include <pcl/io/pcd_io.h>在void PointCloudMapping::viewer() 函数中加入保存地图的命令:... for ( size_t i=lastKeyframeSize; i<N ; i++ ) { PointCloud::Ptr p = generatePointCloud( keyframes[i], colorImgs[i], depthImgs[i] ); *globalMap += *p; } pcl::io::savePCDFileBinary("vslam.pcd", *globalMap); // new ...2.查看pcd点云文件sudo apt-get install pcl-tools pcl_viewer vslam.pcd3 编译工程3.1 普通模式cd ORB_SLAM2_modified ./build.sh3.2 ROS模式1.配置ROS路径sudo vi ~/.bashrc export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:~/Project/SLAM/ORBSLAM2/ORB_SLAM2/pointCloud/ORB_SLAM2_modified/Examples/ROS source ~/.bashrc2.修改ROS CMakeLists.txt即参照 ORB_SLAM2_modified/CMakeLists.txt 文件,把 PCL 相关的设置添加到 ORB_SLAM2_modified/Examples/ROS/ORB_SLAM2/CMakeLists.txt 文件中。具体而言,如下:... find_package(Eigen3 3.1.0 REQUIRED) find_package(Pangolin REQUIRED) find_package( PCL 1.7 REQUIRED ) ####### 1 include_directories( ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/../../../ ${PROJECT_SOURCE_DIR}/../../../include ${Pangolin_INCLUDE_DIRS} ${PCL_INCLUDE_DIRS} ####### 2 ) add_definitions( ${PCL_DEFINITIONS} ) ####### 3 link_directories( ${PCL_LIBRARY_DIRS} ) ####### 4 set(LIBS ${OpenCV_LIBS} ${EIGEN3_LIBS} ${Pangolin_LIBRARIES} ${PROJECT_SOURCE_DIR}/../../../Thirdparty/DBoW2/lib/libDBoW2.so ${PROJECT_SOURCE_DIR}/../../../Thirdparty/g2o/lib/libg2o.so ${PROJECT_SOURCE_DIR}/../../../lib/libORB_SLAM2.so ${PCL_LIBRARIES} ####### 5 ) ...4 测试工程4.1 普通模式首先对 rgbd_dataset_freiburg1_xyz 中的 RGB 文件和 Depth 文件进行匹配:cd ~/Project/SLAM/ORBSLAM2/ORB_SLAM2/pointCloud/ORB_SLAM2_modified/dataSet/rgbd_dataset_freiburg1_xyz python associate.py rgb.txt depth.txt > association.txt然后即可运行cd /ORB_SLAM2_modified ./bin/rgbd_tum ./Vocabulary/ORBvoc.bin Examples/RGB-D/TUM1.yaml ./dataSet/rgbd_dataset_freiburg1_xyz ./dataSet/rgbd_dataset_freiburg1_xyz/association.txt结果如下:4.2 ROS模式运行ROS前,要更改相机参数文件如下// 终端A roscore // 终端B rosrun ORB_SLAM2 RGBD Vocabulary/ORBvoc.txt Examples/RGB-D/TUM1_ROS.yaml // 终端C rosbag play --pause rgbd_dataset_freiburg1_xyz .bag /camera/rgb/image_color:=/camera/rgb/image_raw /camera/depth/image:=/camera/depth_registered/image_raw其中TUM1_ROS.yaml即为上述改过相机参数的标签文件;按空格键可以启动bag播放,最终建图效果与普通模式相同。
1 工具安装2 数据测试2.1 KITTI数据集2.2 EuRoC数据集2.3 TUM数据集3 参数使用说明前文:从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(一):安装与配置从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(二):相机测试与标定从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(三):使用USB相机运行ORBSLAM1 工具安装git clone https://github.com/MichaelGrupp/evo.git cd evo pip3 install --editable . --upgrade --no-binary evo执行下面指令验证安装是否成功:$ evo_ape -h usage: evo_ape [-h] {kitti,tum,euroc,bag} ... Absolute pose error (APE) metric app (c) evo authors positional arguments: {kitti,tum,euroc,bag} optional arguments: -h, --help show this help message and exit2 数据测试2.1 KITTI数据集cd test/data evo_traj kitti KITTI_00_ORB.txt KITTI_00_SPTAM.txt --ref=KITTI_00_gt.txt -p --plot_mode=xz若测试通过说明EVO配置无误,本文在配置过程中遇到下面问题,特作记录。错误1:ImportError cannot import name _imaging from PIL[ERROR] evo module evo.main_ape crashed - no logfile written (disabled)解决方法:更新Pillow库,根据matplotlib3.4.2要求,Pillow库版本应大于等于6.2.0,pyparsing版本应大于2.2.1这里对它们进行升级。由于matplotlib同时依赖两个库,因此升级过程遇到错误1.1:ERROR: pip’s dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts即依赖冲突。解决方法:直接卸载当前的pillow和pyparsing,重新安装pip3 install pillow==6.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple pip3 install pyparsing==2.2.1 -i https://pypi.tuna.tsinghua.edu.cn/simple更新完毕后问题解决。错误2:File “/home/winter/.local/lib/python3.8/site-packages/matplotlib/backends/backend_tkagg.py”, line 1, in from . import _backend_tkFile “/home/winter/.local/lib/python3.8/site-packages/matplotlib/backends/_backend_tk.py”, line 7, in import tkinter as tkModuleNotFoundError: No module named ‘tkinter’[ERROR] evo module evo.main_ape crashed - no logfile written (disabled)解决方法:安装tkinter库sudo apt install tk-dev # 本文使用3.8版本python,因为evo要求Python版本大于3.7 sudo apt-get install python3.8-tk
1 下载安装python3.82 修改python3默认指向3 无法打开终端解决方案3 python版本的卸载4 python多版本管理5 升级pip3/pipUbuntu16.04自带python2.7与python3.5,某个项目编译却要求python版本大于等于3.7,遂考虑在原系统基础上再安装python3.8。1 下载安装python3.8Ubuntu 官方 apt 库中还未收录 python 3.8,因此添加 deadsnakes PPA 源安装python3.8,否则会出现以下错误错误1:E: 无法定位软件包 python3.8 E: 无法按照 glob ‘python3.8’ 找到任何软件包 E: 无法按照正则表达式 python3.8 找到任何软件包解决方法:运行以下指令安装python3.8sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get update sudo apt-get install python3.8此时python3版本还是指向python3.5,可以输入cd /usr/bin && ll查看2 修改python3默认指向sudo rm python3 sudo ln -s python3.8 python3查看python版本验证修改成功:$ python3 --version python3.8.93 无法打开终端解决方案按1、2步骤安装完python3后,一般无法打开终端错误2:$ gnome-terminal Traceback (most recent call last): File "/usr/bin/gnome-terminal", line 9, in <module> from gi.repository import GLib, Gio File "/usr/lib/python3/dist-packages/gi/__init__.py", line 42, in <module> from . import _gi ImportError: cannot import name '_gi'解决方法:cd /usr/lib/python3/dist-packages/gi/ # 下面的35 改成38 表示从py3.5改到py3.8 sudo mv _gi_cairo.cpython-35m-x86_64-linux-gnu.so _gi_cairo.cpython-38m-x86_64-linux-gnu.so sudo mv _gi.cpython-35m-x86_64-linux-gnu.so _gi.cpython-38m-x86_64-linux-gnu.so接下来修改sudo gedit /usr/bin/gnome-terminal将python3改为python问题解决。3 python版本的卸载sudo apt remove python3.8 sudo apt autoremove python3.84 python多版本管理参考https://blog.csdn.net/qq_39779233/article/details/106875184,值得注意,仅按该博文的方式配置仍会打不开终端,建议先按照本文方式配置后再添加管理。将 python 各版本添加到 update-alternatives$ which python3.8 /usr/bin/python3.8 $ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1 $ which python3.5 /usr/bin/python3.5 $ sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 2通过下面配置切换版本$ sudo update-alternatives --config python3 There are 2 choices for the alternative python3 (providing /usr/bin/python3). Selection Path Priority Status ------------------------------------------------------------ * 0 /usr/bin/python3.5 2 auto mode 1 /usr/bin/python3.5 2 manual mode 2 /usr/bin/python3.8 1 manual mode Press <enter> to keep the current choice[*], or type selection number: 25 升级pip3/pip对于初次安装python的Ubuntu系统而言,pip等级较低,这种情况下进行pip install高版本库可能会出现一系列的问题,例如:错误3:AttributeError: module ‘platform’ has no attribute ‘linux_distribution’解决方法:对pip版本进行升级curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py sudo python3 get-pip.py --force-reinstall
1 编译ROS_ORBSLAM2 启动ORBSLAM前文:从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(一):安装与配置从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(二):相机测试与标定1 编译ROS_ORBSLAM进入ORB_SLAM2项目根目录直接运行chmod +x ./build_ros.sh ./build_ros.sh一般情况下会遇到若干错误,例如错误1 /usr/bin/ld: CMakeFiles/RGBD.dir/src/ros_rgbd.cc.o: undefined reference to symbol ‘_ZN5boost6system15system_categoryEv’/usr/lib/x86_64-linux-gnu/libboost_system.so: error adding symbols: DSO missing from command linecollect2: error: ld returned 1 exit status「解决方法」在ORBSLAM2/Examples/ROS/ORBSLAM2下的Cmakelists.txt中添加一行 set(LIBS ${OpenCV_LIBS} ${EIGEN3_LIBS} ${Pangolin_LIBRARIES} ${PROJECT_SOURCE_DIR}/../../../Thirdparty/DBoW2/lib/libDBoW2.so ${PROJECT_SOURCE_DIR}/../../../Thirdparty/g2o/lib/libg2o.so ${PROJECT_SOURCE_DIR}/../../../lib/libORB_SLAM2.so -lboost_system # 添加这行 )2 启动ORBSLAM# 终端A运行ROS roscore # 终端B启动相机 roslaunch usb_cam usb_cam-test.launch # 终端C启动ORBSLAM rosrun ORB_SLAM2 Mono /home/winter/Project/SLAM/ORBSLAM2/ORB_SLAM2/Vocabulary/ORBvoc.txt /home/winter/Project/SLAM/ORBSLAM2/ORB_SLAM2/Examples/ROS/ORB_SLAM2/my_cam.yaml注意:my_cam.yaml用从零开始Ubuntu16.04+ORBSLAM2+ROS实验实录(二):相机测试与标定的标定文件,否则可能出现始终无法初始化帧(TRYING INITIALIZATION)的情况。这里记录几个常见的也是本文遇到的错误错误2:OpenCV Error: Bad argument (Invalid pointer to file storage) in cvGetFileNodeByName, file /tmp/binarydeb/ros-kinetic-opencv3-3.3.1/modules/core/src/persistence.cpp, line 861terminate called after throwing an instance of ‘cv::Exception’what(): /tmp/binarydeb/ros-kinetic-opencv3-3.3.1/modules/core/src/persistence.cpp:861: error: (-5) Invalid pointer to file storage in function cvGetFileNodeByName已放弃 (核心已转储)错误分析:很显然,该错误由Opencv引起,叙述为“严重冲突”,查阅资料得知是opencv的版本问题,ros自带的opencv版本为3.3.1winter@Winter:~$ find /opt/ros/ -name opencv /opt/ros/kinetic/include/opencv-3.3.1-dev/opencv与编译ORBSLAM工程时使用的openCV版本不同,例如本文使用的是opencv2.4.11,产生版本冲突,具体地,体现在编译时的警告信息中:Linking CXX executable ../Mono /usr/bin/ld: warning: libopencv_core.so.2.4, needed by ../../../../lib/libORB_SLAM2.so, may conflict with libopencv_core.so.3.3 [100%] Built target Mono Linking CXX executable ../RGBD Linking CXX executable ../Stereo /usr/bin/ld: warning: libopencv_core.so.2.4, needed by ../../../../lib/libORB_SLAM2.so, may conflict with libopencv_core.so.3.3解决方法简言之就是:更改链接库为3.3.1,重新编译工程。(1) 删除ORB_SLAM2、DBoW2以及ROS/ORB_SLAM2/src下的build文件(2) 更改ORB_SLAM2、DBoW2以及ROS/ORB_SLAM2/src下的CMakeList文件set(OpenCV_DIR "/usr/local/include/opencv331/share/OpenCV") find_package(OpenCV 3.3.1 QUIET) if(NOT OpenCV_FOUND) find_package(OpenCV 2.4.3 QUIET) if(NOT OpenCV_FOUND) message(FATAL_ERROR "OpenCV > 2.4.3 not found.") endif() endif()
1 Wins+Ubuntu双系统安装2 配置ORBSLAM22.1 工具配置2.2 安装Pangolin库2.3 安装Opencv2.4.112.4 安装Eigen32.5 安装ORBSLAM23 安装与配置ROS4 错误记录与解决方案1 Wins+Ubuntu双系统安装对于ORBSLAM这种对硬件要求较高的大型计算机视觉实验,不推荐在虚拟机上实现,建议安装双系统以集成对显卡、摄像头更好的支持,由于本文关注ORBSLAM,因此关于双系统详细的图文安装步骤请移步参考Linux不会装?Win10+Ubuntu双系统安装、配置、卸载保姆级图文避雷指南。2 配置ORBSLAM22.1 工具配置 sudo apt-get install cmake #安装CMake sudo apt-get install git #安装Git sudo apt-get install gcc g++ #安装g++2.2 安装Pangolin库首先安装库依赖文件sudo apt-get install libglew-dev #安装Glew sudo apt-get install libboost-dev libboost-thread-dev libboost-filesystem-dev #安装Boost sudo apt-get install libpython2.7-dev接着下载、编译、安装Pangolin,为方便第三方库管理,在ORBSLAM2根目录下创建3rd/Pangolin/srcmkdir -p ~/Project/SLAM/ORBSLAM2/3rd/Pangolin/src cd ~/Project/SLAM/ORBSLAM2/3rd/Pangolin/src git clone https://github.com/stevenlovegrove/Pangolin.git mkdir build cd build cmake -DCPP11_NO_BOOST=1 .. # 这一步用虚拟机可能会卡死,需要根据自身硬件设备调整线程数 make -j4 sudo make install # 关键,用于安装2.3 安装Opencv2.4.11mkdir -p ~/Project/SLAM/ORBSLAM2/3rd/Opencv2/src cd ~/Project/SLAM/ORBSLAM2/3rd/Opencv2/src从OpenCV官网下载OpenCV2.4.11然后解压到上述目录。接着安装依赖sudo apt-get install build-essential sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev编译安装mkdir build cd build sudo cmake .. -DCMAKE_BUILD_TYPE=Release -DCUDA_nppi_LIBRARY=true -DWITH_CUDA=OFF -DBUILD_TIFF=ON -DCMAKE_INSTALL_PREFIX=/usr/local/include/opencv2411 make sudo make install2.4 安装Eigen3安装 sudo apt-get install libeigen3-devEigen头文件的默认位置在usr/include/eigen3 中。2.5 安装ORBSLAM2ORBSLAM2依赖库g2o和DBoW2在这一步编译完成cd ~/Project/SLAM/ORBSLAM2 git clone https://github.com/raulmur/ORB_SLAM2.git ORB_SLAM2 # 编译 chmod +x build.sh ./build.sh到这里为止ORBSLAM项目编译完成,可以下载数据集进行测试,这里推荐几个数据集网站:1.ORBSLAM2数据集序列(TUM)2.ORBSLAM2数据集序列(EUROC)3.ORBSLAM2数据集序列(KITTI)解压在项目根目录的dataSet文件里,进入ORBSLAM2根目录进行测试TUM数据集./Examples/Monocular/mono_tum Vocabulary/ORBvoc.txt Examples/Monocular/TUM1.yaml ~/Project/SLAM/ORBSLAM2/dataSet/rgbd_dataset_freiburg1_xyz其中标签TUM1.yaml可以替换成其他,~/Project/Slam/ORBSLAM2/dataSet/rgbd_dataset_freiburg1_xyz是数据集序列所在路径,rgbd_dataset_freiburg1_xy是序列名称。EuROC数据集./Examples/Monocular/mono_euroc ./Vocabulary/ORBvoc.txt ./Examples/Monocular/EuRoC.yaml ~/Project/SLAM/ORBSLAM2/dataSet/V2_01_easy/mav0/cam0/data ./Examples/Monocular/EuRoC_TimeStamps/V201.txt 如果遇到加载动态库错误ORB_SLAM2 libORB_SLAM2.so: cannot open shared object file: No such file or directory执行下面语句sudo cp lib/libORB_SLAM2.so /usr/lib sudo cp Thirdparty/g2o/lib/libg2o.so /usr/lib sudo cp Thirdparty/DBoW2/lib/libDBoW2.so /usr/lib3 安装与配置ROSROS的安装与配置请参考ROS从入门到精通(一) ROS简介、安装与常见问题,其中详细地给出了ROS安装的步骤,以及安装过程中可能遇到的问题ROS从入门到精通(二) VSCode 搭建 ROS 工程环境,其中详细介绍了如何将ROS集成到VSCode中4 错误记录与解决方案错误1:编译执行 ./build_ros.sh 时出现CMakeLists.txt:4 (rosbuild_init)– Configuring incomplete, errors occurred!make: *** 没有指明目标并且找不到 makefile停止。解决方案:此为ROS配置错误,需要修改sudo gedit ~/.bashrc # 增加 export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:~/Project/SLAM/ORBSLAM2/ORB_SLAM2/Examples/ROS source ~/.bashrc错误2:error reading version line解决方案:一般由rosbag包错误导致,重新下载ros_bag文件。错误3:[FATAL] [1630244733.986587231]: Error opening file: rgbd_dataset_freiburg1_xyz.bag解决方案:没有启动ros导致,在某个终端启动roscore即可解决。
目录1 导论2 对极约束推导1 导论透视几何的缺陷是图像深度信息的丢失,如图1所示,根据相似变换关系,视线上的若干平面都映射为成像面上的一个平面。对极几何是两个透视几何模型间的几何约束关系,主要用于实现基于三角测量的双目立体视觉、深度估计等,对极几何约束只能实现点到线的映射,因此约束条件弱于透视几何。图2展示了对极几何的基本概念,其中3D世界的真实点称为物点 X X X;物点在相机成像面上形成像点 x L x_L x L 、 x R x_R x R ;两台透视相机间光心的连线称为基线;基线与成像面的交点称为极点 e L e_L e L 、 e R e_R e R ,若图像中不存在极点,则说明两个摄像机不能拍摄到彼此;像点与极点的连线称为极线,所有极线相交于极点;透视相机光心与物点确定的平面称为极平面,对于不同物点,极平面绕基线旋转,极线绕极点矩形,如图3所示。2 对极约束推导下面讨论两个透视模型间的关系。不妨以左侧相机为参考,设物点 L X = [ X Y Z ] T ^{\boldsymbol{L}}\!X=\left[XYZXYZ\right] ^T L X=[ X Y Z ] T ,左右两相机间的变换关系为L R T = [ R t 0 1 ] _{\boldsymbol{L}}^{\boldsymbol{R}}\boldsymbol{T}=\left[R0t1Rt01\right]LR T=[ R0 t1 ]则物点在右透视相机坐标系里的3D坐标为R X = L R T L X = R [ X Y Z ] + t = [ X ′ Y ′ Z ′ ] ^{\boldsymbol{R}}\!\boldsymbol{X}=_{\boldsymbol{L}}^{\boldsymbol{R}}\boldsymbol{T}^{\boldsymbol{L}}\!\boldsymbol{X}=\boldsymbol{R}\left[XYZXYZ\right] +\boldsymbol{t}=\left[X′Y′Z′X′Y′Z′\right]R X= LR T L X=R ⎣⎡ XYZ ⎦⎤ +t= ⎣⎡ X ′Y ′Z ′ ⎦⎤事实上,向量 t t t与成像面的交点即为极点。根据相似关系,物点在左、右成像面上几何坐标为{ x L = f L X Z y L = f L Y Z z L = f L ⇒ L x = f L Z L X { x R = f R X ′ Z ′ y R = f R Y ′ Z ′ z R = f R ⇒ R x = f R Z ′ R X⎧⎩⎨⎪⎪xL=fLXZyL=fLYZzL=fL{xL=fLXZyL=fLYZzL=fL\Rightarrow ^{\boldsymbol{L}}\!\boldsymbol{x}=\frac{f_L}{Z}\,\,^{\boldsymbol{L}}\!\boldsymbol{X}\,\,⎧⎩⎨⎪⎪⎪⎪xR=fRX′Z′yR=fRY′Z′zR=fR{xR=fRX′Z′yR=fRY′Z′zR=fR\Rightarrow ^{\boldsymbol{R}}\!\boldsymbol{x}=\frac{f_R}{Z'}\,\,^{\boldsymbol{R}}\!\boldsymbol{X}⎩⎪⎨⎪⎧ x L =f L ZXy L =f L ZYz L =f L ⇒ L x= Zf L L X ⎩⎪⎨⎪⎧ x R =f R Z ′X ′y R =f R Z ′Y ′z R =f R ⇒ R x= Z ′f R R X结合齐次变换关系,有R x = f R Z ′ ( R L X + t ) = f R Z ′ ( R Z f L ⋅ L x + t ) = α R L x + β t ^{\boldsymbol{R}}\!\boldsymbol{x}=\frac{f_R}{Z'}\left( \boldsymbol{R}^{\boldsymbol{L}}\!\boldsymbol{X}+\boldsymbol{t} \right) \\=\frac{f_R}{Z'}\left( \boldsymbol{R}\frac{Z}{f_L}\cdot ^{\boldsymbol{L}}\!\boldsymbol{x}+\boldsymbol{t} \right) \\=\alpha \boldsymbol{R}^{\boldsymbol{L}}\!\boldsymbol{x}+\beta \boldsymbol{t}\,\,R x= Z ′f R (R L X+t)= Z ′f R (R f LZ ⋅ L x+t)=αR L x+βt其中 α \alpha α、 β \beta β为两个与尺度有关的常数。将等式两边同时与向量 t t t做外积,有R x × t = ( α R L x + β t ) × t ^{\boldsymbol{R}}\!\boldsymbol{x}\times \boldsymbol{t}=\left( \alpha \boldsymbol{R}^{\boldsymbol{L}}\!\boldsymbol{x}+\beta \boldsymbol{t} \right) \times \boldsymbol{t}R x×t=(αR L x+βt)×t线性化为t ∧ R x = α t ∧ R L x {\boldsymbol{t}^{\land}}^{\boldsymbol{R}}\!\boldsymbol{x}=\alpha \boldsymbol{t}^{\land}\boldsymbol{R}^{\boldsymbol{L}}\!\boldsymbol{x}t ∧ R x=αt ∧ R L x两边同乘 R x T ^{\boldsymbol{R}}\!\boldsymbol{x}^T R x T ,考虑到 R x T ^{\boldsymbol{R}}\!\boldsymbol{x}^T R x T 与向量 R x × t ^{\boldsymbol{R}}\!\boldsymbol{x}\times \boldsymbol{t} R x×t垂直,故α R x T t ∧ R L x = 0 \alpha ^{\boldsymbol{R}}\!\boldsymbol{x}^T\boldsymbol{t}^{\land}\boldsymbol{R}^{\boldsymbol{L}}\!\boldsymbol{x}=0α R x T t ∧ R L x=0消去常数 α \alpha α,即得R x T E L x = 0 {^{\boldsymbol{R}}\!\boldsymbol{x}^T\boldsymbol{E}^{\boldsymbol{L}}\!\boldsymbol{x}=0}R x T E L x=0其中 E = t ∧ R \boldsymbol{E}=\boldsymbol{t}^{\land}\boldsymbol{R} E=t ∧ R称为本征矩阵(Essential Matrix),表征了同一物点在两个透视相机成像面上像点的几何约束关系。下面引入相机内参矩阵,将像点映射到像素平面{ L u = K L L x R u = K R R x{Lu=KLLxRu=KRRx{Lu=KLLxRu=KRRx{ L u=K L L xR u=K R R x代入上式即得R u T F L u = 0 { ^{\boldsymbol{R}}\!\boldsymbol{u}^T\boldsymbol{F}^{\boldsymbol{L}}\!\boldsymbol{u}=0}R u T F L u=0其中 F = K R − T E K L − 1 \boldsymbol{F}=\boldsymbol{K}_{R}^{-T}\boldsymbol{EK}_{L}^{-1} F=K R−T EK L−1 称为基本矩阵(Fundamental Matrix),表征了同一物点在两个透视相机像素面上像素点间的几何约束关系。本征矩阵与基本矩阵表征了两个透视模型对极几何的代数特征,以上二式共同构成对极约束(Epipolar Constraint)。🚀 计算机视觉基础教程说明🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
在复杂工程中,一个项目的运行可能需要众多依赖库的支持,例如ORBSLAM需要g2o、Opencv等。此时一个良好的环境依赖部署方法对工程的条理性至关重要,下面详细阐述1 构造依赖目录|--3rd(ThirdParty) // 依赖库根目录 |--LibraryA // 依赖库A |--include // 头文件目录 |--lib // 存放动态库或静态库 |--LibraryB // 依赖库B |--include // 头文件目录 |--lib // 存放动态库或静态库 ...2 构造属性表VS中点击“属性管理器”,在对应的编译模式Release x64 Release x32 Debug x64 Debug x32右键项目,新建属性表,双击属性表进行配置。如图所示,主要配置3个位置1、VC++目录下的“包含目录”和“库目录”,分别对应上述某依赖库的include和lib文件夹 2、链接器输入下的“附加依赖项”,对应lib文件夹中的静态链接库注意:推荐使用相对路径便于移植。使用属性表也是为了便于环境移植。3 配置环境变量上述只配置了静态链接库和头文件,部分工程还会使用动态链接库。dll文件的配置无法通过属性表,只能通过项目->属性->调试->环境实现dll的配置差异。在VS中添加环境变量可以避免设置系统环境变量,使PC工作环境不至于过于冗杂。程序运行需要加载dll时,首先它会在自身周围,也即当前目录下寻找,找不到时再去更大的环境中找。如果不设置vs环境,那么程序就会直接去系统环境中寻找dll,相反,如果设置了vs环境,系统环境就会被忽略,无论vs环境下能否找得到。4 对比项目差异可以通过例程属性管理器相应属性页中的依赖库,来辨析自身工程缺少哪些库。在缺少库的时候,会出现典型的LINK2001 无法解析的外部符号 错误,在缺少头文件时,则会出现无法打开xxx.h的错误,根据这两种错误形式可以筛查项目缺少哪些依赖。
目录1 灭点(Vanishing Point)2 灭线(Vanishing Line)3 实例分析1 灭点(Vanishing Point)考虑三维空间中的一条直线{ x ( t ) = x 0 + a t y ( t ) = y 0 + b t z ( t ) = z 0 + c t⎧⎩⎨x(t)=x0+aty(t)=y0+btz(t)=z0+ct{x(t)=x0+aty(t)=y0+btz(t)=z0+ct⎩⎪⎨⎪⎧ x(t)=x 0 +aty(t)=y 0 +btz(t)=z 0 +ct其中 ( x 0 , y 0 , z 0 ) \left( x_0, y_0, z_0 \right) (x 0 ,y 0 ,z 0 )为直线上一点, t t t为参数。根据相似三角形原理,有{ z ( t ) f = x ( t ) u z ( t ) f = y ( t ) v ⇒ { u = f ( x 0 + a t ) z 0 + c t v = f ( y 0 + b t ) z 0 + c t⎧⎩⎨z(t)f=x(t)uz(t)f=y(t)v{z(t)f=x(t)uz(t)f=y(t)v\Rightarrow⎧⎩⎨u=f(x0+at)z0+ctv=f(y0+bt)z0+ct{u=f(x0+at)z0+ctv=f(y0+bt)z0+ct{ fz(t) = ux(t)fz(t) = vy(t) ⇒{ u= z 0 +ctf(x 0 +at)v= z 0 +ctf(y 0 +bt)其中 f f f为相机焦距。令 t → ∞ t\rightarrow \infty t→∞,则{ u = f a c v = f b c {{u=facv=fbc{u=facv=fbc}{ u= cfav= cfb当 c ≠ 0 c\ne 0 c =0时,即空间直线与成像平面不平行时,该直线在成像平面上的投影为收敛于灭点(Vanishing Point)的线段,如图所示;当 c = 0 c=0 c=0时,则为正交投影,直线与成像面不存在透视关系,亦不存在灭点。从灭点方程可见,灭点只取决于直线的方向,而与直线上具体的点无关,因此空间中不平行于成像平面的平行线将相交于同一个灭点。此外,相机中心与灭点连成的直线平行于原直线。2 灭线(Vanishing Line)将空间直线推广到空间平面可得类似的结论:空间平面与成像平面不平行时,该平面在成像平面上的投影为收敛于灭线(Vanishing Line)的区域,相互平行的空间平面在成像空间收敛于同一条灭线。灭线是该空间平面上所有空间直线灭点的集合。空间水平面上的垂直结构在成像面上的投影可以提供相机或场景的几何信息,例如相机的姿态模式、场景物体高度推测等,列如表所示。3 实例分析下面给出几个实例。如图所示,由于垂直结构在垂直方向上不存在灭点,且灭线穿过像素平面中心,因此根据表,此时相机成像平面不存在偏角(与水平面正交)。此外,图也说明了平行平面收敛于同一条灭线(海、天、木地板),平行直线相交于同一个灭点。图2.4.3给出了基于透视几何高度估计的实例。首先将图2.4.2(a)所示的源图片进行畸变修正,接着按图2.4.2(b)所示进行灭点、灭线估计。根据平行直线相交于同一灭点的几何关系,找出目标物对应的灭点,以及与该灭点对应的参考物,如图2.4.2中的目标物——人,以及参考物——柜子。通过参考物的几何信息,按比例映射到目标物,完成高度估计。此外,由于成像平面中垂直方向有灭点,亦能推测此时相机姿态模式——朝下。🚀 计算机视觉基础教程说明🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录1 导论2 基本直接线性变换(Basic DLT)3 归一化直接线性变换(Normalized DLT)4 鲁棒单应性估计(Robust Homography Estimation)1 导论计算机视觉系列教程1-1:透视空间与透视变换中提到,透视空间所有变换都是投影变换的特例,本节进一步研究投影变换矩阵(单应性矩阵)的估计。透视变换的核心是单应性矩阵 H H H或单应性向量 h h h。H = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ⇔ h = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] T H=\left[h11h21h31h12h22h32h13h23h33h11h12h13h21h22h23h31h32h33\right] \Leftrightarrow h=\left[h11h12h13h21h22h23h31h32h33h11h12h13h21h22h23h31h32h33\right] ^TH= ⎣⎡ h 11h 21h 31 h 12h 22h 32 h 13h 23h 33 ⎦⎤ ⇔h=[ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] T设 p s r c = [ x y 1 ] T p_{src}=\left[xy1xy1\right] ^T p src =[ x y 1 ] T 与 p d s t = [ x ′ y ′ 1 ] T p_{dst}=\left[x′y′1x′y′1\right] ^T p dst =[ x ′ y ′ 1 ] T 是二维透视空间 P 2 \mathbb{P}^2 P 2 中,一次透视变换前后的对应点,因此其满足p d s t = H p s r c ⟺ [ x ′ y ′ 1 ] = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] [ x y 1 ] p_{dst}=Hp_{src}\Longleftrightarrow \left[x′y′1x′y′1\right] =\left[h11h21h31h12h22h32h13h23h33h11h12h13h21h22h23h31h32h33\right] \left[xy1xy1\right]p dst =Hp src ⟺ ⎣⎡ x ′y ′1 ⎦⎤ = ⎣⎡ h 11h 21h 31 h 12h 22h 32 h 13h 23h 33 ⎦⎤ ⎣⎡ xy1 ⎦⎤若将单应性矩阵进行尺度缩放后作用于 p s r c p_{src} p src ,则k H p s r c = k [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] [ x y 1 ] = k p d s t kHp_{src}=k\left[h11h21h31h12h22h32h13h23h33h11h12h13h21h22h23h31h32h33\right] \left[xy1xy1\right] =kp_{dst}kHp src =k ⎣⎡ h 11h 21h 31 h 12h 22h 32 h 13h 23h 33 ⎦⎤ ⎣⎡ xy1 ⎦⎤ =kp dst而透视空间中, k p d s t kp_{dst} kp dst 与 p d s t p_{dst} p dst 实际上对应同一点,因此 k H kH kH与 H H H相当于同一次透视变换,故单应性矩阵 H H H仅有8个自由度,通常通过设置 h 33 = 1 h_{33}=1 h 33 =1或 ∥ h ∥ 2 2 = 1 \lVert h \rVert _{2}^{2}=1 ∥h∥ 22 =1来约束冗余的参数。下面详细阐述单应性矩阵估计方法。2 基本直接线性变换(Basic DLT)将上式改写为齐次形式[ 0 0 0 − x − y − 1 y ′ x y ′ y y ′ x y 1 0 0 0 − x ′ x − x ′ y − x ′ − y ′ x − y ′ y − y ′ x ′ x x ′ y x ′ 0 0 0 ] [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] = [ 0 0 0 ] \left[0x−y′x0y−y′y01−y′−x0x′x−y0x′y−10x′y′x−x′x0y′y−x′y0y′−x′0000−x−y−1y′xy′yy′xy1000−x′x−x′y−x′−y′x−y′y−y′x′xx′yx′000\right] \left[h11h12h13h21h22h23h31h32h33h11h12h13h21h22h23h31h32h33\right] =\left[000000\right]⎣⎡ 0x−y ′ x 0y−y ′ y 01−y ′ −x0x ′ x −y0x ′ y −10x ′ y ′ x−x ′ x0 y ′ y−x ′ y0 y ′−x ′0 ⎦⎤ ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡ h 11h 12h 13h 21h 22h 23h 31h 32h 33 ⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤ = ⎣⎡ 000 ⎦⎤其中系数矩阵的秩为2,因此一对变换点仅能确定2个自由度。因此需要无三点共线的四对变换点才能确定单应性矩阵 H H H。[ 0 0 0 − x 1 − y 1 − 1 y 1 ′ x 1 y 1 ′ y 1 y 1 ′ x 1 y 1 1 0 0 0 − x 1 ′ x 1 − x 1 ′ y 1 − x 1 ′ 0 0 0 − x 2 − y 2 − 1 y 2 ′ x 2 y 2 ′ y 2 y 2 ′ x 2 y 2 1 0 0 0 − x 2 ′ x 2 − x 2 ′ y 2 − x 2 ′ ⋮ ] [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] = [ 0 0 0 ] ⇔ A h = 0 \left[0x10x20y10y20101−x10−x20−y10−y20⋮−10−10y′1x1−x′1x1y′2x2−x′2x2y′1y1−x′1y1y′2y2−x′2y2y′1−x′1y′2−x′2000−x1−y1−1y1′x1y1′y1y1′x1y11000−x1′x1−x1′y1−x1′000−x2−y2−1y2′x2y2′y2y2′x2y21000−x2′x2−x2′y2−x2′⋮\right] \left[h11h12h13h21h22h23h31h32h33h11h12h13h21h22h23h31h32h33\right] =\left[000000\right] \Leftrightarrow {Ah=0}⎣⎢⎢⎢⎢⎢⎡ 0x 10x 2 0y 10y 2 0101 −x 10−x 20 −y 10−y 20⋮ −10−10 y 1′ x 1−x 1′ x 1y 2′ x 2−x 2′ x 2 y 1′ y 1−x 1′ y 1y 2′ y 2−x 2′ y 2 y 1′−x 1′y 2′−x 2′ ⎦⎥⎥⎥⎥⎥⎤ ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡ h 11h 12h 13h 21h 22h 23h 31h 32h 33 ⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤ = ⎣⎡ 000 ⎦⎤ ⇔Ah=0对于形如 A x = b Ax=b Ax=b的非齐次线性方程可以通过伪逆形式计算 x x x;对于形如 A x = 0 Ax=0 Ax=0的齐次线性方程的解则对应矩阵 A A A右奇异向量 v p v_p v p ,其中 v p v_p v p 对应的奇异值 σ p ≈ 0 \sigma _p\approx 0 σ p ≈0或不对应奇异值。3 归一化直接线性变换(Normalized DLT)基本DLT估计算法的缺陷是:(1) 单应性估计不具有相似不变性假设在第一次估计下有 x d s t = H x s r c x_{dst}=Hx_{src} x dst =Hx src 。现对两张图像分别进行相似变换并重新进行单应性估计,得到 ( T d s t x d s t ) = H ′ ( T s r c x s r c ) \left( T_{dst}x_{dst} \right) =H'\left( T_{src}x_{src} \right) (T dst x dst )=H ′ (T src x src ),改写为 x d s t = ( T d s t − 1 H ′ T s r c ) x s r c x_{dst}=\left( T_{dst}^{-1}H'T_{src} \right) x_{src} x dst =(T dst−1 H ′ T src )x src ,大部分情况下 H ≠ T d s t − 1 H ′ T s r c H\ne T_{dst}^{-1}H'T_{src} H =T dst−1 H ′ T src ,这表明基本DLT算法无法抵抗相似变换的干扰。(2) 估计的单应性矩阵容易产生病态条件,鲁棒性差由于默认透视空间的尺度变换因子 w = 1 w=1 w=1,所以齐次坐标下很可能产生分量幅度差异大的情况,例如某特征点 X = [ 100 101 1 ] T X=\left[10010111001011\right] ^T X=[ 100 101 1 ] T 。在这种情况下估计出的单应性矩阵,各个元素数值数量级可能会相差 1 0 4 10^4 10 4 以上,导致病态条件——特征点的轻微变化都会造成单应性矩阵的剧变。基于(1)(2)两种缺陷,需要将基本DLT算法进行优化,优化的核心就是特征点坐标的归一化。设原图像特征点集合为 ,目标图像特征点集合为 ,则具体的算法为:① 将特征点集合 X s r c X_{src} X src 、 X d s t X_{dst} X dst 归一化使用相似变换矩阵 T s r c T_{src} T src 、 T d s t T_{dst} T dst 将特征点集合中心移至原点,且与原点平均距离为 2 \sqrt{2} 2 。由于默认尺度因子为 w = 1 w=1 w=1,所以归一化到 2 \sqrt{2} 2 可以保持齐次坐标的三个分量有相同的幅度,例如 X = [ 100 100 1 ] T ⇒ X n o r m a l = [ 1 1 1 ] T X=\left[10010011001001\right] ^T\Rightarrow X^{normal}=\left[111111\right] ^T X=[ 100 100 1 ] T ⇒X normal =[ 1 1 1 ] T 。这里给出一种相似变换矩阵的计算方式,设X n o r m a l = T X ⇔ [ x ~ i y ~ i 1 ] = [ s t x s t y 1 ] [ x i y i 1 ] X^{normal}=TX\Leftrightarrow \left[x~iy~i1x~iy~i1\right] =\left[sstxty1stxsty1\right] \left[xiyi1xiyi1\right]X normal =TX⇔ ⎣⎡ x~ iy~ i1 ⎦⎤ = ⎣⎡ s s t xt y1 ⎦⎤ ⎣⎡ x iy i1 ⎦⎤令{ 1 N ∑ i = 1 N x ~ i = 1 N ∑ i = 1 N ( s x i + t x ) = 0 1 N ∑ i = 1 N y ~ i = 1 N ∑ i = 1 N ( s y i + t y ) = 0 1 N ∑ i = 1 N x ~ i 2 + y ~ i 2 = 2⎧⎩⎨⎪⎪⎪⎪⎪⎪1N∑Ni=1x~i=1N∑Ni=1(sxi+tx)=01N∑Ni=1y~i=1N∑Ni=1(syi+ty)=01N∑Ni=1x~2i+y~2i−−−−−−√=2–√{1N∑i=1Nx~i=1N∑i=1N(sxi+tx)=01N∑i=1Ny~i=1N∑i=1N(syi+ty)=01N∑i=1Nx~i2+y~i2=2⎩⎪⎨⎪⎧ N1 ∑ i=1N x~ i = N1 ∑ i=1N (sx i +t x )=0N1 ∑ i=1N y~ i = N1 ∑ i=1N (sy i +t y )=0N1 ∑ i=1N x~ i2 + y~ i2 = 2解得{ t x = − s 1 N ∑ i = 1 N x i = − s x ˉ t y = − s 1 N ∑ i = 1 N y i = − s y ˉ s = 2 1 N ∑ i = 1 N x ~ i 2 + y ~ i 2 = 2 1 N ∑ i = 1 N ( x i − x ˉ ) 2 + ( y i − y ˉ ) 2⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪tx=−s1N∑Ni=1xi=−sx¯ty=−s1N∑Ni=1yi=−sy¯s=2√1N∑Ni=1x~2i+y~2i√=2√1N∑Ni=1(xi−x¯)2+(yi−y¯)2√{tx=−s1N∑i=1Nxi=−sx¯ty=−s1N∑i=1Nyi=−sy¯s=21N∑i=1Nx~i2+y~i2=21N∑i=1N(xi−x¯)2+(yi−y¯)2⎩⎪⎪⎨⎪⎪⎧ t x =−s N1 ∑ i=1N x i =−s xˉt y =−s N1 ∑ i=1N y i =−s yˉs= N1 ∑ i=1N x~ i2 + y~ i22 = N1 ∑ i=1N (x i − xˉ ) 2 +(y i − yˉ ) 22② 运用基本DLT算法由 与 估计单应性矩阵③ 解归一化,映射回实际图像4 鲁棒单应性估计(Robust Homography Estimation)结合基本DLT算法、归一化DLT算法及计算机视觉系列教程6-2:图像匹配算法概述.的RANSAC算法进行单应性矩阵估计,具体流程如下:(1) 设置迭代次数 K = ∞ K=\infty K=∞,内点集 S i n = ⊘ S_{in}=\oslash S in =⊘,模型参数 H = H 0 H=H_0 H=H 0 ;(2) 随机从样本数据集 S S S中选取4对特征点,并通过基本DLT算法确定测试模型 H t e s t H_{test} H test ;(3) 用 H t e s t H_{test} H test 遍历样本数据集 S S S,估计误差 ε \varepsilon ε在距离阈值 t t t内的点加入内点集 S i n S_{in} S in 。其中阈值 t = 5.99 σ t=\sqrt{5.99}\sigma t= 5.99 σ, σ \sigma σ为估计不确定度,估计误差 ε \varepsilon ε主要有两种度量方式:① 代数误差 ε i = ∥ A i h t e s t ∥ \varepsilon _i=\left\| A_ih_{test} \right\| ε i =∥A i h test ∥,其中A i = [ 0 0 0 − x − y − 1 y ′ x y ′ y y ′ − y ′ x − y ′ y − y ′ x ′ x x ′ y x ′ 0 0 0 ] A_i=\left[0−y′x0−y′y0−y′−xx′x−yx′y−1x′y′x0y′y0y′0000−x−y−1y′xy′yy′−y′x−y′y−y′x′xx′yx′000\right]A i =[ 0−y ′ x 0−y ′ y 0−y ′ −xx ′ x −yx ′ y −1x ′ y ′ x0 y ′ y0 y ′0 ]② 几何误差(二次投影误差) ε i = ∥ H X s r c , i , X d s t , i ∥ 2 2 + ∥ X s r c , i , H − 1 X d s t , i ∥ 2 2 \varepsilon _i=\left\| HX_{src,i}, X_{dst, i} \right\| _{2}^{2}+\left\| X_{src,i}, H^{-1}X_{dst, i} \right\| _{2}^{2} ε i =∥HX src,i ,X dst,i ∥ 22 + ∥∥ X src,i ,H −1 X dst,i ∥∥ 22 ,可以视作交叉检验。(4) 若 S i n S_{in} S in 的大小小于阈值 T T T,则放弃该模型,重复(2);若 S i n S_{in} S in 的大小大于阈值 t t t,则通过归一化DLT算法或Levenberg Marquardt等迭代优化算法,利用 S i n S_{in} S in 中的所有点重新估计模型 H t e s t ∗ H_{test}^{*} H test∗ ;(5) 计算当前模型 H t e s t ∗ H_{test}^{*} H test∗ 下的内点率 ω = ∣ S i n ∣ ∣ S ∣ \omega =\frac{|S_{in}|}{|S|} ω= ∣S∣∣S in ∣ ,根据 K = ln ( 1 − p ) ln ( 1 − ω n ) K=\frac{\ln \left( 1-p \right)}{\ln \left( 1-\omega ^n \right)} K= ln(1−ω n )ln(1−p) 更新迭代次数;(6) 至此完成一次迭代,若 H t e s t ∗ H_{test}^{*} H test∗ 下内点率为最大,则更新 H = H t e s t ∗ H=H_{test}^{*} H=H test∗ ,重复(2) ~ (5)直至迭代次数满足要求。🚀 计算机视觉基础教程说明🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
目录0 写在前面1 透视空间2 透视变换0 写在前面如图所示是生活中常见的透视现象,其物理本质是光的直线传播。透视也可描述为“近大远小”,如图(a)所示,其对应透视空间;若物体大小不随观察点的远近改变,如图(b)所示,则其对应欧式空间。由于人眼与相机在捕获图像时均存在透视现象,因此计算机几何基于透视空间进行研究。1 透视空间为直观起见,先描述二维透视空间,其原理可直接推广至三维空间。如下左图是一个二维欧式平面 R 2 \mathbb{R}^2 R 2 ,现在引入二维透视坐标 P 2 \mathbb{P}^2 P 2 ,透视坐标将欧式空间的维度扩展到三维,第三维度 w ~ \tilde{w} w~ 表示物体与观察点的距离,约定以 w = 1 w=1 w=1为参考平面。根据光沿直线传播的原理,从透视坐标系原点引出一条视线 l ~ \boldsymbol{\tilde{l}} l~ 穿过欧式平面的点 A A A。不妨上下平移欧式平面,调整点 A A A与观察点的距离。欧式空间中的不同点 A A A、 A ′ A' A ′ 在透视空间中是相同的,因为 l ~ \boldsymbol{\tilde{l}} l~ 是同一个点在不同观察距离下的表现集合,透视空间中用直线(视线) l ~ \boldsymbol{\tilde{l}} l~ 表示欧式空间的点。现在保持欧式空间中的点相同,再次调整观察距离,如下图所示。观察距离越远视线越发散、信息越局部;观察距离越近视线越收敛,信息越全局——此现象可以用投影仪工作场景来说明。定义透视空间的视线l ~ = ( x , y , w ) \boldsymbol{\tilde{l}}=\left( x,y,w \right)l~ =(x,y,w)当 w ≠ 0 w\ne 0 w =0时, l ~ \boldsymbol{\tilde{l}} l~ 对应欧式空间中的点 L ( x w , y w ) L\left( \frac{x}{w},\frac{y}{w} \right) L( wx , wy ), l ~ \boldsymbol{\tilde{l}} l~ 也称为点 L L L的齐次坐标(Homogeneous Coordinates),当 w = 0 w=0 w=0时, l ~ \boldsymbol{\tilde{l}} l~ 对应欧式空间中的向量 L ⃗ = ( x , y ) \vec{L}=\left( x,y \right) L =(x,y), w = 0 w=0 w=0也体现了其在齐次变换中的平移不变性透视空间中点与向量的运算{ V ± V = V P ± V = P P − P = V P + P = M i d P ( P : P o i n t V : V e c t o r M i d : 中点 )⎧⎩⎨⎪⎪⎪⎪V±V=VP±V=PP−P=VP+P=MidP{V±V=VP±V=PP−P=VP+P=MidP\left( P:Point\,\, V:Vector\,\, Mid:\text{中点} \right)⎩⎪⎪⎪⎨⎪⎪⎪⎧ V±V=VP±V=PP−P=VP+P=MidP (P:PointV:VectorMid:中点)齐次坐标具有如下性质:同质性。齐次坐标的几何本质,透视空间中齐次坐标是同一点在不同观察距离下的表现形式线性。齐次坐标下,可将对应维度欧式空间的变换线性化。例如二维欧式平面中点的平移属于非线性变换,但二维透视空间中可通过线性旋转完成欧式空间中非线性的平移,如下图。与用透视空间的直线表示欧式空间的点类似,透视空间中用视平面表示欧式空间的直线——由于法向量唯一确定平面,因此也用该平面的法向量 l ~ \boldsymbol{\tilde{l}} l~ 表示欧式空间的直线。由解析几何易得,透视空间中的直线和点满足下面关系:{ l ~ 1 × l ~ 2 = x ~ x ~ 1 × x ~ 2 = l ~{l~1×l~2=x~x~1×x~2=l~{l~1×l~2=x~x~1×x~2=l~{ l~ 1 × l~ 2 = x~x~ 1 × x~ 2 = l~若透视空间中的视线在视平面上(对应欧式空间中点在直线上),则易知l ~ T x ~ = x ~ T l ~ = 0 \boldsymbol{\tilde{l}}^T\boldsymbol{\tilde{x}}=\boldsymbol{\tilde{x}}^T\boldsymbol{\tilde{l}}=0l~ T x~ = x~ T l~ =02 透视变换透视空间变换总体形式如图所示,具体而言列于表中。透视变换表征了平面间(平面上点)的映射关系。可见透视空间所有变换都是投影变换的特例,因此研究投影变换具有重要意义,其广泛用于图像校正、视角变换、图像拼接、增强现实等方面,如下图所示。🚀 计算机视觉基础教程说明🔥 更多精彩专栏:《机器人原理与技术》《计算机视觉教程》《机器学习》《嵌入式系统》《数值优化方法》…
详解贝叶斯方法1 贝叶斯方法的理解2 贝叶斯定理3 贝叶斯网络4 贝叶斯网络例题分析本文最新版本地址:https://blog.csdn.net/FRIGIDWINTER/article/details/1154046561 贝叶斯方法的理解首先给出一个概率和似然的对比实例:(Qa) 假设袋子内有 N N N个白球, M M M个黑球,伸手进去摸一次,摸出黑球的概率是多大?(Qb) 假设袋子内黑白球比例未知,伸手进去摸若干次,观察取出的球的颜色来推测袋子内的黑白球比例。对于似然问题(Qb),前面说过,贝叶斯学派认为环境参数 θ \theta θ不定,是一个随机变量。贝叶斯方法就是贝叶斯学派思考问题的模式,定义如下:参数先验信息 π ( θ ) + 样本观测数据 X = 后验分布 P ( θ ∣ X ) \text{参数先验信息}\pi \left( \theta \right) +\text{样本观测数据}X=\text{后验分布}P\left( \theta |X \right)参数先验信息π(θ)+样本观测数据X=后验分布P(θ∣X)上述思考模式意味着,新观察到的样本信息将修正人们以前对事物的认知。换言之,在得到新的样本信息之前,人们对模型的认知是先验分布 π ( θ ) \pi \left( \theta \right) π(θ),在得到新的样本信息 X X X后,人们对模型的认知为后验分布 P ( θ ∣ X ) P\left( \theta |X \right) P(θ∣X)。贝叶斯方法的深刻原因在于:现实世界本身就是不确定的,人类观察能力有局限性,日常所见几乎都是事物表面。正如(Qb)所描述的,我们往往只能知道从里面取出来的球是什么颜色,而并不能直接看到袋子里面得实际情况。所以这种通过对观测样本的补充,不断更新对事物规律的认识的思维方法符合机器学习思想和人类认识规律。2 贝叶斯定理贝叶斯定理的数学表述为:若 ⋃ k = 1 n B k = S \bigcup_{k=1}^n{B_k}=S ⋃ k=1n B k =S且 B i B j = ∅ ( i ≠ j , i , j = 1 , 2 , 3 , . . . , n ) B_iB_j=\varnothing \left( i\ne j,\;\;i,j=1,2,3,...,n \right) B i B j =∅(i =j,i,j=1,2,3,...,n), P ( B k ) > 0 ( k = 1 , 2 , . . . , n ) P\left( B_k \right) >0\left( k=1,2,...,n \right) P(B k )>0(k=1,2,...,n), P ( A ) > 0 P\left( A \right) >0 P(A)>0,则有: P ( B k ∣ A ) = P ( B k ) ⋅ P ( A ∣ B k ) ∑ i = 1 n P ( B i ) ⋅ P ( A ∣ B i ) = 边缘化 全概率公式 P ( B k ) ⋅ P ( A ∣ B k ) P ( A ) \;P\left( B_k\mid A \right) =\frac{P\left( B_k \right) \cdot P\left( A\mid B_k \right)}{\sum_{i=1}^n{P\left( B_i \right) \cdot P\left( A\mid B_i \right)}}\xlongequal[\text{边缘化}]{\text{全概率公式}}\frac{P\left( B_k \right) \cdot P\left( A\mid B_k \right)}{P\left( A \right)}P(B k ∣A)= ∑ i=1n P(B i )⋅P(A∣B i )P(B k )⋅P(A∣B k ) 全概率公式边缘化 P(A)P(B k )⋅P(A∣B k )其中的等式也称为贝叶斯公式。从机器学习模型的角度理解贝叶斯公式。假设数据样本 ( x 1 , x 2 , ⋯ , x n ) \left( x_1,x_2,\cdots ,x_n \right) (x 1 ,x 2 ,⋯,x n )是满足独立同分布的一组抽样 X X X,设模型参数为 θ \theta θ,基于贝叶斯方法,这里认为 满足先验分布,因此考虑参数后验分布: P ( θ ∣ X ) = P ( θ ) ⋅ P ( X ∣ θ ) P ( X ) \;P\left( \theta \mid X \right) =\frac{P\left( \theta \right) \cdot P\left( X\mid \theta \right)}{P\left( X \right)}P(θ∣X)= P(X)P(θ)⋅P(X∣θ)设稳定系数 α ( X ; θ ) = P ( X ∣ θ ) P ( X ) = 某参数下的样本分布 实际样本分布 \alpha \left( X;\theta \right) =\frac{P\left( X|\theta \right)}{P\left( X \right)}=\frac{\text{某参数下的样本分布}}{\text{实际样本分布}} α(X;θ)= P(X)P(X∣θ) = 实际样本分布某参数下的样本分布 ,当 α ( X ; θ ) = 1 \alpha \left( X;\theta \right) =1 α(X;θ)=1时说明参数估计与实际情况最为符合,其余情况下则说明此模型的样本估计并不稳定。所以在机器学习视角下,贝叶斯公式表述为:模型参数的后验概率等于其先验分布与稳定系数的乘积: P ( θ ∣ X ) = P ( θ ) ⋅ α ( X ; θ ) \;P\left( \theta \mid X \right) =P\left( \theta \right) ·\alpha \left( X;\theta \right)P(θ∣X)=P(θ)⋅α(X;θ)基于贝叶斯定理的模型训练,总会使稳定系数趋于1以使模型估计更稳定。3 贝叶斯网络贝叶斯网络又称信念网络(Belief Network),是一种概率图模型,模拟了人类推理过程中因果关系的不确定性,其网络拓扑结构是有向无环图(Directed Acyclic Graphical, DAG)。贝叶斯网络中的节点表示随机变量,有向连边表示变量间有因果关系或非条件独立,两个用箭头连接的节点就会产生一个条件概率值,如图所示。设 G = ( I , E ) G=\left( I,E \right) G=(I,E)表示一个DAG,其中 I I I是图形中所有节点的集合, E E E是所有有向连边的集合;函数 p a ( x ) pa\left( x \right) pa(x)表示一个从子节点到父节点的映射。令 x i ( i ∈ I ) x_i\left( i\in I \right) x i (i∈I)为DAG中某一节点 i i i所代表的随机变量,若 ∀ x i , i ∈ I \forall x_i\,\,, i\in I ∀x i ,i∈I的概率可以表示成:p ( x i ) = ∏ i ∈ I p ( x i ∣ x p a ( i ) ) p\left( x_i \right) =\prod_{i\in I}{p\left( x_i|x_{pa\left( i \right)} \right)}p(x i )= i∈I∏ p(x i ∣x pa(i) )则称此DAG是贝叶斯网络模型。图2列出了贝叶斯网络的基本结构,判断节点变量的独立性意义在于:从概率图模型可知,进行独立性假设后可以作出可视化的概率图模型,那么如果给定一个有向概率图,判断变量间的独立性关系就是其逆过程。从理论上对图2进行简单证明。(a) 直连给定 Z Z Z时,根据有向概率图因果关系可得:P ( X , Y , Z ) = P ( X ) P ( Z ∣ X ) P ( Y ∣ Z ) P\left( X,Y,Z \right) =P\left( X \right) P\left( Z|X \right) P\left( Y|Z \right)P(X,Y,Z)=P(X)P(Z∣X)P(Y∣Z)从而:P ( X , Y ∣ Z ) = P ( X , Y , Z ) P ( Z ) = P ( X ) P ( Z ∣ X ) P ( Y ∣ Z ) P ( Z ) = P ( X ∣ Z ) P ( Y ∣ Z ) P\left( X,Y|Z \right) =\frac{P\left( X,Y,Z \right)}{P\left( Z \right)}\\=\frac{P\left( X \right) P\left( Z|X \right) P\left( Y|Z \right)}{P\left( Z \right)}\\=P\left( X|Z \right) P\left( Y|Z \right)P(X,Y∣Z)= P(Z)P(X,Y,Z)= P(Z)P(X)P(Z∣X)P(Y∣Z)=P(X∣Z)P(Y∣Z)所以 X X X、 Y Y Y在给定 Z Z Z时条件独立。(b) 分连给定 Z Z Z时,根据有向概率图因果关系可得:P ( X , Y , Z ) = P ( Z ) P ( X ∣ Z ) P ( Y ∣ Z ) P\left( X,Y,Z \right) =P\left( Z \right) P\left( X|Z \right) P\left( Y|Z \right)P(X,Y,Z)=P(Z)P(X∣Z)P(Y∣Z)从而:P ( X , Y ∣ Z ) = P ( X , Y , Z ) P ( Z ) = P ( Z ) P ( X ∣ Z ) P ( Y ∣ Z ) P ( Z ) = P ( X ∣ Z ) P ( Y ∣ Z ) P\left( X,Y|Z \right) =\frac{P\left( X,Y,Z \right)}{P\left( Z \right)}\\=\frac{P\left( Z \right) P\left( X|Z \right) P\left( Y|Z \right)}{P\left( Z \right)}\\=P\left( X|Z \right) P\left( Y|Z \right)P(X,Y∣Z)= P(Z)P(X,Y,Z)= P(Z)P(Z)P(X∣Z)P(Y∣Z)=P(X∣Z)P(Y∣Z)所以 X X X、 Y Y Y在给定 Z Z Z时条件独立。© 汇连给定 Z Z Z时,根据有向概率图因果关系可得:P ( X , Y , Z ) = P ( X ) P ( Y ) P ( Z ∣ X , Y ) P\left( X,Y,Z \right) =P\left( X \right) P\left( Y \right) P\left( Z|X,Y \right)P(X,Y,Z)=P(X)P(Y)P(Z∣X,Y)从而:P ( X , Y ∣ Z ) = P ( X , Y , Z ) P ( Z ) = P ( X ) P ( Y ) P ( Z ∣ X , Y ) P ( Z ) ≠ P ( X ∣ Z ) P ( Y ∣ Z ) P\left( X,Y|Z \right) =\frac{P\left( X,Y,Z \right)}{P\left( Z \right)}\\=\frac{P\left( X \right) P\left( Y \right) P\left( Z|X,Y \right)}{P\left( Z \right)}\\\ne P\left( X|Z \right) P\left( Y|Z \right)P(X,Y∣Z)= P(Z)P(X,Y,Z)= P(Z)P(X)P(Y)P(Z∣X,Y) =P(X∣Z)P(Y∣Z)而不给定 Z Z Z时:P ( X , Y ) = ∑ Z P ( X , Y , Z ) = P ( X ) P ( Y ) ∑ Z P ( Z ∣ X , Y ) = P ( X ) P ( Y ) P\left( X,Y \right) =\sum_Z{P\left( X,Y,Z \right)}\\=P\left( X \right) P\left( Y \right) \sum_Z{P\left( Z|X,Y \right)}\\=P\left( X \right) P\left( Y \right)P(X,Y)= Z∑ P(X,Y,Z)=P(X)P(Y) Z∑ P(Z∣X,Y)=P(X)P(Y)所以 X X X、 Y Y Y在给定 Z Z Z时不条件独立,不给定 Z Z Z时条件独立。综合上述(a)(b)©,当 X X X、 Y Y Y关于 Z Z Z(给定或不给定)条件独立时,称 X X X、 Y Y Y关于 Z Z Z(给定或不给定)有向分离,简称D-分离。4 贝叶斯网络例题分析
详解进程及其调度算法1 基本概念2 进程调度2.1 时间片管理2.2 调度算法1 基本概念进程与程序的关系是:程序是存储在文件中的、带有结构和顺序信息(用于控制指令执行次序)的指令列表,进程是程序的运行实例。通俗来讲,程序描述了如何处理一个事务,进程按程序的描述实际执行之。相同的程序可以多次执行,每次执行操作系统都将创建一个独立的进程,并开始读取指令列表。进程更正式的定义是:计算机中的程序关于某数据集合的一次运行活动,是系统进行资源分配和调度的最小单位,是操作系统结构的基础。进程是线程的容器,同时包含了内存、句柄等计算机资源,如图1所示。数据集合包括指令在内存中的版本、程序中使用的变量以及其他相关元数据。其中变量和元数据是可变的,称为进程状态。进程主要的五种状态如图2所示。重点辨析就绪和阻塞状态。就绪:进程分配的时间片已用完,或在中断机制下,有更高优先级的进程打入,此时进程进入就绪队列等待下一次被选中阻塞:进程所需要的资源或某些事件不满足要求,如暂时不能访问某一资源、操作系统尚未完成服务、系统正在初始化I/O设备、等待用户的输入信息等,当这些系统资源被满足后,进程从阻塞队列出队而进入就绪队列等待CPU资源。简言之,就绪状态等待CPU资源,而阻塞状态等待CPU以及所需的其他系统资源。进程状态区分了同一程序的多道进程,操作系统级别用唯一进程标识符(Process IDentifier, PID)识别不同的进程。2 进程调度进程的运行,是其指令在CPU中执行的行为。CPU是系统最主要的资源,而操作系统最基本的作用是管理系统资源,因此操作系统负责管理系统进程,以及在任意给定的时间选择哪一个进程运行(仅考虑单核系统)。现代操作系统中,执行上述职能的部件是调度器。调度器选中某道进程后,由其子部件分派器将该进程由就绪状态引入到运行状态。因此调度是通过一定算法选择某道进程,并将其从就绪状态切换到运行状态的过程。2.1 时间片管理在抢占式调度器中,允许某个特定进程运行一段时间后,切换到就绪队列等待调度器下一次选中,而将另一个进程从就绪队列中引出,并设置为运行状态。这里的时间称为时间片。由于调度器执行调度决策本身需要占用系统处理时间,这部分进程外的额外时间称为调度开销。调度开销中的最大成分是上下文切换,即CPU从执行一道进程或线程切换到执行另一道进程或线程的动作,如图3所示。上下文切换时要进行保护现场和恢复现场,即保存某道进程被抢占或阻塞前最后一次运行时的执行环境,和在下一次运行时复现,这里的执行环境包括堆栈、程序计数器以及其他操作系统结构等。若时间片太短,由于调度开销的存在,操作系统将频繁执行调度活动,使系统处理资源被浪费;若时间片太长,则就绪队列的其他进程不得不在轮转期间等待更长的时间,表现出应用程序的未响应或缺乏响应状态。时间片大小没有随看技术进步(主要指CPU处理速度)而大幅变化,原因在于:(1) 人类对响应性的感知能力没有改变,没有必要为此缩短时间片;(2) 系统的复杂性和在其上运行的应用程序的数量也在以高速率提升,以实现其丰富的功能,即相较原有系统多出的运算资源又被分配给了更多的进程,而非延长每一道进程的时间。现代系统中时间片大小一般在大概10~200ms的范围内(Windows操作系统为10ms), 在非常快速的平台上时间片更小(低至约1ms)。时间片大小的范围设定能够用作实现进程优先级的一种手段:高优先级的进程应该获得较大份额的可用处理资源。一种特例是,如果进程是IO密集型的,它们执行IO时会被堵塞而无视其优先级(分配的时间片再长也会进入阻塞队列)。2.2 调度算法在基础算法上,进行了调度算法的改进。(1) 多级队列(MLQ, Multilevel Queue):按进程性质设置若干就绪队列,每个队列可执行不同调度算法。例如计算密集型进程队列中执行FCFS调度;系统进程队列执行RR调度等。根据系统的工作特点,为不同的进程队列分配不同的CPU处理时间。值得注意的是,每级队列执行的调度算法,不会影响到其他进程队列。(2) 多级反馈队列(MLFQ, Multilevel Feed-back Queue):以MLQ为基础,但与之不同在于:进程能够在不同队列中移动以允许调度过程达到一种平衡——短期调度获得任务响应性,长期调度确保公平性和系统效率;队列按优先级组织。进程移动的基本规则是:①若进程用尽时间片仍未完成任务,则降到下一级进程队列;②若进程在时间片内让出控制(如发生阻塞),则保留在该级队列;③若进程因为执行IO而发生阻塞,则会提升到上一级进程队列。以科学计算模拟为例,由于其计算时间较长以至于在若干个时间片内无法处理完毕,进程将逐次被降级,并留在较低级的进程队列中接受RR调度。当科学计算结束后,需要通过IO将结果反馈给用户,则该进程开始逐步回升到高优先级队列中。只有当最高优先级队列中进程临时用尽时,才有机会执行低级队列中的进程。由于进程的优先级根据其行为动态变化,因此该算法灵活性得到保证。
详解极大似然估计与极大后验估计1 概率与似然2 先验概率与后验概率3 极大似然估计4 极大后验估计最新文章地址:机器学习:通俗理解极大似然估计和极大后验估计+实例分析1 概率与似然概率与似然是站在两个角度上看待问题。假设样本集为 X X X,环境参数为 θ \theta θ,则对于:p ( X ↔ θ ) p\left( X\leftrightarrow \theta \right)p(X↔θ)(a) 当环境参数 θ \theta θ已知时,上式退化为 p ( X ∣ θ ) p\left( X|\theta \right) p(X∣θ),此为概率问题:概率即是给定模型参数后,对样本合理性的描述,而不涉及任何观测数据。(b) 当环境参数 θ \theta θ未知时,上式退化为 p ( θ ∣ X ) p\left( \theta |X \right) p(θ∣X),此为似然问题:似然即是给定样本观测数据,从而推测可能产生这个结果的环境参数。似然问题也称为逆概(Converse Probability)问题。以一个实例进一步说明:(Qa) 假设掷一枚硬币正面朝上概率为 θ = 0.5 \theta=0.5 θ=0.5,求掷10次硬币中有7次正面朝上的概率。(Qb) 有一个硬币有 的概率正面朝上,为确定 θ \theta θ做如下实验:掷10次硬币得到一个正反序列——HHTTHTHHHH。显然,(Qa)可以不依赖任何观测数据计算出 ,即最终的观测数据在0.117附近则认为是合理的;(Qb)则是通过观测数据来反推环境参数:事实上,这符合人类认识的规律——即在不断地实践中获得观测数据,再从观测数据中总结经验规律(对应于模型参数)。在机器学习中,也往往是需要机器根据已有的数据学到相应的分布——即确定由 θ \theta θ决定的模型。在似然问题中,需要引入**似然函数(Likelihood Function)**的概念——其描述了对于不同的取值 ,其对于观测数据的适合程度。由于似然本身的定义,似然函数应由全体样本得出。仍以(Qb)为例,计算似然函数为:L ( θ ) = P ( X ; θ ) = θ ⋅ θ ⋅ ( 1 − θ ) ⋅ ( 1 − θ ) ⋅ θ ⋅ ( 1 − θ ) ⋅ θ ⋅ θ ⋅ θ ⋅ θ = θ 7 ( 1 − θ ) 3 L\left( \theta \right) =P\left( X;\theta \right) \\=\theta \cdot \theta \cdot \left( 1-\theta \right) \cdot \left( 1-\theta \right) \cdot \theta \cdot \left( 1-\theta \right) \cdot \theta \cdot \theta \cdot \theta \cdot \theta \\=\theta ^7\left( 1-\theta \right) ^3L(θ)=P(X;θ)=θ⋅θ⋅(1−θ)⋅(1−θ)⋅θ⋅(1−θ)⋅θ⋅θ⋅θ⋅θ=θ 7 (1−θ) 3值得注意的是 P ( X ; θ ) ≠ P ( X ∣ θ ) P\left( X;\theta \right) \ne P\left( X|\theta \right) P(X;θ) =P(X∣θ),因为环境参数 θ \theta θ不总是随机变量。 θ \theta θ确定,即为一个固定参数是频率学派的观点; θ \theta θ不定,即为一个随机变量是贝叶斯学派的观点。对上例中 L ( θ ) L\left( \theta \right) L(θ)作出曲线如图1所示。根据似然函数可以看出:当 θ = 0.7 \theta=0.7 θ=0.7即硬币向上概率为70%时,最有可能出现实验样本的正反序列。当然 θ = 0.7 \theta=0.7 θ=0.7并不符合直觉,因为实验样本数量太少存在偶然误差,随着实验次数的增加, θ \theta θ将趋于0.5。2 先验概率与后验概率从一个实例说起。如图2所示,假设每次实验时随机从某个盒子里挑出一个球。随机变量 A A A表示挑出的盒子,且设 P ( A = b l u e ) = 0.6 P\left( A=blue \right) =0.6 P(A=blue)=0.6;随机变量 B B B表示挑中的球。(Qa) P ( A = r e d ) = ? P\left( A=red \right) =? P(A=red)=?(Qb) 在挑中蓝色盒子的条件下, P ( B = g r e e n ) = ? P\left( B=green \right) =? P(B=green)=?(Qc) 已知挑出绿色的球,则其是从红色盒子中挑出的概率有多大?很显然,可以得到如下答案:(Aa) P ( A = r e d ) = 0.4 P\left( A=red \right) =0.4 P(A=red)=0.4(Ab) P ( B = g r e e n ∣ A = b l u e ) = P ( B = g r e e n , A = b l u e ) P ( A = b l u e ) = 3 4 P\left( B=green|A=blue \right) =\frac{P\left( B=green,A=blue \right)}{P\left( A=blue \right)}=\frac{3}{4} P(B=green∣A=blue)= P(A=blue)P(B=green,A=blue) = 43(Ac) P ( A = r e d ∣ B = g r e e n ) = P ( B = g r e e n ∣ A = r e d ) P ( A = r e d ) P ( B = g r e e n ) = 2 11 P\left( A=red|B=green \right) =\frac{P\left( B=green|A=red \right) P\left( A=red \right)}{P\left( B=green \right)}=\frac{2}{11} P(A=red∣B=green)= P(B=green)P(B=green∣A=red)P(A=red) = 112先验概率,指根据以往经验和分析,在实验或采样前就可以得到而不依赖于其他任何事件的概率。例如(Aa)中 P ( A = r e d ) = 0.4 P\left( A=red \right) =0.4 P(A=red)=0.4,其不依赖于任何事件(如取出绿球等),在实验前就可得出。先验概率有时也称作边缘概率,全概率公式 P ( Y ) = ∑ i P ( X i ) P ( Y ∣ X i ) P\left( Y \right) =\sum_i{P\left( X_i \right) P\left( Y|X_i \right)} P(Y)=∑ i P(X i )P(Y∣X i )也可作为边缘化的工具,例如(Ac)中:P ( B = g r e e n ) = P ( A = r e d ) P ( B = g r e e n ∣ A = r e d ) + P ( A = b l u e ) P ( B = g r e e n ∣ A = b l u e ) = 0.4 × 0.25 + 0.6 × 0.75 = 0.55 P\left( B=green \right) =P\left( A=red \right) P\left( B=green|A=red \right) +P\left( A=blue \right) P\left( B=green|A=blue \right) \\=0.4\times 0.25+0.6\times 0.75\\=0.55P(B=green)=P(A=red)P(B=green∣A=red)+P(A=blue)P(B=green∣A=blue)=0.4×0.25+0.6×0.75=0.55后验概率,指某事件(如实验或采样)已经发生,而与此相因果事件的概率。后验概率有时也称作条件概率。例如(Ab)(Ac)3 极大似然估计根据似然的定义知道,似然方法往往只关注似然函数取最大值时,对应的模型参数,因为这个模型参数下最有可能出现样本的分布。由此引申出基于似然函数值极大的参数估计方法——极大似然法。假设数据样本 ( x 1 , x 2 , ⋯ , x n ) \left( x_1,x_2,\cdots ,x_n \right) (x 1 ,x 2 ,⋯,x n )是满足独立同分布的一组抽样 X X X,设模型参数为 θ \theta θ,则:θ ^ M L E = a r g max P ( X ; θ ) = a r g max [ P ( x 1 ; θ ) P ( x 2 ; θ ) ⋯ P ( x n ; θ ) ] = a r g max [ ln P ( x 1 ; θ ) + ln P ( x 2 ; θ ) + ln P ( x n ; θ ) ] = a r g min [ − ∑ i = 1 n ln P ( x i ; θ ) ] \widehat{\theta }_{MLE}=arg\max P\left( X;\theta \right) \\=arg\max \left[ P\left( x_1;\theta \right) P\left( x_2;\theta \right) \cdots P\left( x_n;\theta \right) \right] \\=arg\max \left[ \ln P\left( x_1;\theta \right) +\ln P\left( x_2;\theta \right) +\ln P\left( x_n;\theta \right) \right] \\=arg\min \left[ -\sum_{i=1}^n{\ln P\left( x_i;\theta \right)} \right]θ MLE =argmaxP(X;θ)=argmax[P(x 1 ;θ)P(x 2 ;θ)⋯P(x n ;θ)]=argmax[lnP(x 1 ;θ)+lnP(x 2 ;θ)+lnP(x n ;θ)]=argmin[− i=1∑n lnP(x i ;θ)]其中对数化是极大似然估计及其他机器学习理论推导的重要方法。MLE是对似然函数 P ( X ; θ ) P\left( X;\theta \right) P(X;θ)的优化,其认为环境参数 θ \theta θ确定而样本空间 X X X不定。MLE可以看作采用了经验风险最小化(Experience Risk Minimization)策略,更侧重从样本数据中学习模型的潜在参数,将训练集损失降到最低。这在数据样本缺失的情况下容易管中窥豹,使模型发生过拟合。4 极大后验估计顾名思义,MAP优化的是后验概率 P ( θ ∣ X ) P\left( \theta |X \right) P(θ∣X),即根据采集的数据样本反过来修正参数——因此必然存在先验性的模型参数。归结起来,MAP依然是根据已知样本,通过调整模型参数使得模型能够产生该数据样本的概率最大——这点与极大似然估计完全一致。只不过MAP相较MLE多了一个对于模型参数的先验分布假设,而不再一味地依赖数据样例,这使得此时的环境参数不再是固定的,而是一个随机变量。假设数据样本 ( x 1 , x 2 , ⋯ , x n ) \left( x_1,x_2,\cdots ,x_n \right) (x 1 ,x 2 ,⋯,x n )是满足独立同分布的一组抽样 X X X,设模型参数为 θ \theta θ,则:θ ^ M A P = a r g max P ( θ ∣ X ) = a r g max P ( X ∣ θ ) P ( θ ) P ( X ) = a r g max [ ln P ( X ∣ θ ) + ln P ( θ ) − ln P ( X ) ] = a r g min [ − ∑ i = 1 n ln P ( x i ∣ θ ) − ln P ( θ ) ] \widehat{\theta }_{MAP}=arg\max P\left( \theta |X \right) \\=arg\max \frac{P\left( X|\theta \right) P\left( \theta \right)}{P\left( X \right)}\\=arg\max \left[ \ln P\left( X|\theta \right) +\ln P\left( \theta \right) -\ln P\left( X \right) \right] \\=arg\min \left[ -\sum_{i=1}^n{\ln P\left( x_i|\theta \right) -\ln P\left( \theta \right)} \right] θ MAP =argmaxP(θ∣X)=argmax P(X)P(X∣θ)P(θ)=argmax[lnP(X∣θ)+lnP(θ)−lnP(X)]=argmin[−∑ i=1n lnP(x i ∣θ)−lnP(θ)]其中忽略 P ( X ) P\left( X \right) P(X)的合理性在于,对于任意一次抽样 X X X进行的参数调整都不会影响当次的 P ( X ) P\left( X \right) P(X),因为 P ( X ) P\left( X \right) P(X)属于先验概率或者说是实验的客观事实,并不依赖于环境参数。MAP可以看作采用了结构风险最小化(Structural Risk Minimization)策略,在经验风险最小化的基础上考虑参数的先验分布——类似于在损失函数上增加了正则项,防止模型出现过拟合状态。这样在数据样本不是很充分的情况下,仍可以通过模型参数先验假设,辅以数据样本,做到尽可能地还原真实模型分布。从最终的优化结果可知,MAP的似然函数为:L ( θ ) = P ( X ∣ θ ) P ( θ ) L\left( \theta \right) =P\left( X|\theta \right) P\left( \theta \right)L(θ)=P(X∣θ)P(θ)其中 P ( θ ) P\left( \theta\right) P(θ)为参数的先验分布。再次以第2节(Qb)为例。假设一枚质地均匀的硬币正面概率 θ \theta θ满足均值为0.5,方差为1的先验分布,即:θ N ( 0.5 , 1 ) \theta~N\left( 0.5,1 \right)θ N(0.5,1)则似然函数为:L ( θ ) = θ 7 ( 1 − θ ) 3 1 2 π e − ( θ − 0.5 ) 2 2 L\left( \theta \right) =\theta ^7\left( 1-\theta \right) ^3\frac{1}{\sqrt{2\pi}}e^{-\frac{\left( \theta -0.5 \right) ^2}{2}}L(θ)=θ 7 (1−θ) 3 2π1 e − 2(θ−0.5) 2再对其进行MLE(考虑先验分布的MLE等价于MAP;环境参数服从均匀分布的MAP等价于MLE)可得 θ ≈ 0.696 \theta \approx 0.696 θ≈0.696,与第2节的 θ = 0.7 \theta =0.7 θ=0.7相比,MAP考虑了结构风险,有对过拟合进行修正的趋势。
主要讨论实值函数对矩阵或向量的梯度。先给出定义,若函数 f : R m × n → R f:\mathbb{R}^{m\times n}\rightarrow \mathbb{R} f:R m×n →R,则 ∂ f ∂ X \frac{\partial f}{\partial X} ∂X∂f 也是一个 m × n m\times n m×n矩阵,且满足:( ∂ f ∂ X ) i j = ∂ f ∂ x i j \left( \frac{\partial f}{\partial X} \right) _{ij}=\frac{\partial f}{\partial x_{ij}}( ∂X∂f ) ij = ∂x ij∂f则表示实值函数对矩阵的梯度,记做 ∇ X f \nabla _{\boldsymbol{X}}f ∇ X f同样地,若函数 f : R m → R f:\mathbb{R}^{m}\rightarrow \mathbb{R} f:R m →R,则 ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f 也是一个 m m m维列向量,且满足:( ∂ f ∂ x ) i = ∂ f ∂ x i \left( \frac{\partial f}{\partial \boldsymbol{x}} \right) _i=\frac{\partial f}{\partial x_i}( ∂x∂f ) i = ∂x i∂f则表示实值函数对向量的梯度,记做 ∇ x f \nabla _{\boldsymbol{x}}f ∇ x f总结:实值函数对向量或矩阵的梯度,与该向量或矩阵同型。下面从定义出发,推导机器学习中常用的向量/矩阵梯度公式。① ∇ ( a T x ) = ∇ ( x T a ) = a \nabla \left( \boldsymbol{a}^T\boldsymbol{x} \right) =\nabla \left( \boldsymbol{x}^T\boldsymbol{a} \right) =\boldsymbol{a} ∇(a T x)=∇(x T a)=a证明:∇ ( a T x ) i = ∂ a T x ∂ x i = ∂ ( ∑ j a j x j ) ∂ x i = a i ⇒ ∇ ( a T x ) = a \nabla \left( \boldsymbol{a}^T\boldsymbol{x} \right) _i=\frac{\partial \boldsymbol{a}^T\boldsymbol{x}}{\partial x_i}=\frac{\partial \left( \sum_j{a_jx_j} \right)}{\partial x_i}=a_i\\\Rightarrow \,\, \nabla \left( \boldsymbol{a}^T\boldsymbol{x} \right) =\boldsymbol{a}∇(a T x) i = ∂x i∂a T x = ∂x i∂(∑ j a j x j ) =a i⇒∇(a T x)=a② ∇ ∥ x ∥ 2 2 = ∇ ( x T x ) = 2 x \nabla \lVert \boldsymbol{x} \rVert _{2}^{2}=\nabla \left( \boldsymbol{x}^T\boldsymbol{x} \right) =2\boldsymbol{x} ∇∥x∥ 22 =∇(x T x)=2x证明:∇ ( x T x ) i = ∇ ( ∑ j x j 2 ) x i = 2 x i ⇒ ∇ ( x T x ) = 2 x \nabla \left( \boldsymbol{x}^T\boldsymbol{x} \right) _i=\frac{\nabla \left( \sum_j{x_{j}^{2}} \right)}{x_i}=2x_i\\\Rightarrow \,\, \nabla \left( \boldsymbol{x}^T\boldsymbol{x} \right) =2\boldsymbol{x}∇(x T x) i = x i∇(∑ j x j2 ) =2x i⇒∇(x T x)=2x③ ∇ ( x T A x ) = ( A + A T ) x \nabla \left( \boldsymbol{x}^T\boldsymbol{Ax} \right) =\left( \boldsymbol{A}+\boldsymbol{A}^T \right) \boldsymbol{x} ∇(x T Ax)=(A+A T )x证明:L H S = ∇ ( x C T A x ) + ∇ ( x T A x C ) = ( x C T A ) T + A x C = ( A + A T ) x = R H S LHS=\nabla \left( \boldsymbol{x}_{C}^{T}\boldsymbol{Ax} \right) +\nabla \left( \boldsymbol{x}^T\boldsymbol{Ax}_C \right) \\=\left( \boldsymbol{x}_{C}^{T}\boldsymbol{A} \right) ^T+\boldsymbol{Ax}_C\\=\left( \boldsymbol{A}+\boldsymbol{A}^T \right) \boldsymbol{x}\\=RHSLHS=∇(x CT Ax)+∇(x T Ax C )=(x CT A) T +Ax C=(A+A T )x=RHS
C++多态性原理详解(静态多态、动态多态、虚函数、虚函数表)先给出定义:多态是同一个行为具有多个不同表现形式或形态的能力。1 联编联编也称绑定,是指在一个源程序经过编译链接成为可执行文件的过程中,将可执行代码“缝合”在一起的步骤。其中在程序运行前就完成的称为静态联编(前期联编);在程序运行时完成的称为动态联编(后期联编)。静态联编支持的多态性称为编译时多态(静态多态),通过函数重载或函数模板实现;动态联编支持的多态性称为运行时多态(动态多态),通过虚函数表实现。2 静态多态性2.1 函数重载首先考察代码工程中的一种情形:void Swap1(int* a, int* b); void Swap2(float* a, float* b); void Swap3(char* a, char* b); void Swap4(double* a, double* b);当用到几个实现功能相同,但细节不同的函数时,C语言中会如上例所示,用不同的函数名进行区分,但这样不仅影响美观,也不便于调用和代码管理。于是在C++中引入了函数重载:重载允许在同一作用域中声明几个类似的同名函数,这些同名函数的形参列表(参数个数,类型,顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。上例在C++中可以改写为:void Swap(int* a, int* b); void Swap(float* a, float* b); void Swap(char* a, char* b); void Swap(double* a, double* b);在C++中不仅函数可以重载,运算符也可以重载。可以将运算符理解为一种函数名,例如a+=1可以视作:a.+=(1),其中a是对象,+=是其属性。既然运算符可以这样审视,那么自然可以进行重载。操作符重载示例如下,其中operator为声明操作符的关键字。void operator +=(int number);2.2 函数模板考察2.1节中的函数重载。若重载函数仅有接口数据类型的不同,函数体、函数名都完全一样,那么可以使用函数模板进一步简化代码。首先给出函数模板的格式:template <typename 类型参数1 , typename 类型参数2 , ...> 返回值类型 函数名(形参列表){ 函数体 }例如2.1节中的swap重载函数可用一个函数模板代替为:template <typename T>void swap(T* a, T* b) { T temp = *a; *a = *b; *b = temp; }其中template、typename均为声明函数模板用的关键字。用上面一个函数模板即可对任意合法的输入数据类型进行处理。需要注意的是,目前编译里不支持模板函数的声明和实现分离,因此一般将模板函数体也写在头文件中。3 动态多态性动态多态允许用一个或多个派生类对象的属性配置父类对象。在多态性的支持下,父类对象的某个接口会随着派生类对象的不同而执行不同的操作。3.1 实现原理先考虑下面的代码工程。class Father { public: virtual void func1(){ std::cout << "FUNC1:Father" << std::endl; } virtual void func2(){ std::cout << "FUNC2:Father" << std::endl; } void func3(){ std::cout << "FUNC3:Father" << std::endl; } }; class Son :public Father { public: virtual void func1(){ std::cout << "FUNC1:Son" << std::endl; } void func3(){ std::cout << "FUNC3:Son" << std::endl; } }; void testFunc(Father* ptr) { ptr->func1(); ptr->func3(); } int main() { Son* testSon = new Son; Father* testFather = new Father; std::cout << "==============子类测试=============\n"; testFunc(testSon); std::cout << "\n==============父类测试=============\n"; testFunc(testFather); }其执行结果为:==============子类测试============= FUNC1:Son FUNC3:Father ==============父类测试============= FUNC1:Father FUNC3:Father首先阐明要通过子类属性配置父类的原因。如图1所示的例子,移动硬盘、U盘、SD卡都是从父类存储设备派生出来的子类,在实际应用中,只需设计一个存储外设接口,通过判断具体使用的是哪一种外设来确定该接口将执行的驱动程序。这种面向接口的设计思路可以增强复用性和模块化,否则一种外设就需要对应一种接口,过于冗杂。下面通过分析代码结果,来说明多态的实现原理。函数testFunc()希望展现出多态性,子类实例testSon在执行func1()时体现子类特性,但在执行func3()时却仍保留了父类特性——即不显示出多态,这是因为func1()被关键字virtual所修饰。关键字virtual申请使用后期联编,被其修饰的函数称为虚函数,而在后期联编下才支持动态多态,因此只有func1()显示出多态性。至此,先小结构成动态多态的条件:(a) 调用函数的对象必须是指针或者引用;(b) 被调用的函数必须是虚函数。后期联编通过虚函数表V-Table实现动态多态,虚函数表是一个实例的虚函数地址表。若某个实例存在虚函数,则该实例的内存中会自动分配虚函数表,指明该实例实际应该调用的函数。实例中通过虚函数指针,指向虚函数表所在的内存位置。图2展示了各种类型的多态情况,实例化时将更新虚函数表——完成继承和覆盖,运行程序过程中,编译器将查找虚表,从而链接到该实例实际应该执行的函数。示例代码为图2(b)的类型,图3所示是示例代码的变量监视区,可见父类和派生类虚函数表不同,派生类若覆盖虚函数则会对虚表进行更新(func1),父类中不被覆盖的虚函数(func2)仍被子类继承下来。3.2 纯虚函数通过令虚函数为0可以表示纯虚函数,纯虚函数只定义了函数接口,包含纯虚函数的类称为抽象类。抽象类定义了一个类可能发出的动作的原型,但既没有实现,也没有任何状态信息。引入抽象类的原因在于,很多情况下基类本身实例化不合情理。例如动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身实例化没有意义,这时就可将动物类定义成抽象类。class Animal { public: virtual void eat() = 0; virtual void run() = 0; virtual ~Animal() = default; };由于抽象类只提供原型而无法被实例化,因此派生类必须提供接口的具体实现,否则亦无法被实例化。
机器学习:从随机过程到蒙特卡罗方法1 理解随机过程人类的主观能动性决定,为了改造世界,必须认识世界的发展规律。而所谓发展,实质上就是各种事物随着时间推移,不断运动变化的过程——从宏观的天体运动到微观的分子运动。在古典时期,牛顿的经典物理分析方法成功地描述了宏观运动世界,确定性是牛顿体系最大的特征——给定速度和加速度,运动轨迹随即可以建模确立。在近代物理时期,牛顿体系的确定性似乎不够适用,因为真实世界的更多情况,是充满复杂而未知因素的运动。这些不确定的因素——噪音,对事物变化至关重要,例如微观世界的分子热运动、不确定性关系等。由于牛顿方法的确定性,这些不确定性领域的问题难以或不能应用经典物理方法。因此,为了描述不确定性,引入了概率论与数理统计的一套方法。牛顿体系研究的经典过程指的是一个量随时间确定的变化;相应地,随机过程指的是一个量随时间可能的变化。在随机过程里,每一个时刻变化的方向都不确定,换言之,随机过程由一系列随机变量组成,每个时刻的系统状态都由一个或一组随机变量表述,整个随机过程的实现构成了概率空间的一个轨迹。在随机过程中,也有一套类似于经典过程中牛顿方法的理论武器,下面分点阐明。1.1 概率空间在不确定性领域,无非关心两件事:①有哪些可以实现的可能;②每种可能的数值大小。用概率空间(或称事件空间、态空间)描述①,用概率描述②。所谓概率空间,是人类现有知识总和的近似。把我们用它描述已知的未知,而无法描述未知的未知,因为只有经过人类实践,能动地转化为知识后,才能对概率空间进行补充。通俗来讲,概率空间相当于“过往经验”。事实上,对未来的预测来源于过往经验,而沟通经验与未来的工具就是概率——所谓一件事发生可能性大小,就是这件事在历史中发生的频率大小。通过经验加上已知信息实现对未来预测的方式,就是贝叶斯方法。1.2 分布函数分布函数描述了概率空间与概率的对应关系,概率空间和概率是不确定性领域研究的关键和基础,因此分布函数的引入也具有深远意义:分布函数是提取随机过程内有效信息的第一手段和入口,而蒙特卡洛方法就是研究分布函数的武器。例如图2所示的高斯分布 z ( x , y ) = 1 2 π e − ( x 2 + y 2 ) z\left( x,y \right) =\frac{1}{2\pi}e^{-\left( x^2+y^2 \right)} z(x,y)= 2π1 e −(x 2 +y 2 ) ,其描述的概率空间为 Σ = { ( x , y ) ∣ x , y ∈ R } \varSigma =\left\{ \left( x,y \right) \,\,| x,y\in \mathbb{R} \right\} Σ={(x,y)∣x,y∈R},将概率空间中的数据点代入分布函数,得到的值即为此数据点的实现概率,所以说分布函数指出了概率空间与概率的对应。2 蒙特卡罗方法随机过程问题中经常要研究一些随机变量的分布规律,比如分布函数的形式、分布参数估计等。利用解析方法来求解或利用大量实验来研究分布情况,通常耗资巨大或根本无法测量。为了解决这个难点,引入了蒙特卡洛方法(也称统计模拟方法)。蒙特卡洛方法是利用随机数进行数值模拟的方法,它根据待求随机问题的变化规律和物理现象本身的统计规律人为地构造出一个合适的概率模型来模拟实际过程,并按照此概率模型进行大量的随机统计实验,使实验得出的某些统计参数正好是待求问题的解。例1 求解积分 θ = ∫ a b f ( x ) d x \theta =\int\limits_a^b{f\left( x \right) dx} θ= a∫b f(x)dx如图3所示,假设此积分的解析方法十分繁琐,借助蒙特卡洛方法来近似此积分数值。设在区间 [ a , b ] \left[ a,b \right] [a,b]上 x x x的概率密度函数是 p ( x ) p(x) p(x),则:基于概率密度函数 p ( x ) p(x) p(x)进行大量的随机抽样,将样本代入上面的求和式子中即可得到积分的近似解。其中 θ ≈ 1 n ∑ i = 1 n f ( x i ) p ( x i ) \theta \approx \frac{1}{n}\sum_{i=1}^n{\frac{f\left( x_i \right)}{p\left( x_i \right)}} θ≈ n1 ∑ i=1n p(x i )f(x i ) , 称为蒙特卡洛方法的一般数学形式。借助例1,阐明使用蒙特卡洛方法的几个关键点与步骤:(i) 构造或描述概率过程例1中的概率密度函数 p ( x ) p(x) p(x)就是所谓的概率过程,通常根据待求随机问题的变化规律和物理现象本身的统计规律人为构造,没有人为确定的先验分布将无法使用MC。(ii) 从已构造的概率分布中抽样按照步骤(i)构造的 p ( x ) p(x) p(x)进行随机抽样。最容易采样的分布是均匀分布 ,而其他的常见分布,无论离散还是连续(例如正态分布等),它们的随机样本都可以通过均匀分布 U ( 0 , 1 ) U\left( 0,1 \right) U(0,1)转换得到。举例而言,Box-Muller变换实现了从均匀分布样本到标准二维正态分布样本的映射:{ X = − 2 ln U 2 cos ( 2 π U 1 ) Y = − 2 ln U 2 sin ( 2 π U 1 ) 其中 U 1 , U 2 U ( 0 , 1 ) \left\{X=−2lnU2−−−−−−−√cos(2πU1)Y=−2lnU2−−−−−−−√sin(2πU1)X=−2lnU2cos(2πU1)Y=−2lnU2sin(2πU1)\right. \,\, \text{其中}U_1, U_2~U\left( 0,1 \right){ X= −2lnU 2 cos(2πU 1 )Y= −2lnU 2 sin(2πU 1 ) 其中U 1 ,U 2 U(0,1)则 X X X、 Y Y Y服从均值为0,方差为1的正态分布。对于不常见的复杂分布 ,难以通过均匀分布来实现相互映射,此时采用拒绝采样(Reject Sampling):首先选定一个容易采样的概率分布 q ( x ) q\left( x \right) q(x)(例如高斯分布),再选定一个常数 k k k,使得在概率空间中的所有样本点都满足 p ( x ) ⩽ k q ( x ) p\left( x \right) \leqslant kq\left( x \right) p(x)⩽kq(x);对概率分布 q ( x ) q\left( x \right) q(x)进行随机采样;对采样样本 x i x_i x i 进行如下处理:计算 α i = p ( x ) k q ( x ) \alpha _i=\frac{p\left( x \right)}{kq\left( x \right)} α i = kq(x)p(x) ,同时采样均匀分布 U ( 0 , 1 ) U\left( 0,1 \right) U(0,1)生成 u i u_i u i ,若 u i ⩽ α i u_i\leqslant \alpha _i u i ⩽α i 则接收本次样本,否则拒绝。下面从定性与定量两个角度说明拒绝采样合理性。定性角度: α i = p ( x ) k q ( x ) \alpha _i=\frac{p\left( x \right)}{kq\left( x \right)} α i = kq(x)p(x) 的意义是接受率,即基于分布 q ( x ) q\left( x \right) q(x)的采样样本 x x x被认为符合目标分布 p ( x ) p\left( x \right) p(x)的概率。如图4所示,若采样到样本值 x 0 x_0 x 0 则其接受率很低,因为直观地,分布 q ( x ) q\left( x \right) q(x)采样到 x 0 x_0 x 0 的概率高( x 0 x_0 x 0 处概率密度大)而分布 p ( x ) p\left( x \right) p(x)在此处概率密度并不大,因此 x 0 x_0 x 0 大概率被认为不符合分布 p ( x ) p\left( x \right) p(x)而拒绝采样。 u i u_i u i 可以理解为均匀的概率因子, u i ⩽ α i u_i\leqslant \alpha _i u i ⩽α i 的条件可以理解为只有样本的接受概率足够大,才会被接受。几何上,如果样本落在灰色区域则拒绝本次样本,如果落在白色区域则接收本次样本。定量角度:设目标分布为 π ( x ) \pi \left( x \right) π(x),简单采样分布为 q ( x ) q\left( x \right) q(x),且满足 π ( x ) ⩽ k q ( x ) \pi \left( x \right) \leqslant kq\left( x \right) π(x)⩽kq(x),需要证明 p ( x ∣ a c c e p t ) = π ( x ) p\left( x|accept \right) =\pi \left( x \right) p(x∣accept)=π(x),即被接受的采样样本符合目标分布情况。由贝叶斯公式:p ( x ∣ a c c e p t ) = p ( a c c e p t ∣ x ) p ( x ) p ( a c c e p t ) = x q ( x ) p ( a c c e p t ∣ x ) q ( x ) p ( a c c e p t ) p\left( x|accept \right) =\frac{p\left( accept|x \right) p\left( x \right)}{p\left( accept \right)}\xlongequal{x~q\left( x \right)}\frac{p\left( accept|x \right) q\left( x \right)}{p\left( accept \right)}p(x∣accept)= p(accept)p(accept∣x)p(x) x q(x) p(accept)p(accept∣x)q(x)p ( a c c e p t ∣ x ) p\left( accept|x \right) p(accept∣x)是给定一个样本的条件下,被认为符合目标分布的概率,定性角度分析过,此条件概率即为接受率。先验概率 p ( a c c e p t ) p\left( accept \right) p(accept)由全概率公式展开:p ( x ∣ a c c e p t ) = π ( x ) k q ( x ) q ( x ) ∫ π ( x ) k q ( x ) q ( x ) d x = π ( x ) ∫ π ( x ) d x = π ( x ) p\left( x|accept \right) =\frac{\frac{\pi \left( x \right)}{kq\left( x \right)}q\left( x \right)}{\int{\frac{\pi \left( x \right)}{kq\left( x \right)}q\left( x \right) dx}}\\=\frac{\pi \left( x \right)}{\int{\pi \left( x \right) dx}}\\=\pi \left( x \right)p(x∣accept)= ∫ kq(x)π(x) q(x)dxkq(x)π(x) q(x)= ∫π(x)dxπ(x)=π(x)综上所述,对于任意分布,我们就可以设计合适的 q ( x ) q\left( x \right) q(x)和参数 k k k,从而使得所有接受点都是该分布的样本。(iii) 建立各种统计估计量实现对问题的求解通过蒙特卡洛方法的一般数学形式 ,以及其他数理统计公式,建立各种统计估计量如期望、方差、协方差、变异系数等,实现对问题的求解。综上所述,要想将MC方法作为一个通用的随机模拟方法,必须解决如何基于各种复杂概率分布进行采样这一问题,因为无法实现采样意味着无法建立统计估计,也无法证明人为构造的概率模型是否合适。但在实际应用中,对于常见的高维分布,即使通过拒绝采样也难以实现(无法找到合适的 q ( x ) q\left( x \right) q(x)和参数 k k k,或是 q ( x ) q\left( x \right) q(x)与目标分布差别太大导致接受率过低)。在这种情况下,将进一步引入马尔科夫链的概念。
文章目录一、引入1.1 信号分解的基本思想1.2 系统特征函数1.3 复指数分解二、周期信号的傅里叶级数2.1 谐波复指数集2.2 傅里叶级数2.2.1 表示形式2.2.2 收敛条件2.2.3 傅里叶系数三、傅里叶变换3.1 周期矩形脉冲信号3.2 傅里叶变换对四、傅里叶级数与傅里叶变换的联系4.1 信号三参数4.2 几何直观一、引入1.1 信号分解的基本思想信号分析的基本思想之一是将复杂信号用基本信号表示,这样就能通过简单信号的性质来分析复杂信号。这里要求基本信号应具有:(1)由这些基本信号能够构成相当广泛的一类信号(2)线性时不变系统(LTIS)对基本信号的响应应当十分简单,以使其对任意输入信号的响应都有很方便的表达式举例而言,基本信号可以是冲激函数x ( t ) = ∫ x ( τ ) δ ( t − τ ) d τ x\left( t \right) =\int{x\left( \tau \right)}\delta \left( t-\tau \right) d\taux(t)=∫x(τ)δ(t−τ)dτ上式即为连续时间信号的冲激分解,利用的是冲激函数的采样性质将信号冲激分解后,就可以简单地利用冲激响应来表示输出:y ( t ) = ∫ x ( τ ) h ( t − τ ) d τ y\left( t \right) =\int{x\left( \tau \right)}h\left( t-\tau \right) d\tauy(t)=∫x(τ)h(t−τ)dτ1.2 系统特征函数若系统对一个信号的响应仅为一个常数乘以该信号,则称该信号为此系统的特征函数,这个常数可以视作幅度因子,定义为特征值1.3 复指数分解按照1.1的思路,考察具有类似性质的基本信号——复指数设激励 x ( t ) = e s t s ∈ C x\left( t \right) =e^{st}\,\, s\in C x(t)=e st s∈C,则输出通过上面说的冲激响应表示为:y ( t ) = ∫ h ( τ ) x ( t − τ ) d τ = e s t ∫ h ( τ ) e − s τ d τ = e s t H ( s ) y\left( t \right) =\int{h\left( \tau \right)}x\left( t-\tau \right) d\tau =e^{st}\int{h\left( \tau \right) e^{-s\tau}}d\tau =e^{st}H\left( s \right)y(t)=∫h(τ)x(t−τ)dτ=e st ∫h(τ)e −sτ dτ=e st H(s)按照1.2的定义知道,复指数信号为LTIS的特征函数,对任一给定的 s s s,常数 H ( s ) H(s) H(s)为特征值;而对于一般的 s s s, H ( s ) H(s) H(s)为关于 s s s的函数,称为系统函数,当 s s s为纯虚数时, H ( j w ) H(jw) H(jw)称为系统频率响应,下面要引入的傅里叶分析都是建立在 s = j w s=jw s=jw的基础上,当 s s s为一般复数时,考察的是拉普拉斯变换,本文不赘述。那么复指数是否满足1.1节基本信号的要求呢?为便于理解,先给出离散信号 x ( t ) = ∑ k c k e s k t x\left( t \right) =\sum_k{c_k}e^{s_kt} x(t)=∑ k c k e s k t ,则响应 y ( t ) = ∑ k c k H ( s k ) e s k t y\left( t \right) =\sum_k{c_kH\left( s_k \right)}e^{s_kt} y(t)=∑ k c k H(s k )e s k t ,即若信号可以进行复指数分解,则响应可表示为相同复指数的线性组合,系数与输入和频率响应相关可见,复指数信号完美地符合了1.1的要求,在此基础上就建立起了傅里叶分析。二、周期信号的傅里叶级数2.1 谐波复指数集设 x 0 ( t ) = e j ω 0 t x_0\left( t \right) =e^{j\omega _0t} x 0 (t)=e jω 0 t ,定义基波频率为 ω 0 \omega _0 ω 0 ,基波周期为 T 0 T_0 T 0令谐波信号集:ψ k ( t ) = e j k ω 0 t , k = 0 , ± 1 , ± 2 ⋯ \psi _k\left( t \right) =e^{jk\omega _0t}, k=0,\pm 1,\pm 2\cdotsψ k (t)=e jkω 0 t ,k=0,±1,±2⋯其中 k = 0 k=0 k=0时为直流分量, k = ± N k=\pm N k=±N时为 N N N次谐波分量注意到谐波复指数集中,每一个信号都可以 T 0 T_0 T 0 为周期,这是因为:ψ k ( t ) = e j ( k ω 0 ) t ⇒ T k = 2 π ∣ k ∣ ω 0 = T 0 ∣ k ∣ \psi _k\left( t \right) =e^{j\left( k\omega _0 \right) t}\\\Rightarrow T_k=\frac{2\pi}{|k|\omega _0}=\frac{T_0}{|k|}ψ k (t)=e j(kω 0 )t⇒T k = ∣k∣ω 02π = ∣k∣T 0即每经过一个 T 0 T_0 T 0 ,相当于经过了 ∣ k ∣ |k| ∣k∣个相应的谐波周期。因此,谐波复指数集的线性组合也就以 T 0 T_0 T 0 为周期:x ( t ) = ∑ k = − ∞ + ∞ a k e j k ω 0 t ( 1 ) x\left( t \right) =\sum_{k=-\infty}^{+\infty}{a_k}e^{jk\omega _0t}\,\, \left( 1 \right)x(t)= k=−∞∑+∞ a k e jkω 0 t (1)2.2 傅里叶级数2.2.1 表示形式在信号 x ( t ) x(t) x(t)可以复指数分解的条件下研究此问题。考察(1)式,现实中绝大多数信号为实信号,因此认为 x ( t ) x(t) x(t)为实数,满足:x ( t ) ‾ = ∑ k = − ∞ + ∞ a k e j k ω 0 t ‾ \overline{x\left( t \right) }=\overline{\sum_{k=-\infty}^{+\infty}{a_k}e^{jk\omega _0t}}x(t) = k=−∞∑+∞ a k e jkω 0 t由 x ( t ) = x ( t ) ‾ x\left( t \right) =\overline{x\left( t \right) } x(t)= x(t) 导出:x ( t ) = ∑ k = − ∞ + ∞ a − k ‾ e j k ω 0 t x\left( t \right) =\sum_{k=-\infty}^{+\infty}{\overline{a_{-k}}e^{jk\omega _0t}}x(t)= k=−∞∑+∞ a −k e jkω 0 t进一步:x ( t ) = a 0 + ∑ k = 1 + ∞ [ a k e j ( k ω 0 ) t + a k e j ( k ω 0 ) t ‾ ] = a 0 + 2 ∑ k = 1 + ∞ Re { a k e j ( k ω 0 ) t } x\left( t \right) =a_0+\sum_{k=1}^{+\infty}{\left[ a_ke^{j\left( k\omega _0 \right) t}+\overline{a_ke^{j\left( k\omega _0 \right) t}} \right]}\\=a_0+2\sum_{k=1}^{+\infty}{\text{Re}\left\{ a_ke^{j\left( k\omega _0 \right) t} \right\}}x(t)=a 0 + k=1∑+∞ [a k e j(kω 0 )t + a k e j(kω 0 )t ]=a 0 +2 k=1∑+∞ Re{a k e j(kω 0 )t }(1)若 a k a_k a k 以极坐标形式给出,即 a k = A k e j w 0 a_k=A_ke^{jw_0} a k =A k e jw 0 ,此时x ( t ) = a 0 + 2 ∑ k = 1 + ∞ A k cos [ ( k ω 0 ) t + θ k ] ( 2 ) x\left( t \right) =a_0+2\sum_{k=1}^{+\infty}{A_k\cos \left[ \left( k\omega _0 \right) t+\theta _k \right]}\,\,\,\left( 2 \right)x(t)=a 0 +2 k=1∑+∞ A k cos[(kω 0 )t+θ k ](2)(2)若 a k a_k a k 以笛卡尔坐标形式给出,即 a k = B k + j C k a_k=B_k+jC_k a k =B k +jC k ,此时x ( t ) = a 0 + 2 ∑ k = 1 + ∞ [ B k cos ( k ω 0 t ) − C k sin ( k ω 0 t ) ] ( 3 ) x\left( t \right) =a_0+2\sum_{k=1}^{+\infty}{\left[ B_k\cos \left( k\omega _0t \right) -C_k\sin \left( k\omega _0t \right) \right]}\,\,\,\left( 3 \right)x(t)=a 0 +2 k=1∑+∞ [B k cos(kω 0 t)−C k sin(kω 0 t)](3)对于周期函数,(1)式即为傅里叶级数的复指数形式;(2)式为傅里叶级数的三角形式(极坐标下);(3)式为傅里叶级数的三角形式(笛卡尔坐标下)。一般地,若信号能展开为傅里叶级数,其表示形式必为(1)(2)(3)之一2.2.2 收敛条件并非所有周期信号都可以级数展开,即,并非所有信号都可以进行复指数分解。一般而言,满足Dirchlet条件的信号必可进行傅里叶分析,不满足Dirchlet条件的信号没有傅里叶级数形式,但可能有傅里叶变换。Dirchlet条件(1)信号绝对可积(2)在任何有限区间内,信号只有有限个最值(3)在任何有限区间内,信号只有有限个不连续点,且每个不连续点处都只有有限值2.2.3 傅里叶系数若信号满足Dirchlet条件,必能复指数分解为:x ( t ) = ∑ k = − ∞ + ∞ a k e j k ω 0 t x\left( t \right) =\sum_{k=-\infty}^{+\infty}{a_k}e^{jk\omega _0t}x(t)= k=−∞∑+∞ a k e jkω 0 t现在问题在于傅里叶系数 a k a_k a k 的确定,可以采用以下方式求得:e − j ( n ω 0 ) t x ( t ) = ∑ k = − ∞ + ∞ a k e j ( k − n ) ω 0 t e^{-j\left( n\omega _0 \right) t}x\left( t \right) =\sum_{k=-\infty}^{+\infty}{a_k}e^{j\left( k-n \right) \omega _0t}\,\,e −j(nω 0 )t x(t)= k=−∞∑+∞ a k e j(k−n)ω 0 t两边同时在基波周期内积分:∫ T e − j ( n ω 0 ) t x ( t ) d t = ∑ k = − ∞ + ∞ ∫ T a k e j ( k − n ) ω 0 t d t ⇒ ∫ T e − j ( n ω 0 ) t x ( t ) d t = ∑ k = − ∞ + ∞ a k ∫ T [ cos ( k − n ) w 0 t + j sin ( k − n ) w 0 t ] d t \int_T{e^{-j\left( n\omega _0 \right) t}x\left( t \right)}dt=\sum_{k=-\infty}^{+\infty}{\int_T{a_ke^{j\left( k-n \right) \omega _0t}dt}}\,\,\\\Rightarrow \int_T{e^{-j\left( n\omega _0 \right) t}x\left( t \right)}dt=\sum_{k=-\infty}^{+\infty}{a_k\int_T{\left[ \cos \left( k-n \right) w_0t+j\sin \left( k-n \right) w_0t \right] dt}}\,\,∫ T e −j(nω 0 )t x(t)dt= k=−∞∑+∞ ∫ T a k e j(k−n)ω 0 t dt⇒∫ T e −j(nω 0 )t x(t)dt= k=−∞∑+∞ a k ∫ T [cos(k−n)w 0 t+jsin(k−n)w 0 t]dt2.1节说过,谐波复指数集共同周期是基波周期,而三角函数一个周期内积分为0,在这里 T = ∣ k − n ∣ T k T=|k-n|T_k T=∣k−n∣T k ,因此等式左边在 k ≠ n k\ne n k =n时为0, k = n k=n k=n时为 T T T,即:∫ T e − j ( n ω 0 ) t x ( t ) d t = a n T \int_T{e^{-j\left( n\omega _0 \right) t}x\left( t \right)}dt=a_nT∫ T e −j(nω 0 )t x(t)dt=a n T由于 k = n k=n k=n,所以改写为:a k = 1 T ∫ T e − j ( k ω 0 ) t x ( t ) d t ( 4 ) a_k=\frac{1}{T}\int_T{e^{-j\left( k\omega _0 \right) t}x\left( t \right)}dt\,\, \left( 4 \right)a k = T1 ∫ T e −j(kω 0 )t x(t)dt(4)此式即为傅里叶系数求解公式。三、傅里叶变换3.1 周期矩形脉冲信号按照(4)式求解其傅里叶系数,得到:从两个角度审视此式:(1)视其为关于k kk的函数,即此时相当于将傅里叶系数等距离地排列在 k k k轴上,因此当 T T T趋于无穷时, ∣ a k ∣ |a_k| ∣a k ∣趋于0,即非周期信号的傅里叶系数幅度趋于0,正因如此,在幅度频谱中就看不出任何信息,所以对于非周期信号,不能仅关注 a k a_k a k(2)视其为包络线的采样此时,视为:考虑关于 w w w的函数 f ( w ) = 2 E sin ( ω T 1 ) ω f\left( w \right) =\frac{2E\sin \left( \omega T_1 \right)}{\omega} f(w)= ω2Esin(ωT 1 ) , a k T a_kT a k T就表示对 f ( w ) f(w) f(w)上 w = k w 0 w=kw_0 w=kw 0 的位置进行采样。显然上面的采样间隔为 w 0 = 2 π / T w_0=2\pi/T w 0 =2π/T,因此随着 T T T不断增大,就出现了图2(a)->©取样变密的现象重点理解的地方来了!!注意这里 T T T趋于无穷时, ∣ a k ∣ |a_k| ∣a k ∣依然趋于0,但可见的是 ∣ a k ∣ T |a_k|T ∣a k ∣T是有限值(落在 f ( w ) f(w) f(w)上),因此 ∣ a k ∣ T |a_k|T ∣a k ∣T的意义就是在 ∣ a k ∣ |a_k| ∣a k ∣趋于0的情况下,通过T的加权作用,在一个有限的范围内显示出 ∣ a k ∣ |a_k| ∣a k ∣间的相对大小关系,简言之, ∣ a k ∣ T |a_k|T ∣a k ∣T把肉眼不可见的非周期信号的傅里叶系数放大到肉眼可见,这其实就是傅里叶变换的引入基础。3.2 傅里叶变换对从3.1节知道,傅里叶变换的出发点,就是傅里叶系数的幅度加权与包络采样,因此:X ( w ) ∣ w = k w 0 = a k T X\left( w \right) \mid_{w=kw_0}^{}=a_kTX(w)∣ w=kw 0 =a k T从而,X ( w ) = ∫ − ∞ + ∞ x ( t ) e − j w t d t ( 5 ) X\left( w \right) =\int\limits_{-\infty}^{+\infty}{x\left( t \right) e^{-jwt}}dt\,\, \left( 5 \right)X(w)= −∞∫+∞ x(t)e −jwt dt(5)代入 x ( t ) = ∑ k = − ∞ + ∞ a k e j k ω 0 t x\left( t \right) =\sum_{k=-\infty}^{+\infty}{a_k}e^{jk\omega _0t} x(t)=∑ k=−∞+∞ a k e jkω 0 t 中即得:x ( t ) = 1 2 π ∫ − ∞ + ∞ X ( w ) e j w t d w ( 6 ) x\left( t \right) =\frac{1}{2\pi}\int\limits_{-\infty}^{+\infty}{X\left( w \right) e^{jwt}}dw\,\, \left( 6 \right)x(t)= 2π1 −∞∫+∞ X(w)e jwt dw(6)(5)(6)式合称为一对傅里叶变换对,(5)式称为傅里叶变换积分四、傅里叶级数与傅里叶变换的联系4.1 信号三参数这里定义信号的三参数为幅度、初相、频率(或角频率),在傅里叶分析中,只要确定组成信号的所有复指数信号的三参数,就可以完全表征。无论是傅里叶级数还是傅里叶变换,事实上都是在求一个包含三参数的表达式来表示一个信号。在傅里叶级数展开中,傅里叶系数表示了在频率 w = k w 0 w=kw_0 w=kw 0 时复指数信号的幅度和相位;在傅里叶变换中,傅里叶积分 X ( w ) X(w) X(w)表示了全频率复指数信号的三参数信息——可以认为是公式化的频谱具体来说,列于下表:事实上,不应该以信号的周期与否来割裂傅里叶变换与傅里叶级数。换言之,周期信号与非周期信号都有相应的傅里叶变换和傅里叶系数,只不过周期信号的傅里叶变换为冲激函数的线性组合,非周期信号的傅里叶系数趋于0,但有相对大小。4.2 几何直观几何直观上,傅里叶变换是连续函数,因为其对象是全频率;傅里叶级数是离散的,因为其对象是采样的部分频率。
线性回归是在已有数据的基础上,通过建立含有未知参数的线性模型来拟合样本点。通过已有数据确定未知参数后,就可用回归模型来预测其他输入状态下的输出值。一般地,线性回归分为标准线性回归(Standard LR)和局部加权线性回归(Locally Weighted LR)两种。其区别在于:前者对所有的样本点共享一个权重矩阵,权重矩阵由全局MSE得出;后者每个样本点使用一个权重矩阵,权重矩阵由核函数与MSE共同决定。下面从一个实例考虑二者的区别。考虑如图1(i)所示的数据集,用标准线性回归直观上并不恰当,似乎用曲线可以拟合得更好,这是全局共享权重矩阵的弊端。如图1(ii)所示,将某个样本点 x i x_i x i 及其邻域内的点视为一个元集合 A A A,对 A A A进行标准线性回归以得到回归直线 ,将样本中的所有元集合对应的回归直线连接起来,就形成了对整个样本集的拟合。换言之,这是用局部最优拟合全局最优,用线性拟合非线性模型。局部加权线性回归的核心原理在于如何计算出一个元集合并进行标准线性回归。这里引入的是高斯核函数:k ( x i , x j ) = e − ( x i − x j ) 2 2 σ 2 k(x_i,x_j)=e^{-\frac{(x_i-x_j)^2}{2\sigma^2}}k(x i ,x j )=e − 2σ 2(x i −x j ) 2这使得离 x i x_i x i 近的点对 x i x_i x i 对应元集合的影响大,离 x i x_i x i 远的点影响小。对每一个点 x i x_i x i 都运用高斯核函数计算出一个权重矩阵 W i = d i a g ( w 1 , w 2 , . . . , w m ) W_i=diag(w_1,w_2,...,w_m) W i =diag(w 1 ,w 2 ,...,w m ) ,此时对于每一个样本点都有一个标准线性回归方程,其代价函数为:于是对于每个预测值而言都有:在代码实现层面,下面贴出计算参数的核心代码:def lwlr_weights(x_Test,xMat,yMat,Gama): #计算权重矩阵并返回theta n = np.shape(xMat)[1] weights = np.eye(n) temp = np.multiply(np.diagonal((xMat-x_Test).T*(xMat-x_Test)).T,weights) weights = np.multiply(np.diagonal(np.exp(-Gama*temp)).T,weights) xWx = xMat*weights*xMat.T if (np.linalg.det(xWx)==0): return "\terror" else: theta = xWx.I*xMat*weights*yMat.T return theta其中temp与weights的计算通过numpy库中矩阵点乘、提取对角元素等操作避免了显式的for循环。向量化是机器学习中非常重要的节约资源的手段,经验证,本实验200个数据采用矩阵运算只需0.7ms左右完成,而使用for循环则需要3.8ms左右,且此倍率将随着样本扩大而增加。如图2所示为回归分析的结果,取Gama=1,900,50000进行实验。Gama越大说明高斯核的作用越强,前述元集合越小,拟合程度越高。但过拟合和欠拟合均不具备机器学习提高泛化能力的初衷,因此在一般模型设计时还会对其正则化。
1、工作模式中断模式:调用中断服务程序(ISR, Interrupt Service Routine)时处于中断模式线程模式:除了中断服务程序以外的程序都处于线程模式2、任务特权级特权级:此任务等级下,用户可以对系统控制寄存器进行访问和配置用户级:此任务等级下,用户不允许对系统控制寄存器进行访问和配置(一旦访问将导致硬件异常)。起保护用户任务的作用,防止用户在任意任务中访问和修改系统寄存器,操作不当从而造成系统崩溃注1用户级线程模式下可通过MSR访问xPSR中的APSR,其余特殊功能寄存器禁止访问按特权级和用户级区分代码,有利于架构的安全和健壮。 例如,当用户代码出问题时,因其被禁止写特殊功能寄存器和系统寄存器,所以不会影响系统中其他代码的正常运行。 若配有MPU,保护力度就更大,甚至可以阻止用户代码访问不属于它的内存区域。3、工作状态Cortex-M3处理器有两种工作状态:①Thumb-2工作状态:此状态下,执行Thumb-2指令②调试工作状态:处理器停机调试时(In Halting Debug)进入该状态4、工作模态间的转换如图所示,MCU复位后,首先进入特权级线程模式;在特权级线程模式下,可通过置位CONTROL[0]来进入用户级线程模式;在任意模态下,不管是任何原因产生了任何异常中断,处理器都将以特权级中断模式来运行其服务例程,异常中断返回后MCU必须恢复到先前的工作模态。在黑色部分表示的内循环中,用户级线程模式无法返回特权级线程模式,因此提供了一种间接切换的途径:在用户级线程模式下使用一条系统服务调用指令(SVC, Supervisor Call)来触发一个SVC异常,在特权级中断模式下,该异常的服务例程可以选择修改CONTROL[0],清零CONTROL[0]后使异常返回到特权级线程模式而非原先工作的用户级线程模式。
1、看门狗模块概述在由单片机构成的微机系统中,由于单片机工作常常会受到来自外界电磁场干扰导致程序跑飞,陷入死循环——即程序正常运行被打断,系统无法继续工作。这种情况下会造成系统陷入停滞状态,发生不可预料的后果。因此出于对单片机运行状态进行实时监测的考虑,产生了一种专门用于监测单片机程序运行状态的模块或芯片,称为看门狗。STM32F10xxx内置两个看门狗:独立看门狗(IWDG, Independent WatchDoG)和窗口看门狗(WWDG, Windows WatchDoG),提供了更高的安全性、时间的精确性和使用的灵活性。WWDG和IWDG原理有所区别,本篇文章仅对IWDG作分析2、IWDG原理IWDG由独立时钟LSI驱动且处于VDD供电区,因此可在睡眠、停机、待机模式下运行。当IWDG_KR写入0xCCCC时IWDG递减计数器启动,从0xFFFF开始减计数,当计数器计数为0时系统就进行复位。在正常工作状态下,每隔一段时间会向IWDG_KR写入0xAAAA,即将一个寄存在IWDG_RLR的12bits数值装载到IWDG计数器中——可见正常工作下IWDG永远不会触发系统复位;当系统跑飞时将无法正常喂狗,从而一段时间后产生系统复位,维护系统运行稳定。看门狗溢出时间如下,当计数器第一次重装载后,超过 T o u t T_{out} T out 不重装载将产生系统复位。在战舰版中,LSI如图2为40kHz,因此令 f L S I f_{LSI} f LSI =40即可T o u t = ( 4 × 2 P r e ) × R L R f L S I T_{out}=\frac{(4×2^{Pre})×RLR}{f_{LSI}}T out = f LSI(4×2 Pre )×RLR 3、IWDG实验分析本实验基于STM32NANO(HAL库),结合KEY、LED与IWDG观察看门狗的监视复位功能。int main(void) { HAL_Init(); //初始化HAL库 Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M delay_init(72); //初始化延时函数 uart_init(115200); //初始化串口 LED_Init(); //初始化LED KEY_Init(); //初始化按键 delay_ms(100); //延时100ms再初始化看门狗 IWDG_Init(IWDG_PRESCALER_64,625); //分频数为64,重载值为625 LED0=0; while(1) { if(KEY_Scan(1)==WKUP_PRES) //如果WK_UP按下,喂狗 { IWDG_Feed(); //喂狗 } delay_ms(10); } }IWDG_Init(IWDG_PRESCALER_64,625)设置了溢出时间为:T o u t = 64 × 625 40 m s = 1 s T_{out}=\frac{64×625}{40}ms=1sT out = 4064×625 ms=1s即超过1s没有喂狗,系统将执行复位。LED0=0使用位带操作进行点灯,位带操作点灯的原理可以参考:位带操作原理+LED实验分析while(1)循环内将喂狗函数和WK_UP按键绑定,即按下WK_UP就执行了重装载操作(KEY_Scan(1)即选用支持连按)。IWDG初始化前的延时delay_ms(100)是为了使LED0的闪烁可见:如图4所示,若没有delay(),两次点灯时间间隔很短,无法观察到因为复位导致的LED0灭灯现象。下面讲解封装过的独立看门狗初始化函数void IWDG_Init(u8,u16)原理:IWDG_HandleTypeDef IWDG_Handler; //独立看门狗句柄 void IWDG_Init(u8 prer,u16 rlr) { IWDG_Handler.Instance=IWDG; IWDG_Handler.Init.Prescaler=prer; //设置IWDG分频系数 IWDG_Handler.Init.Reload=rlr; //重装载值 HAL_IWDG_Init(&IWDG_Handler); //初始化IWDG,默认会开启独立看门狗 HAL_IWDG_Start(&IWDG_Handler); //启动独立看门狗 }首先在IWDG头文件中定义了全局的IWDG句柄,句柄定义如下,包含的是IWDG寄存器组基址以及预分频系数、重装载值等资源信息。typedef struct { IWDG_TypeDef *Instance; /*!< Register base address */ IWDG_InitTypeDef Init; /*!< IWDG required parameters */ }IWDG_HandleTypeDef;接下来在函数中对这个全局句柄进行初始化,将输入参数Prep、Rlr以及IWDG基址赋给IWDG_Handle,之后使用此句柄进行实质性的IWDG初始化和启动。下面给出了IWDG的初始化封装HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *)中与IWDG配置有关的代码HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg) { …… /* Enable IWDG. LSI is turned on automaticaly */ __HAL_IWDG_START(hiwdg); /* Enable write access to IWDG_PR and IWDG_RLR registers by writing 0x5555 in KR */ IWDG_ENABLE_WRITE_ACCESS(hiwdg); /* Write to IWDG registers the Prescaler & Reload values to work with */ hiwdg->Instance->PR = hiwdg->Init.Prescaler; hiwdg->Instance->RLR = hiwdg->Init.Reload; /* Reload IWDG counter with value defined in the reload register */ __HAL_IWDG_RELOAD_COUNTER(hiwdg); /* Return function status */ return HAL_OK; }大多数配置都是通过宏定义函数进行的,例如#define WRITE_REG(REG, VAL) ((REG) = (VAL)) #define IWDG_ENABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_ENABLE)其中IWDG_KEY_WRITE_ACCESS_ENABLE就是0x5555,即往KR写入0x5555,参照图3知此时取消了RLR与PR的写保护,可以向其中写入预分频因子和重装载值了。将IWDG初始化并启动后,若不进行喂狗系统将在溢出时间到达后自动复位。IWDG_Feed()是对宏定义函数__HAL_IWDG_RELOAD_COUNTER(hiwdg)的封装,即往KR中写入0xAAAA对计数器进行重装载。
一、指令执行流程冯诺依曼架构CPU指令执行的五个阶段:在上述各阶段CPU与内存的交互时,还涉及系统总线如AB、CB、DB等1、取指阶段(IF, Instruction Fetch)在上一个指令周期时,程序计数器PC中记录的是下一条指令的内存地址。因此IF阶段CPU指令寄存器按照PC的地址从主存中取得一条指令,当前指令被取出后,PC更新到下一条指令的地址。2、指令译码阶段(ID, Instruction Decode)在ID阶段,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类别以及各种获取操作数的方法。3、执行指令阶段(EXE, Execute)通过对指令的译码,CPU已经知晓这条指令要如何执行,因此EXE阶段就是通过操作控制器OC,按确定的时序向相应的部件发出微操作控制信号以对指令要求的特定操作进行具体实现。EXE阶段CPU的不同部分(如ALU、寄存器组等)被联合起来,以实现指令所需的操作。4、访存取数阶段(MEM, Memory)根据指令需要可能要访问内存,读取操作数。MEM阶段根据指令地址码(如果有要求的话)得到操作数在主存中的地址,并从主存中读取该操作数用于运算。5、写回阶段(WB, Write Back)WB阶段把执行指令阶段的运行结果数据“写回”到某种存储形式:①写入CPU的内部寄存器中,以便被后续的指令快速地存取②写入内存③改变FR中标志位状态(如果需要改变的话),用来影响接下来程序的动作WB阶段结束后,若无意外事件(如结果溢出等)或异常中断发生,计算机就接着从程序计数器PC中取得下一条指令地址,开始新一轮的循环。上述五个阶段在具体实现过程中会引入指令流水线来提高效率。二、指令流水线(Instruction Pipeline)指令流水线概念的提出是为了提高CPU处理指令的效率。假设将CPU执行一条指令按照流程分为四个步骤如图1(ii)考虑一个完全不采用流水线的系统如图3(i),对于一组指令序列,CPU总是等某条指令的全部过程都执行完之后再开始下一条指令。与之对比的是图1(ii)的指令流水线,在第一条指令完成IF后,第二条指令就可以开始IF,依次类推。直观地,指令流水可以大幅提高CPU处理指令的效率。1、指令流水线基本概念①指令流水周期:将一条指令划分为不同的步骤涉及到的一个重要问题就是——不一致的划分现象,即每个步骤完成的时间不均匀,例如在经典划分中,IF,WB的速度比较快而EXE速度较慢,如图2所示阴影部分为指令步骤所需的时间。因此必须把流水周期设置为单级操作的最大时延。指令阶段间的指令转移(例如IF→ID)是由时钟信号控制的,驱动这条指令流水工作的时钟周期应该要等于这个最大时延。②CPI(Cycles Per Instruction):即每条指令经过整个流水线所需周期数,它与各指令有关,不同的指令,CPI也不相同。因此,一般使用平均CPI表示③ILP(Instruction Level Parallelism):即指令级并行度,是指当指令流水线被充分利用时,一个基本周期内并行执行的指令数④指令发射速率:指在每个周期内发射的指令数,在数值上等于超标量处理机的级数。2、影响流水线性能的因素①数据相关:如第一条指令的输出为第二条指令的输入,则第二条指令必须等待第一条指令执行完毕而不能直接进入流水,造成流水线效率下降②控制相关:如遇到分支判断指令,那么该指令可能顺序执行,也可能分支跳转到其他位置,造成流水线断流。引入流水线会提高CPU指令处理效率,但过深的流水等级会使数据相关和控制相关的副作用增大,反而又降低了CPU工作性能。3、流水线的改进技术——超标量技术采用资源重复的并行性思想,设置多条指令流水线和多个功能部件。每个周期发送多条指令(即指令发射速率大于1),同时并行地对多条指令进行流水处理
2022年04月